@lunora/cli 1.0.0-alpha.351 → 1.0.0-alpha.353
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_chunks/handler.mjs +1 -1
- package/dist/packem_chunks/handler13.mjs +1 -1
- package/dist/packem_chunks/handler14.mjs +1 -1
- package/dist/packem_chunks/handler19.mjs +1 -1
- package/dist/packem_chunks/handler20.mjs +1 -1
- package/dist/packem_chunks/handler22.mjs +3 -3
- package/dist/packem_chunks/handler26.mjs +1 -1
- package/dist/packem_chunks/handler27.mjs +15 -15
- package/dist/packem_chunks/handler28.mjs +1 -1
- package/dist/packem_chunks/handler9.mjs +1 -1
- package/dist/packem_chunks/runDeployCommand.mjs +3 -3
- package/dist/packem_chunks/runDevCommand.mjs +1 -1
- package/dist/packem_chunks/runInitCommand.mjs +44 -44
- package/dist/packem_shared/COMMANDS-CWnIJmxM.mjs +1 -0
- package/dist/packem_shared/cli-6BRlq6nw.mjs +2 -0
- package/dist/packem_shared/cli-manifest-CRet0egA.mjs +1 -0
- package/dist/packem_shared/cloudflare-credentials-lC5JtPLe.mjs +1 -0
- package/dist/packem_shared/{commands-Cg06JNO2.mjs → commands-DgUDxkA8.mjs} +5 -5
- package/dist/packem_shared/plan-DJeIwzFu.mjs +5 -0
- package/dist/packem_shared/{planDevCommand-1uuaH06D.mjs → planDevCommand-DkaC-fNF.mjs} +1 -1
- package/dist/packem_shared/resolve-BrsV4Qyc.mjs +5 -0
- package/dist/packem_shared/runAddCommand-DTvfp4h2.mjs +1 -0
- package/dist/packem_shared/secret-key-B4aoLE0w.mjs +1 -0
- package/dist/packem_shared/{storage-ChJtQ9E4.mjs → storage-DWhTX8eS.mjs} +1 -1
- package/package.json +7 -7
- package/dist/packem_shared/COMMANDS-DRMcVnnw.mjs +0 -1
- package/dist/packem_shared/cli-CG_JgNKu.mjs +0 -2
- package/dist/packem_shared/plan-Cod9Cgll.mjs +0 -5
- package/dist/packem_shared/resolve-DUCSc7jQ.mjs +0 -5
- package/dist/packem_shared/runAddCommand-BsOrlyfD.mjs +0 -1
- package/dist/packem_shared/secret-key-DsMeUoEg.mjs +0 -1
package/dist/bin.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{r as e}from"./packem_shared/cli-
|
|
2
|
+
import{r as e}from"./packem_shared/cli-6BRlq6nw.mjs";try{const r=await e();process.exit(r)}catch(r){process.stderr.write(`${r instanceof Error?r.message:String(r)}
|
|
3
3
|
`),process.exit(1)}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as o,V as a,r as n}from"./packem_shared/cli-
|
|
1
|
+
import{C as o,V as a,r as n}from"./packem_shared/cli-6BRlq6nw.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{runDeployCommand as p}from"./packem_chunks/runDeployCommand.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-DJeIwzFu.mjs";import{runInitCommand as l}from"./packem_chunks/runInitCommand.mjs";import{runMigrateGenerateCommand as I}from"./packem_shared/runMigrateGenerateCommand-Ii_WMZEA.mjs";import{runResetCommand as g}from"./packem_chunks/runResetCommand.mjs";import{runRpcCommand as T}from"./packem_chunks/runRpcCommand.mjs";import{EXIT_CODE as S,exitCodeForCode as A,exitCodeForError as c,exitCodeForStatus as M}from"./packem_shared/EXIT_CODE-D_XCixv8.mjs";import{insertSchemaExtension as v}from"./packem_shared/insertSchemaExtension-DuiV6cba.mjs";import{createLogger as b,pail as O}from"./packem_shared/createLogger-Cl18I8AX.mjs";import{diffSnapshots as y,renderAddColumn as L,renderCreateIndex as w,renderCreateTable as B,renderDropIndex as P,renderDropTable as U,renderMigrationFile as V,validatorKindToSqlType as W}from"./packem_shared/diffSnapshots-WWwx-KvZ.mjs";import{default as N}from"./packem_shared/schemaIrToSnapshot-BjK-0IRo.mjs";import{createRecordingSpawner as j,defaultSpawner as q}from"./packem_shared/createRecordingSpawner-Ch7zuQQE.mjs";import{default as K}from"./packem_shared/parseManifest-CwPTKdtS.mjs";import{D as Y,r as Z}from"./packem_shared/import-BRM1hs7j.mjs";import{REQUIRED_COMPATIBILITY_DATE as z,REQUIRED_FLAG as J,validateWranglerProject as $,validateWranglerConfig as rr}from"@lunora/config/cloudflare";import{b as or}from"./packem_shared/catalog-CahzmDLV.mjs";import{r as nr,a as tr,b as mr}from"./packem_shared/commands-DgUDxkA8.mjs";import{runExportCommand as pr}from"./packem_shared/runExportCommand-slAq5BCF.mjs";export{o as COMMANDS,Y as DEFAULT_IMPORT_BATCH_SIZE,S as EXIT_CODE,z as REQUIRED_COMPATIBILITY_DATE,J as REQUIRED_FLAG,a as VERSION,or as buildRegistryIndex,b as createLogger,j as createRecordingSpawner,q as defaultSpawner,y as diffSnapshots,A as exitCodeForCode,c as exitCodeForError,M as exitCodeForStatus,v as insertSchemaExtension,O as pail,K as parseManifest,i as planDevCommand,L as renderAddColumn,w as renderCreateIndex,B as renderCreateTable,P as renderDropIndex,U as renderDropTable,V as renderMigrationFile,nr as runAddCommand,tr as runBuildIndexCommand,n as runCli,m as runCodegenCommand,p as runDeployCommand,f as runDevCommand,pr as runExportCommand,Z as runImportCommand,l as runInitCommand,I as runMigrateGenerateCommand,mr as runRegistryViewCommand,g as runResetCommand,T as runRpcCommand,N as schemaIrToSnapshot,$ as validateWrangler,rr as validateWranglerConfig,W as validatorKindToSqlType};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as b}from"node:fs";import{readProjectDependencies as w,applyLintIgnores as A,detectLintTools as k}from"@lunora/config";import{findWranglerFile as I}from"@lunora/config/cloudflare";import{join as U,basename as h}from"@visulima/path";import{d as N}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as o}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{r as S}from"../packem_shared/lint-ignore-report-DKZagpqk.mjs";import{t as y,a as E}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{i as _}from"../packem_shared/resolve-BrsV4Qyc.mjs";import{r as D}from"../packem_shared/commands-DgUDxkA8.mjs";import{n as L,i as M,A as P,E as R,s as O,d as f,p as $,r as v,M as j,a as C,b as g,c as F,D as m,e as x,f as H,g as d,h as z,j as B,w as G,k as W,l as V}from"../packem_shared/storage-DWhTX8eS.mjs";const p=e=>{const r=e.trim().toLowerCase();return B.find(a=>a.value===r||a.label.toLowerCase()===r||r==="auth0"&&a.value==="auth-auth0"||r==="clerk"&&a.value==="auth-clerk")?.value},q=async e=>{const r=e.cwd??process.cwd(),t=H(w(r));return t!==void 0?t:e.yes===!0?(e.logger.warn(`add: couldn't detect your framework — using "${d}". Pass a specific item (e.g. \`lunora add auth-ui-vue\`) to override.`),d):await(e.promptSelect??((c,n,i)=>y(c,n,i)))("Which framework is your app?",z,{default:d})??d},X=async e=>{if(e.provider!==void 0&&e.provider!==""){const t=p(e.provider);return t===void 0?(e.logger.warn(`add: unknown --provider "${e.provider}" — using "${m}" (email & password).`),m):t}if(e.yes===!0)return m;const r=e.promptSelect??((t,a,c)=>y(t,a,c));return x(r)},l=e=>e.promptText??((r,t)=>E(r,t)),J=async e=>{const r=h(e.cwd??process.cwd());if(e.bucket!==void 0&&e.bucket!==""){const t=O(e.bucket);if(t!==void 0)return t;const a=f(r);return e.logger.warn(`add: "${e.bucket}" isn't a valid R2 bucket name (lowercase alphanumeric + hyphens, 3–63 chars) — using "${a}".`),a}return e.yes===!0?f(r):$(l(e),r)},K=async e=>{const r=t=>{e.logger.warn(`add: ${t}`)};if(e.mailTo!==void 0&&e.mailTo!=="")return v(e.mailTo,r);if(e.yes!==!0)return v(await l(e)(j,{placeholder:"you@yourdomain.com"}),r)},Q=async e=>{const r=h(e.cwd??process.cwd());if(e.db!==void 0&&e.db!==""){const t=C(e.db);if(t!==void 0)return t;const a=g(r);return e.logger.warn(`add: "${e.db}" isn't a usable D1 database name — using "${a}".`),a}return e.yes===!0?g(r):F(l(e),r)},Y=async(e,r)=>e.kind==="auth"?[await X(r)]:e.kind==="auth-ui"?[await q(r)]:e.kind==="email"?[R]:[e.item],Z=(e,r)=>{S(A(e,k(e)),r)},ee=e=>!_(e)||e.yes===!0,re=async e=>{const r=e.cwd??process.cwd(),t=e.feature===void 0?void 0:L(e.feature);if(t===void 0)return e.logger.error("add requires a feature or registry item. Usage: lunora add <auth|email|storage|crons|presence|…>"),{code:o.USAGE,items:[]};if(!b(U(r,"lunora"))||I(r)===void 0)return e.logger.error("add: not a Lunora project here (need a lunora/ directory and a wrangler.jsonc). Run `lunora init` first."),{code:o.USAGE,items:[]};if(t.kind==="auth-ui"&&M(w(r)))return e.logger.error(`add: ${P}`),{code:o.USAGE,items:[]};const a=await Y(t,e),c=[];if(a.includes("storage")){const s=await J(e);c.push(u=>G(u,s))}if(a.includes("mail")){const s=await K(e);s!==void 0&&c.push(u=>W(u,s))}if(a.some(s=>s==="auth"||s.startsWith("auth-"))){const s=await Q(e);c.push(u=>V(u,s))}const n=c.length>0?s=>{let u=s;for(const T of c)u=T(u);return u}:void 0,i=await D({allowUnsafeSource:e.allowUnsafeSource,confirm:e.confirm,cwd:r,from:e.from,logger:e.logger,names:[...a],ref:e.ref,source:e.source,transformManifest:n,yes:ee(e)});return i.code===0&&Z(r,e.logger),{code:i.code,items:a}},fe=N(async({argument:e,cwd:r,logger:t,options:a})=>{const c=await re({allowUnsafeSource:a.allowUnsafeSource===!0,bucket:a.bucket,cwd:r,db:a.db,feature:e[0],from:a.from,logger:t,mailTo:a.mailTo,provider:a.provider,ref:a.ref,source:a.source,yes:a.yes===!0});return{code:c.code,data:{items:c.items},...c.error===void 0?{}:{error:c.error}}});export{fe as execute,re as runAddFeature};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as l}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as n}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{r as s,b as t,a as o}from"../packem_shared/commands-
|
|
1
|
+
import{d as l}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as n}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{r as s,b as t,a as o}from"../packem_shared/commands-DgUDxkA8.mjs";const y=l(({argument:a,cwd:u,format:m,logger:e,options:r})=>{const f=a[0],c=a.slice(1);if(f==="add")return s({allowUnsafeSource:r.allowUnsafeSource===!0,cwd:u,diff:r.diff===!0,dryRun:r.dryRun===!0,format:m,from:r.from,logger:e,names:c,overwrite:r.overwrite===!0,ref:r.ref,source:r.source,yes:r.yes===!0});if(f==="list")return s({allowUnsafeSource:r.allowUnsafeSource===!0,cwd:u,format:m,from:r.from,list:!0,logger:e,names:[],ref:r.ref,source:r.source});if(f==="view")return t({allowUnsafeSource:r.allowUnsafeSource===!0,cwd:u,from:r.from,logger:e,names:c,ref:r.ref,source:r.source});if(f==="build")return o({check:r.check===!0,cwd:u,from:r.from,logger:e,names:[],out:r.out});const d="registry: unknown subcommand. Usage: lunora registry <add|list|view|build> [names…]";return e.error(d),{code:n.USAGE,error:d}});export{y as execute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as u,readdirSync as d,statSync as g,mkdirSync as h,writeFileSync as w,readFileSync as y}from"node:fs";import{fileURLToPath as v}from"node:url";import{AGENT_RULES_DIR as a,detectAgentRules as R,LUNORA_SKILL_NAMES as L}from"@lunora/config";import{join as i,relative as A,resolve as E,dirname as m}from"@visulima/path";import{f as I}from"../packem_shared/cli-manifest-CRet0egA.mjs";import{d as $}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as _}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";const b=(e=m(v(import.meta.url)))=>{const r=I(e),t=r===void 0?void 0:i(r,"skills");return t!==void 0&&u(t)?t:void 0},j=e=>d(e).filter(r=>{const t=i(e,r);return g(t).isDirectory()&&u(i(t,"SKILL.md"))}),f=(e,r,t)=>{h(r,{recursive:!0});let n=!1;for(const o of d(e)){const l=i(e,o),s=i(r,o);if(g(l).isDirectory()){n=f(l,s,t)||n;continue}u(s)&&!t||(w(s,y(l)),n=!0)}return n},D=["pnpm-workspace.yaml","pnpm-lock.yaml","yarn.lock","package-lock.json","bun.lock","bun.lockb",".git"],O=e=>{let r=e;for(;;){if(D.some(n=>u(i(r,n))))return r;const t=m(r);if(t===r)return e;r=t}},k=(e,r)=>r===void 0?O(e):E(e,r),U=e=>{const r=e.cwd??process.cwd(),t=k(r,e.dir),n=e.overwrite===!0,o=b();if(o===void 0)return e.logger.error("rules: could not locate the bundled skills (is @lunora/cli installed correctly?)."),{code:1,installed:[],skipped:[]};const l=[],s=[];for(const c of j(o)){const S=i(t,a,c);f(i(o,c),S,n)?l.push(c):s.push(c)}const p=A(r,i(t,a))||a;return l.length>0&&e.logger.success(`Installed ${String(l.length)} Lunora skill(s) into ${p}/: ${l.join(", ")}.`),s.length>0&&e.logger.info(`Skipped ${String(s.length)} existing skill(s) (re-run with --overwrite to replace): ${s.join(", ")}.`),e.logger.info("Your AI coding agent will pick these up automatically. Start with the `lunora` skill."),{code:0,installed:l,skipped:s}},x=e=>{const r=e.cwd??process.cwd(),t=k(r,e.dir),n=R(t);return n.installed?(e.logger.success(`Lunora agent rules are installed (${String(n.present.length)}/${String(L.length)} skills).`),n.missing.length>0&&e.logger.info(`Missing: ${n.missing.join(", ")}. Run \`lunora rules install\` to add them.`),{code:0,installed:n.present,skipped:[]}):(e.logger.warn("Lunora agent rules are not installed. Run `lunora rules install` so your AI agent knows how to use Lunora."),{code:e.strict===!0?1:0,installed:n.present,skipped:[]})},G=$(({argument:e,cwd:r,logger:t,options:n})=>{const o=e[0]??"check";return o==="install"?U({cwd:r,dir:n.dir,logger:t,overwrite:n.overwrite===!0}):o==="check"?x({cwd:r,dir:n.dir,logger:t,strict:n.strict===!0}):(t.error("rules: unknown subcommand. Usage: lunora rules <install|check>"),{code:_.USAGE})});export{G as execute,j as listBundledSkills,b as resolveBundledSkillsDirectory,x as runRulesCheck,U as runRulesInstall};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{writeFileSync as h,existsSync as S,readFileSync as p}from"node:fs";import{isAbsolute as v,join as w}from"node:path";import{byCodepoint as b,scoreAdvisor as y,parseAdvisorMap as x,compareToBaseline as j}from"@lunora/advisor";import{runCodegen as A}from"@lunora/codegen";import{d as M}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as D}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{D as u}from"../packem_shared/cli-
|
|
1
|
+
import{writeFileSync as h,existsSync as S,readFileSync as p}from"node:fs";import{isAbsolute as v,join as w}from"node:path";import{byCodepoint as b,scoreAdvisor as y,parseAdvisorMap as x,compareToBaseline as j}from"@lunora/advisor";import{runCodegen as A}from"@lunora/codegen";import{d as M}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as D}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{D as u}from"../packem_shared/cli-6BRlq6nw.mjs";const l={clean:"·",exempt:"–",failing:"✗",warned:"!"},d=(e,r)=>e.length>=r?e:e+" ".repeat(r-e.length),E=e=>`${e.id} (${e.visibility} ${e.kind})`,f=e=>{let r=0;for(const n of e)r=Math.max(r,n.id.length);return r},g=(e,r)=>` ${d(String(e.score),3)} ${l[e.coverage]} ${d(e.id,r)} ${e.visibility} ${e.kind}`,R=e=>{if(!e.comparable)return[` baseline not comparable (${e.reason}) — regenerate it; this run cannot verify anything`];if(!e.regressed)return[` no regression against baseline (score ${e.scoreDelta>=0?"+":""}${String(e.scoreDelta)})`];const r=[` REGRESSED against baseline (score ${e.scoreDelta>=0?"+":""}${String(e.scoreDelta)})`];for(const n of e.dropped)r.push(` ${n.id}: ${String(n.before)} → ${String(n.after)}`);for(const n of e.newFailing)r.push(` ${n}: now failing`);for(const n of e.worsened)r.push(` ${n}: more findings than the baseline`);return e.projectRegressed&&r.push(" project: new schema/config rules fired"),r},F=(e,r)=>{const n=e.procedures.filter(i=>i.coverage!=="clean"&&i.coverage!=="exempt"),t=f(n),o=[`advisor health ${String(e.score)}/100 — ${e.grade}`,` ${String(e.summary.clean)} clean · ${String(e.summary.warned)} warned · ${String(e.summary.failing)} failing · ${String(e.summary.exempt)} exempt (${String(e.summary.procedures)} procedures, ${String(e.summary.rulesFired)} rules fired)`];return e.project.checks.length>0&&o.push("",` project ${String(e.project.score)}/100 — ${e.project.checks.map(i=>i.name).join(", ")}`),n.length>0&&o.push("",...n.map(i=>g(i,t)),""," run with --all for every procedure, or --entry <file#export> for one"),r!==void 0&&o.push("",...R(r)),o.join(`
|
|
2
2
|
`)},k=e=>e.coverage==="exempt"?[` exempt: ${e.exemptReason===void 0||e.exemptReason===""?"no reason given":e.exemptReason}`]:e.checks.map(r=>{const n=r.occurrences>1?`, ×${String(r.occurrences)}`:"";return` ${r.name} (−${String(r.weight)}${n})`}),T=e=>{const r=new Map;for(const n of e){const t=r.get(n.file);t===void 0?r.set(n.file,[n]):t.push(n)}return[...r].toSorted(([n],[t])=>b(n,t))},B=e=>{const r=f(e.procedures),n=[`advisor health ${String(e.score)}/100 — ${e.grade}`,"",` legend: ${l.clean} clean ${l.warned} warned ${l.failing} failing ${l.exempt} exempt`];for(const[t,o]of T(e.procedures)){n.push("",` ${t}`);for(const i of o)n.push(g(i,r),...k(i))}return n.join(`
|
|
3
3
|
`)},C=(e,r)=>{const n=e.procedures.find(o=>o.id===r);if(n===void 0){const o=e.procedures.slice(0,10).map(i=>` ${i.id}`);return[`no procedure ${r} in the map. Known ids:`,...o,e.procedures.length>10?" …":""].filter(Boolean).join(`
|
|
4
4
|
`)}const t=[`${E(n)} — ${String(n.score)}/100, ${n.coverage}`,` weight in the global mean: ${String(n.weight)}`];if(n.checks.length===0)return t.push(""," no rule fired"),t.join(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{writeFileSync as
|
|
1
|
+
import{writeFileSync as C}from"node:fs";import{findWranglerFile as O,readWranglerJsonc as x,applyModify as G}from"@lunora/config/cloudflare";import{r as E,n as w}from"../packem_shared/cloudflare-credentials-lC5JtPLe.mjs";import{d as R}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as g,exitCodeForStatus as h}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{A as S,a as T}from"../packem_shared/variables-acxcvVQa.mjs";const p=256,U=e=>e.length>p?`${e.slice(0,p)}… (truncated)`:e,k="https://api.cloudflare.com/client/v4/accounts",A=64,m={byok:"https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/",unifiedBilling:"https://developers.cloudflare.com/ai-gateway/features/unified-billing/"},l=(e,t,n)=>(e.error(n),{code:t,error:n}),v=async(e,t,n,a={})=>{const o=await e(t,{...a,headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"}}),r=await o.text();let i;try{i=JSON.parse(r)}catch{}return{body:i,ok:o.ok&&i?.success!==!1,status:o.status,text:r}},_=e=>{const t=(e.body?.errors??[]).map(n=>n.message).filter(n=>typeof n=="string");return`${String(e.status)}: ${U(t.length>0?t.join("; "):e.text)}`},D=(e,t,n)=>{let a=e;const o=[];for(const[r,i]of Object.entries(n))t?.[r]!==i&&(a=G(a,["vars",r],i),o.push(r));return{changed:o,text:a}},j=(e,t)=>{e.info(`next: \`<provider>/<model>\` slugs (e.g. "anthropic/claude-sonnet-5") now route through gateway "${t}". Before the first call,`),e.info(` configure how the provider is paid for in the Cloudflare dashboard (AI > AI Gateway > ${t}):`),e.info(` - Unified Billing (OpenAI, Anthropic, Google, xAI, Groq, DeepSeek): load credits — ${m.unifiedBilling}`),e.info(` - a key stored on the gateway (Mistral, Perplexity, OpenRouter, …): ${m.byok}`),e.info(" Then redeploy (`lunora deploy`) so the Worker picks up the new vars.")},L=async(e,t,n,a,o,r)=>{const i=`${k}/${encodeURIComponent(n)}/ai-gateway/gateways`,c=await v(t,`${i}/${encodeURIComponent(o)}`,a);if(c.ok){const u=c.body?.result?.collect_logs;return{action:"existing",collectLogs:typeof u=="boolean"?u:r}}if(c.status===401||c.status===403)return l(e,h(c.status),`ai gateway: could not read AI Gateway "${o}" (${_(c)})`);const s=await v(t,i,a,{body:JSON.stringify({cache_invalidate_on_update:!0,cache_ttl:0,collect_logs:r,id:o,rate_limiting_interval:0,rate_limiting_limit:0}),method:"POST"});return s.ok?{action:"created",collectLogs:r}:l(e,h(s.status),`ai gateway: could not create AI Gateway "${o}" (${_(s)})`)},N=e=>{const{logger:t}=e,n=O(e.cwd);if(n===void 0)return l(t,g.NOT_FOUND,"ai gateway: no wrangler.jsonc found — run `lunora init` (or `lunora dev`) first.");const{parsed:a,text:o}=x(n);if(a===void 0)return l(t,g.USAGE,`ai gateway: could not parse ${n} as JSONC.`);const r=w(e.id)??w(a.name);return r===void 0?l(t,g.USAGE,"ai gateway: no gateway id — pass --id <id>, or set `name` in wrangler.jsonc."):r.length>A?l(t,g.USAGE,`ai gateway: gateway id "${r}" is longer than ${String(A)} characters — pass a shorter --id.`):{gatewayId:r,parsed:a,text:o,wranglerPath:n}},f=e=>e?"on":"off",W=(e,t)=>(e.info(`ai gateway (dry run): would create or reuse AI Gateway "${t.gatewayId}" (log collection ${f(t.collectLogs)}).`),e.info(t.varsWritten.length===0?`ai gateway (dry run): ${t.wranglerPath} vars are already up to date.`:`ai gateway (dry run): would write ${t.varsWritten.join(", ")} into ${t.wranglerPath} vars.`),t.accountId===void 0&&e.warn("ai gateway (dry run): no CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc) — a real run will stop there."),{code:0,data:t}),P=async e=>{const{logger:t}=e,n=N(e);if("code"in n)return n;const{gatewayId:a,parsed:o,text:r,wranglerPath:i}=n,c=e.logs!==!1,{accountId:s,token:u}=E(e.environment??process.env,o.account_id),$={[T]:a,...s===void 0?{}:{[S]:s}},y=D(r,o.vars,$);if(e.dryRun===!0)return W(t,{accountId:s,action:"planned",collectLogs:c,dryRun:!0,gatewayId:a,varsWritten:y.changed,wranglerPath:i});if(u===void 0||s===void 0){const I=[u===void 0?"CLOUDFLARE_API_TOKEN (an API token with AI Gateway Write)":void 0,s===void 0?"CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc)":void 0].filter(b=>b!==void 0);return l(t,g.AUTH,`ai gateway: missing ${I.join(" and ")}.`)}const d=await L(t,e.fetch??globalThis.fetch.bind(globalThis),s,u,a,c);return"code"in d?d:(d.action==="created"?t.success(`ai gateway: created AI Gateway "${a}" (log collection ${f(d.collectLogs)}).`):t.info(`ai gateway: AI Gateway "${a}" already exists — reusing it (log collection ${f(d.collectLogs)}; change it in the dashboard).`),y.changed.length>0?(C(i,y.text,"utf8"),t.success(`ai gateway: wrote ${y.changed.join(", ")} into ${i} vars.`)):t.info(`ai gateway: ${i} vars are already up to date.`),j(t,a),{code:0,data:{accountId:s,action:d.action,collectLogs:d.collectLogs,dryRun:!1,gatewayId:a,varsWritten:y.changed,wranglerPath:i}})},B=async e=>e.subcommand!=="gateway"?l(e.logger,g.USAGE,`ai: unknown subcommand "${e.subcommand??""}" — expected: gateway. Example: lunora ai gateway`):P(e),X=R(({argument:e,cwd:t,logger:n,options:a})=>B({cwd:t,dryRun:a.dryRun===!0,id:a.id,logger:n,logs:a.logs,subcommand:e[0]}));export{X as execute,B as runAiCommand};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{writeFileSync as E,existsSync as w,cpSync as j,statSync as b,lstatSync as I,readdirSync as N,mkdirSync as D,readFileSync as M}from"node:fs";import{join as u,sep as x,relative as B,resolve as O,dirname as K}from"node:path";import{SDK_TARGETS as V,SDK_LANGUAGES as G,generateSdk as P,isTypedSchema as Q}from"@lunora/codegen";import{LunoraError as d}from"@lunora/errors";import{d as J}from"../packem_shared/command-CAXl2A1S.mjs";import{r as _,a as
|
|
2
|
-
`)[0]??i.message:String(i);a.warn(`could not fetch the transport at ${
|
|
3
|
-
`,"utf8")},te=u("lunora","_generated","openrpc.json"),re=t=>{if(!w(t))throw new d("NOT_FOUND",`no OpenRPC document at ${t} — run \`lunora codegen --api-spec openrpc\` (or \`--api-spec both\`) first, or pass --spec`);let
|
|
1
|
+
import{writeFileSync as E,existsSync as w,cpSync as j,statSync as b,lstatSync as I,readdirSync as N,mkdirSync as D,readFileSync as M}from"node:fs";import{join as u,sep as x,relative as B,resolve as O,dirname as K}from"node:path";import{SDK_TARGETS as V,SDK_LANGUAGES as G,generateSdk as P,isTypedSchema as Q}from"@lunora/codegen";import{LunoraError as d}from"@lunora/errors";import{d as J}from"../packem_shared/command-CAXl2A1S.mjs";import{r as _,a as q,b as H,s as W}from"../packem_shared/resolve-BrsV4Qyc.mjs";import{r as U}from"../packem_shared/cli-manifest-CRet0egA.mjs";const $="gh:anolilab/lunora/sdks",v="lunora-transport.json",X=/_test\.|Tests?\./u,k=t=>t.startsWith("test_")||X.test(t),A=(t,o="")=>N(t).toSorted((e,r)=>e.localeCompare(r)).flatMap(e=>{const r=u(t,e),a=o.length>0?`${o}/${e}`:e;return b(r).isDirectory()?A(r,a):[a]}),C=(t,o)=>{const e=I(o);if(e.isSymbolicLink())throw new d("INTERNAL",`refusing to vendor "${B(t,o)}" — it is a symlink, not a regular file`);if(e.isDirectory())for(const r of N(o))k(r)||C(t,u(o,r))},F=(t,o,e,r)=>{const a=u(t,e);if(!w(a))throw new d("NOT_FOUND",`transport is missing ${e}`);C(t,a);const s=u(o,r);return j(a,s,{filter:n=>!k(n.slice(n.lastIndexOf(x)+1)),force:!0,recursive:!0}),b(s).isDirectory()?A(s).map(n=>`${r}/${n}`):[r]},R=(t,o)=>o.vendor.every(e=>w(u(t,e.from))),z=async(t,o)=>{const{language:e,logger:r,outputDirectory:a,registryOptions:s,target:n}=o,{cleanup:f,directory:h}=await H(e,{...s,ref:t},$);try{if(!R(h,n)){r.warn(`${$}/${e} does not exist at ${t}.`);return}return{files:n.vendor.flatMap(c=>F(h,a,c.from,c.to)).toSorted((c,l)=>c.localeCompare(l)),ref:t,source:`${$}/${e}`,versionMatched:t===_()}}finally{f()}},Y=(t,o)=>{const{language:e,outputDirectory:r,target:a}=o,s=u(t,e);if(!w(s))throw new d("NOT_FOUND",`no transport at ${s} — --from must point at a directory of per-language transports (the repo's \`sdks/\`)`);if(!R(s,a)){const n=a.vendor.map(f=>f.from).join(", ");throw new d("NOT_FOUND",`${s} is not a ${e} transport — expected it to contain ${n}`)}return{files:a.vendor.flatMap(n=>F(s,r,n.from,n.to)).toSorted((n,f)=>n.localeCompare(f)),ref:"local",source:s,versionMatched:!1}},Z=async t=>{const{allowUnsafeSource:o,from:e,language:r,logger:a,outputDirectory:s,ref:n,source:f,target:h}=t;if(e!==void 0)return Y(e,{language:r,outputDirectory:s,target:h});const c={allowUnsafeSource:o,logger:a,names:[],source:f},l=async i=>z(await q(i,a),{language:r,logger:a,outputDirectory:s,registryOptions:c,target:h});if(n!==void 0&&n.length>0){const i=await l(n);if(i===void 0)throw new d("NOT_FOUND",`--ref ${n} carries no ${r} transport — pick a ref that does, or pass --from <dir> to copy a local one`);return i}const p=_(),g=await l(p).catch(i=>{const y=i instanceof Error?i.message.split(`
|
|
2
|
+
`)[0]??i.message:String(i);a.warn(`could not fetch the transport at ${p} (${y}).`)});if(g!==void 0)return g;const m=await l(void 0);if(m===void 0)throw new d("NOT_FOUND",`no ${r} transport at ${p} or on the release branch. This language may not be released yet — pass --from <dir> to copy one from a checkout, or --ref <tag> to name a ref that has it.`);return a.warn(`FALLBACK: the transport is NOT version-matched to this CLI (${U()}). Wanted ${p}, used ${m.ref}. The vendored wire protocol may not be the one this surface was generated against; ${v} records what was actually copied.`),m},ee=(t,o,e)=>{const r={cliVersion:U(),files:e.files,language:o,note:e.versionMatched?"Vendored by `lunora sdk generate` at the ref matching the CLI that generated the surface beside it. Regenerate with a newer CLI to upgrade.":"Vendored by `lunora sdk generate` from a ref that is NOT this CLI's release tag, so the transport and the generated surface may be different protocol vintages.",ref:e.ref,source:e.source,versionMatched:e.versionMatched};E(u(t,v),`${JSON.stringify(r,void 0,4)}
|
|
3
|
+
`,"utf8")},te=u("lunora","_generated","openrpc.json"),re=t=>{if(!w(t))throw new d("NOT_FOUND",`no OpenRPC document at ${t} — run \`lunora codegen --api-spec openrpc\` (or \`--api-spec both\`) first, or pass --spec`);let o;try{o=JSON.parse(M(t,"utf8"))}catch(e){throw new d("BAD_REQUEST",`${t} is not valid JSON: ${e instanceof Error?e.message:String(e)}`)}if(o===null||typeof o!="object"||!Array.isArray(o.methods))throw new d("BAD_REQUEST",`${t} is not an OpenRPC document (no \`methods\` array)`);return o},le=J(async({argument:t,cwd:o,logger:e,options:r})=>{const a=t[0]??"generate";if(a!=="generate")throw new d("BAD_REQUEST",`unknown subcommand "${a}" — the only one is \`lunora sdk generate\``);const s=r.lang??"python",n=V[s];if(n===void 0)throw new d("BAD_REQUEST",`unsupported --lang "${s}" — expected one of: ${G.join(" | ")}`);const f=W("sdk generate",{allowUnsafeSource:r.allowUnsafeSource,logger:e,names:[],source:r.source});if(f!==void 0)throw new d("BAD_REQUEST",f);const h=O(o,r.spec??te),c=O(o,r.out??u("sdk",s)),l=re(h);l.methods.length===0&&e.warn(`${h} declares no methods — writing an empty SDK.`);const{files:p,undeclared:g,unrepresentable:m}=await P(l,n);D(c,{recursive:!0});const i=await Z({allowUnsafeSource:r.allowUnsafeSource,from:r.from,language:s,logger:e,outputDirectory:c,ref:r.ref,source:r.source,target:n});ee(c,s,i);for(const[S,L]of Object.entries(p)){const T=u(c,S);D(K(T),{recursive:!0}),E(T,L,"utf8")}const y=l.methods.filter(S=>!Q(S.result?.schema)).length;return e.success(`Generated ${s} SDK for ${String(l.methods.length)} function(s) → ${c}`),e.info(`Vendored the ${s} transport (${String(i.files.length)} file(s)) from ${i.source} @ ${i.ref}${i.versionMatched?" — version-matched to this CLI":""}; see ${v}.`),e.info(n.requires.length>0?`Install in the consuming project: ${n.requires.join(", ")}.`:"Nothing to install — the transport is vendored and needs only the standard library."),g.length>0&&e.warn(`${String(g.length)} schema(s) are not expressible as a named ${s} model, so those arguments or returns stay untyped: ${g.join(", ")}`),m.length>0&&e.warn(`${String(m.length)} function(s) take or return a \`v.bigint()\`/\`v.bytes()\`, which no generated model can carry — their parameters stay untyped, so pass wire values directly: ${m.join(", ")}`),y>0&&e.warn(`${String(y)} of ${String(l.methods.length)} function(s) return an untyped result — declare \`.output()\` on them for typed returns.`),{code:0}});export{le as execute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as f,readFileSync as v,realpathSync as w}from"node:fs";import{join as u}from"node:path";import{DEV_VARS_FILE as h,parseDevVariableEntries as $,isPlaceholderValue as A,discoverSchemaInfo as R,inferLunoraBindings as S,resolveSchemaDirectory as j}from"@lunora/config";import{GLOBAL_FETCH_STRICTLY_PUBLIC_FLAG as m,resolveObservabilitySampling as L,findWranglerFile as I,readWranglerJsonc as O,validateWranglerProject as N,UNEXPORTED_CLASS_MARKER as y,detectCloudflareCliConfig as C,collectExportGaps as E}from"@lunora/config/cloudflare";import{i as D,m as P,c as V}from"../packem_shared/secret-key-DsMeUoEg.mjs";import{r as F,d as U}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{d as G}from"../packem_shared/command-CAXl2A1S.mjs";import{i as T}from"../packem_shared/path-containment-CgxYZggb.mjs";import{b as k,c as x,a as g}from"../packem_shared/variables-acxcvVQa.mjs";import{readServiceBindings as W}from"@lunora/codegen";const M=(t,r)=>{const e=new Set,n=s=>{for(const[a,i]of Object.entries(s??{}))typeof i=="string"&&i.trim().length>0&&e.add(a)};n(t.vars);for(const s of Object.values(t.env??{}))n(s.vars);const o=u(r,h);if(f(o))try{n(Object.fromEntries($(v(o,"utf8")).map(s=>[s.key,s.value])))}catch{}return e},B=(t,r,e,n)=>{if(t===void 0||!e)return;const o=M(t,r),s=o.has(k),a=t.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 ${k} 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."}),o.has(x)&&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:`${x} is set, but ctx.ai's Workers AI binding cannot send a gateway token — it is ignored on that path.`}),!s&&!o.has(g)&&n.push({code:"ai-gateway-default",fix:`Run \`lunora ai gateway\` to create a gateway for this app and write ${g} into wrangler vars.`,level:"info",message:`no ${g} in vars or ${h} — \`<provider>/<model>\` slugs route through the account's \`default\` AI Gateway.`})},K=(t,r,e)=>{if(t===void 0||!r)return;const n=[];(t.compatibility_flags??[]).includes(m)||n.push("the top level");for(const[o,s]of Object.entries(t.env??{}))s.compatibility_flags!==void 0&&!s.compatibility_flags.includes(m)&&n.push(`env.${o}`);n.length>0&&e.push({code:"cimd-fetch-not-strictly-public",fix:`Add "${m}" to "compatibility_flags" in wrangler.jsonc.`,level:"warn",message:`@lunora/auth/cimd/workers is imported, but ${n.join(", ")} of wrangler.jsonc lacks the ${m} compatibility flag — workersCimdFetch() refuses to build without it.`})},_=t=>{const{logs:r,traces:e}=L(t);return Object.entries({logs:r,traces:e}).filter(([,n])=>n.enabled&&n.headSamplingRate>=1).map(([n])=>n)},z=(t,r)=>{if(t===void 0)return;const e=[],n=_(t.observability);n.length>0&&e.push(`${n.join(" + ")} (top level)`);for(const[o,s]of Object.entries(t.env??{})){if(s.observability===void 0)continue;const a=_(s.observability);a.length>0&&e.push(`${a.join(" + ")} (env.${o})`)}e.length!==0&&r.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.`})},H=(t,r)=>{for(const e of W(t).services)for(const n of e.publicScopes){const o=n===""?"":` (env.${n})`;r.push({code:"service-workers-dev",fix:`Set \`"workers_dev": false\`${o} in ${e.wranglerPath} — the app's service binding is its only way in, so internal auth (HMAC) can go too.`,level:"warn",message:`service ${e.name} (${e.worker}) is bound by the app but still public on workers.dev${o}.`})}},q=t=>{const r=t.trim();if(r==="")return!0;const e=r.toLowerCase();return e.includes("replace")||e.startsWith("<")&&e.endsWith(">")},Y=t=>{const r=I(t);if(r===void 0)return{parsed:void 0,path:void 0};const{parsed:e}=O(r);return{parsed:e,path:r}},J=(t,r,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(r===void 0){n.push({code:"wrangler-unparseable",fix:`Check ${e} is valid JSONC.`,level:"fail",message:`Could not parse ${e}.`});return}const{report:o}=N({projectRoot:t});for(const i of o.errors.filter(l=>l.includes(y)))n.push({code:"wrangler-class-unexported",level:"fail",message:i});for(const i of o.warnings.filter(l=>l.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:i});const s=i=>i.includes(y)||i.includes("SHARD");for(const i of o.errors.filter(l=>!s(l)))n.push({code:"wrangler-invalid",fix:"Fix the reported wrangler.jsonc problem; `lunora verify` reports the same set.",level:"fail",message:i});for(const i of o.warnings.filter(l=>!l.includes("LUNORA_ORIGIN_URL")))n.push({code:"wrangler-advisory",level:"warn",message:i});const a=o.errors.find(i=>i.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})},X=(t,r)=>{f(u(t,"lunora.json"))&&r.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."})},Q=(t,r)=>{const e=C(t);e!==void 0&&r.push({code:"cf-config-present",fix:e.fix,level:"warn",message:e.message})},Z=(t,r)=>{t===void 0||t.limits?.cpu_ms!==void 0||r.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."})},ee=(t,r)=>{const e=(t?.r2_buckets??[]).map(n=>n?.bucket_name).filter(n=>typeof n=="string"&&n.length>0);e.length!==0&&r.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.`})},ne=(t,r)=>{const{error:e,info:n}=R(t,"lunora");if(e!==void 0){r.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 o of n?.vectorMetadata??[]){const s=P(o.kind);if(s===void 0){r.push({code:"vector-metadata-unfilterable",fix:"Filter on a string, number or boolean column, or drop it from `metadata`.",level:"warn",message:`vector index "${o.index}" declares metadata "${o.property}", whose type Vectorize cannot filter on.`});continue}r.push({code:"vector-metadata-index-required",fix:`wrangler ${V({index:o.index,property:o.property,type:s}).join(" ")}`,level:"info",message:`vector index "${o.index}" filters on metadata "${o.property}" — that needs a Vectorize metadata index (\`lunora deploy\` creates it).`})}},re=(t,r)=>{if(t===void 0)return;const e=(t.d1_databases??[]).filter(Boolean);for(const n of e){const o=typeof n.database_id=="string"?n.database_id:"";if(q(o)){const s=typeof n.binding=="string"&&n.binding.length>0?n.binding:"<unnamed>";r.push({code:"d1-placeholder-id",fix:"Run `wrangler d1 create <name>` and paste the returned database_id into wrangler.jsonc.",level:"fail",message:`D1 binding "${s}" has a placeholder database_id ("${o||"<empty>"}").`})}}},te=(t,r)=>{if(t===void 0)return;const e=(t.send_email??[]).filter(Boolean);for(const n of e){const o=typeof n.destination_address=="string"?n.destination_address:"";if(o!==""&&A(o)){const s=typeof n.name=="string"&&n.name.length>0?n.name:"send_email";r.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 ("${o}").`})}}},oe=(t,r)=>{const e=u(t,h);if(!f(e))return;let n;try{n=v(e,"utf8")}catch{return}const o=$(n).filter(s=>D(s.key)&&A(s.value)).map(s=>s.key);o.length>0&&r.push({code:"dev-vars-missing-secret",fix:"Run `lunora dev` to auto-generate secrets, or fill them in by hand.",level:"warn",message:`${h} has unfilled secret value(s): ${o.join(", ")}.`})},se=(t,r)=>{const{source:e}=F({cwd:t});e===void 0?r.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."}):r.push({code:"admin-token-set",level:"pass",message:`LUNORA_ADMIN_TOKEN is set (${U(e)}).`})},ae=async(t,r)=>{try{return await S({projectRoot:t,schemaDir:j(t)})}catch(e){r.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}},ie=(t,r)=>{const e=E(t),n=[...t.containers.map(o=>({...o,kind:"container"})),...t.workflows.map(o=>({...o,kind:"workflow"})),...t.agents.map(o=>({...o,kind:"agent"}))];for(const o of n.filter(s=>s.exported))r.push({code:"declared-export-ok",level:"pass",message:`${o.kind} "${o.exportName}" is exported by the worker entry.`});for(const o of e)r.push({code:"declared-export-missing",fix:`Add \`export * from "./lunora/_generated/${o.module}"\` to your worker entry (or re-run \`vis generate lunora-${o.kind}\`).`,level:"fail",message:`${o.kind} "${o.exportName}" is declared but ${o.className} is not exported by the worker entry.`})},ce=/^[\^~><= ]+/u,le=/^(?<core>\d+\.\d+\.\d+)(?:-(?<channel>[a-z]+)\.(?<counter>\d+))?$/u,de=t=>{const r=t.replace(ce,"").trim(),e=le.exec(r);if(!e?.groups)return;const{channel:n,core:o,counter:s}=e.groups;return{channel:n??"",core:o??"",counter:s===void 0?void 0:Number.parseInt(s,10),raw:r}},ue=(t,r)=>{const e=u(t,"package.json");if(!f(e))return;let n;try{n=JSON.parse(v(e,"utf8"))}catch{return}const o={...n.devDependencies,...n.dependencies},s=[];for(const[c,d]of Object.entries(o)){if(c!=="lunorash"&&!c.startsWith("@lunora/"))continue;const b=de(d);b&&s.push({name:c,version:b})}if(s.length<2)return;const a=c=>c.map(d=>`${d.name}@${d.version.raw}`).join(", "),i=new Set(s.map(c=>c.version.core)),l=new Set(s.map(c=>c.version.channel));if(i.size>1){r.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(i.size)} different versions: ${a(s)}.`});return}if(l.size>1){r.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 (${[...l].map(c=>c===""?"stable":c).join(" + ")}): ${a(s)}.`});return}const p=s.map(c=>c.version.counter).filter(c=>c!==void 0);if(p.length>1){const c=Math.min(...p),d=Math.max(...p);r.push({code:"version-counter-spread",level:"info",message:p.length===s.length&&c===d?`Lunora packages are all at ${[...l][0]??""}.${String(d)}.`:`Lunora pre-release counters span ${String(c)}–${String(d)}: ${a(s)}.`})}},fe=[u("@lunora","cli"),"lunorash"],pe=(t,r,e)=>{if(r===void 0)return;const n=[];for(const s of fe){const a=u(t,"node_modules",s);if(f(a))try{n.push(w(a))}catch{}}if(n.length===0)return;let o;try{o=w(r)}catch{return}n.some(s=>T(s,o))||e.push({code:"cli-shadowed",fix:"Run the project's own CLI: `pnpm exec lunora …` (or `npx lunora …`).",level:"warn",message:`the running lunora (${o}) is not the project's own install (${n.join(", ")}).`})},me=async t=>{const r=t.cwd??process.cwd(),e=[],{parsed:n,path:o}=Y(r);J(r,n,o,e),Q(r,e),re(n,e),te(n,e),Z(n,e),ee(n,e),z(n,e),oe(r,e),se(r,e),ue(r,e),ne(r,e),X(r,e),pe(r,t.executablePath??process.argv[1],e),H(r,e);const s=await ae(r,e);s!==void 0&&ie(s,e),B(n,r,s?.usesAi===!0,e),K(n,s?.usesCimdWorkers===!0,e);const a={fail:0,info:0,pass:0,warn:0};for(const l of e)a[l.level]+=1;const i=a.fail>0?1:0;return{code:i,findings:e,ok:i===0,summary:a}},he={fail:"FAIL",info:"INFO",pass:"PASS",warn:"WARN"},ge=(t,r)=>{r.info("lunora doctor — project preflight");for(const o of t.findings){const s=`[${he[o.level]}] ${o.message}`;o.level==="fail"?r.error(s):o.level==="warn"?r.warn(s):r.info(s),o.fix!==void 0&&o.level!=="pass"&&r.info(` fix: ${o.fix}`)}const{fail:e,warn:n}=t.summary;e>0?r.error(`${String(e)} failure(s), ${String(n)} warning(s).`):n>0?r.warn(`0 failures, ${String(n)} warning(s).`):r.success("all checks passed.")},ve=async t=>{const{logger:r}=t,e=await me({...t});return ge(e,r),e},je=G(async({cwd:t,format:r,logger:e})=>{const n=await ve({cwd:t,format:r,logger:e});return{code:n.code,data:{findings:n.findings,summary:n.summary}}});export{je as execute,me as runDoctor,ve as runDoctorCommand};
|
|
1
|
+
import{existsSync as m,readFileSync as k,realpathSync as h}from"node:fs";import{join as u}from"node:path";import{readServiceBindings as A,readProjectTarget as R,DEFAULT_TARGET as S}from"@lunora/codegen";import{DEV_VARS_FILE as p,parseDevVariableEntries as x,isPlaceholderValue as _,readProjectDependencies as j,discoverSchemaInfo as L,inferLunoraBindings as I,resolveSchemaDirectory as O}from"@lunora/config";import{GLOBAL_FETCH_STRICTLY_PUBLIC_FLAG as f,resolveObservabilitySampling as E,findWranglerFile as N,readWranglerJsonc as C,validateWranglerProject as D,UNEXPORTED_CLASS_MARKER as v,detectCloudflareCliConfig as P,collectExportGaps as T}from"@lunora/config/cloudflare";import{i as U,m as F,c as G,a as V}from"../packem_shared/secret-key-B4aoLE0w.mjs";import{r as B,d as W}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{n as $}from"../packem_shared/cloudflare-credentials-lC5JtPLe.mjs";import{d as M}from"../packem_shared/command-CAXl2A1S.mjs";import{i as K}from"../packem_shared/path-containment-CgxYZggb.mjs";import{b,c as w,a as g}from"../packem_shared/variables-acxcvVQa.mjs";const z=(t,r)=>{const e=new Set,n=s=>{for(const[c,i]of Object.entries(s??{}))typeof i=="string"&&i.trim().length>0&&e.add(c)};n(t.vars);for(const s of Object.values(t.env??{}))n(s.vars);const o=u(r,p);if(m(o))try{n(Object.fromEntries(x(k(o,"utf8")).map(s=>[s.key,s.value])))}catch{}return e},H=(t,r,e,n)=>{if(t===void 0||!e)return;const o=z(t,r),s=o.has(b),c=t.ai?.binding;!s&&(typeof c!="string"||c.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 ${b} 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."}),o.has(w)&&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:`${w} is set, but ctx.ai's Workers AI binding cannot send a gateway token — it is ignored on that path.`}),!s&&!o.has(g)&&n.push({code:"ai-gateway-default",fix:`Run \`lunora ai gateway\` to create a gateway for this app and write ${g} into wrangler vars.`,level:"info",message:`no ${g} in vars or ${p} — \`<provider>/<model>\` slugs route through the account's \`default\` AI Gateway.`})},q=(t,r,e)=>{if(t===void 0||!r)return;const n=[];(t.compatibility_flags??[]).includes(f)||n.push("the top level");for(const[o,s]of Object.entries(t.env??{}))s.compatibility_flags!==void 0&&!s.compatibility_flags.includes(f)&&n.push(`env.${o}`);n.length>0&&e.push({code:"cimd-fetch-not-strictly-public",fix:`Add "${f}" to "compatibility_flags" in wrangler.jsonc.`,level:"warn",message:`@lunora/auth/cimd/workers is imported, but ${n.join(", ")} of wrangler.jsonc lacks the ${f} compatibility flag — workersCimdFetch() refuses to build without it.`})},y=t=>{const{logs:r,traces:e}=E(t);return Object.entries({logs:r,traces:e}).filter(([,n])=>n.enabled&&n.headSamplingRate>=1).map(([n])=>n)},Y=(t,r)=>{if(t===void 0)return;const e=[],n=y(t.observability);n.length>0&&e.push(`${n.join(" + ")} (top level)`);for(const[o,s]of Object.entries(t.env??{})){if(s.observability===void 0)continue;const c=y(s.observability);c.length>0&&e.push(`${c.join(" + ")} (env.${o})`)}e.length!==0&&r.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.`})},J=(t,r)=>{for(const e of A(t).services)for(const n of e.publicScopes){const o=n===""?"":` (env.${n})`;r.push({code:"service-workers-dev",fix:`Set \`"workers_dev": false\`${o} in ${e.wranglerPath} — the app's service binding is its only way in, so internal auth (HMAC) can go too.`,level:"warn",message:`service ${e.name} (${e.worker}) is bound by the app but still public on workers.dev${o}.`})}},X=t=>{const r=t.trim();if(r==="")return!0;const e=r.toLowerCase();return e.includes("replace")||e.startsWith("<")&&e.endsWith(">")},Q=t=>{const r=N(t);if(r===void 0)return{parsed:void 0,path:void 0};const{parsed:e}=C(r);return{parsed:e,path:r}},Z=(t,r,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(r===void 0){n.push({code:"wrangler-unparseable",fix:`Check ${e} is valid JSONC.`,level:"fail",message:`Could not parse ${e}.`});return}const{report:o}=D({projectRoot:t});for(const i of o.errors.filter(a=>a.includes(v)))n.push({code:"wrangler-class-unexported",level:"fail",message:i});for(const i of o.warnings.filter(a=>a.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:i});const s=i=>i.includes(v)||i.includes("SHARD");for(const i of o.errors.filter(a=>!s(a)))n.push({code:"wrangler-invalid",fix:"Fix the reported wrangler.jsonc problem; `lunora verify` reports the same set.",level:"fail",message:i});for(const i of o.warnings.filter(a=>!a.includes("LUNORA_ORIGIN_URL")))n.push({code:"wrangler-advisory",level:"warn",message:i});const c=o.errors.find(i=>i.includes("SHARD"));c===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:c})},ee=(t,r)=>{m(u(t,"lunora.json"))&&r.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."})},ne=(t,r)=>{const e=P(t);e!==void 0&&r.push({code:"cf-config-present",fix:e.fix,level:"warn",message:e.message})},re=(t,r)=>{t===void 0||t.limits?.cpu_ms!==void 0||r.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."})},oe=(t,r)=>{const e=(t?.r2_buckets??[]).map(n=>n?.bucket_name).filter(n=>typeof n=="string"&&n.length>0);e.length!==0&&r.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.`})},te=(t,r)=>{const{error:e,info:n}=L(t,"lunora");if(e!==void 0){r.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 o of n?.vectorMetadata??[]){const s=F(o.kind);if(s===void 0){r.push({code:"vector-metadata-unfilterable",fix:"Filter on a string, number or boolean column, or drop it from `metadata`.",level:"warn",message:`vector index "${o.index}" declares metadata "${o.property}", whose type Vectorize cannot filter on.`});continue}r.push({code:"vector-metadata-index-required",fix:`wrangler ${G({index:o.index,property:o.property,type:s}).join(" ")}`,level:"info",message:`vector index "${o.index}" filters on metadata "${o.property}" — that needs a Vectorize metadata index (\`lunora deploy\` creates it).`})}},se=(t,r)=>{if(t===void 0)return;const e=(t.d1_databases??[]).filter(Boolean);for(const n of e){const o=typeof n.database_id=="string"?n.database_id:"";if(X(o)){const s=typeof n.binding=="string"&&n.binding.length>0?n.binding:"<unnamed>";r.push({code:"d1-placeholder-id",fix:"Run `wrangler d1 create <name>` and paste the returned database_id into wrangler.jsonc.",level:"fail",message:`D1 binding "${s}" has a placeholder database_id ("${o||"<empty>"}").`})}}},ae=(t,r)=>{if(t===void 0)return;const e=(t.send_email??[]).filter(Boolean);for(const n of e){const o=typeof n.destination_address=="string"?n.destination_address:"";if(o!==""&&_(o)){const s=typeof n.name=="string"&&n.name.length>0?n.name:"send_email";r.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 ("${o}").`})}}},ie=(t,r)=>{const e=u(t,p);if(!m(e))return;let n;try{n=k(e,"utf8")}catch{return}const o=x(n).filter(s=>U(s.key)&&_(s.value)).map(s=>s.key);o.length>0&&r.push({code:"dev-vars-missing-secret",fix:"Run `lunora dev` to auto-generate secrets, or fill them in by hand.",level:"warn",message:`${p} has unfilled secret value(s): ${o.join(", ")}.`})},ce=(t,r)=>{const{source:e}=B({cwd:t});e===void 0?r.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."}):r.push({code:"admin-token-set",level:"pass",message:`LUNORA_ADMIN_TOKEN is set (${W(e)}).`})},le=async(t,r)=>{try{return await I({projectRoot:t,schemaDir:O(t)})}catch(e){r.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}},de=(t,r)=>{const e=T(t),n=[...t.containers.map(o=>({...o,kind:"container"})),...t.workflows.map(o=>({...o,kind:"workflow"})),...t.agents.map(o=>({...o,kind:"agent"}))];for(const o of n.filter(s=>s.exported))r.push({code:"declared-export-ok",level:"pass",message:`${o.kind} "${o.exportName}" is exported by the worker entry.`});for(const o of e)r.push({code:"declared-export-missing",fix:`Add \`export * from "./lunora/_generated/${o.module}"\` to your worker entry (or re-run \`vis generate lunora-${o.kind}\`).`,level:"fail",message:`${o.kind} "${o.exportName}" is declared but ${o.className} is not exported by the worker entry.`})},ue=/^[\^~><= ]+/u,fe=/^(?<core>\d+\.\d+\.\d+)(?:-(?<channel>[a-z]+)\.(?<counter>\d+))?$/u,pe=t=>{const r=t.replace(ue,"").trim(),e=fe.exec(r);if(!e?.groups)return;const{channel:n,core:o,counter:s}=e.groups;return{channel:n??"",core:o??"",counter:s===void 0?void 0:Number.parseInt(s,10),raw:r}},me=(t,r)=>{const e=j(t),n=[];for(const[a,l]of Object.entries(e)){if(a!=="lunorash"&&!a.startsWith("@lunora/"))continue;const d=pe(l);d&&n.push({name:a,version:d})}if(n.length<2)return;const o=a=>a.map(l=>`${l.name}@${l.version.raw}`).join(", "),s=new Set(n.map(a=>a.version.core)),c=new Set(n.map(a=>a.version.channel));if(s.size>1){r.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: ${o(n)}.`});return}if(c.size>1){r.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 (${[...c].map(a=>a===""?"stable":a).join(" + ")}): ${o(n)}.`});return}const i=n.map(a=>a.version.counter).filter(a=>a!==void 0);if(i.length>1){const a=Math.min(...i),l=Math.max(...i);r.push({code:"version-counter-spread",level:"info",message:i.length===n.length&&a===l?`Lunora packages are all at ${[...c][0]??""}.${String(l)}.`:`Lunora pre-release counters span ${String(a)}–${String(l)}: ${o(n)}.`})}},ge=[u("@lunora","cli"),"lunorash"],he=(t,r,e)=>{if(r===void 0)return;const n=[];for(const s of ge){const c=u(t,"node_modules",s);if(m(c))try{n.push(h(c))}catch{}}if(n.length===0)return;let o;try{o=h(r)}catch{return}n.some(s=>K(s,o))||e.push({code:"cli-shadowed",fix:"Run the project's own CLI: `pnpm exec lunora …` (or `npx lunora …`).",level:"warn",message:`the running lunora (${o}) is not the project's own install (${n.join(", ")}).`})},ve={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"}},be=t=>{const r=new Map,e=(n,o)=>{const s=$(n);s!==void 0&&r.set(s,[...r.get(s)??[],o])};e(t?.account_id,"top level");for(const[n,o]of Object.entries(t?.env??{}))e(o?.account_id,`env.${n}`);return r},we=async(t,r,e,n)=>{if((R(t)??S)!=="cloudflare")return;const s=$((e.environment??process.env).CLOUDFLARE_ACCOUNT_ID)!==void 0?[]:[...be(r)],c=s.length===0?[[void 0,[]]]:s,i=await Promise.all(c.map(async([a,l])=>({accountId:a,blocks:l,budget:await V({accountId:a,environment:e.environment,fetch:e.fetch})})));for(const{accountId:a,blocks:l,budget:d}of i)n.push({...ve[d.verdict],message:i.length>1?`account ${String(a)} (${l.join(", ")}): ${d.message}`:d.message,...d.fix===void 0?{}:{fix:d.fix}})},ye=async t=>{const r=t.cwd??process.cwd(),e=[],{parsed:n,path:o}=Q(r);Z(r,n,o,e),ne(r,e),se(n,e),ae(n,e),re(n,e),oe(n,e),Y(n,e),ie(r,e),ce(r,e),me(r,e),te(r,e),ee(r,e),he(r,t.executablePath??process.argv[1],e),J(r,e);const s=await le(r,e);s!==void 0&&de(s,e),H(n,r,s?.usesAi===!0,e),await we(r,n,t,e),q(n,s?.usesCimdWorkers===!0,e);const c={fail:0,info:0,pass:0,warn:0};for(const a of e)c[a.level]+=1;const i=c.fail>0?1:0;return{code:i,findings:e,ok:i===0,summary:c}},ke={fail:"FAIL",info:"INFO",pass:"PASS",warn:"WARN"},xe=(t,r)=>{r.info("lunora doctor — project preflight");for(const o of t.findings){const s=`[${ke[o.level]}] ${o.message}`;o.level==="fail"?r.error(s):o.level==="warn"?r.warn(s):r.info(s),o.fix!==void 0&&o.level!=="pass"&&r.info(` fix: ${o.fix}`)}const{fail:e,warn:n}=t.summary;e>0?r.error(`${String(e)} failure(s), ${String(n)} warning(s).`):n>0?r.warn(`0 failures, ${String(n)} warning(s).`):r.success("all checks passed.")},_e=async t=>{const{logger:r}=t,e=await ye({...t});return xe(e,r),e},De=M(async({cwd:t,format:r,logger:e})=>{const n=await _e({cwd:t,format:r,logger:e});return{code:n.code,data:{findings:n.findings,summary:n.summary}}});export{De as execute,ye as runDoctor,_e as runDoctorCommand};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import{existsSync as O,readFileSync as
|
|
1
|
+
import{existsSync as O,readFileSync as k}from"node:fs";import{writeFile as I,mkdir as U}from"node:fs/promises";import{addArgsFor as B,detectPackageManager as F,parseSchema as Y,applyAdditiveEdit as q,projectUsesUmbrella as P}from"@lunora/config";import{join as _}from"@visulima/path";import{d as H}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as L}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{createRequire as K}from"node:module";import{LunoraError as N}from"@lunora/errors";const j=/^\//,G=(e,r)=>{try{const{args:n,command:t}=B(F(e),[r],{dev:!0});return`${t} ${n.join(" ")}`}catch{return`<your-package-manager> add -D ${r}`}},T=(e,r,n)=>{const t=K(_(n,"noop.cjs"));try{t.resolve(e)}catch{throw new N("INTERNAL",`\`lunora introspect\` needs the \`${r}\` driver to read this database, and it isn't installed.
|
|
2
2
|
|
|
3
3
|
Install it in your project:
|
|
4
4
|
|
|
5
|
-
${
|
|
5
|
+
${G(n,r)}`)}return t(e)},Q=e=>{const r=e.slice(0,Math.max(0,e.indexOf(":"))).toLowerCase();if(r==="postgres"||r==="postgresql")return"postgres";if(r==="mysql"||r==="mariadb")return"mysql";throw new N("INTERNAL",`Unrecognised database URL scheme \`${r}\`. Expected a \`postgres://\`, \`postgresql://\`, \`mysql://\`, or \`mariadb://\` connection string.`)},J=e=>{try{const r=new URL(e).pathname.replace(j,"");return r===""?void 0:decodeURIComponent(r)}catch{return}},W=async(e,r,n,t=process.cwd())=>{if(r==="postgres"){const m=T("pg","pg",t).Client,u=new m({connectionString:e});return await u.connect(),{close:async()=>{await u.end()},execute:async(l,p)=>(await u.query(l,[...p])).rows,schema:n??"public"}}const a=T("mysql2/promise","mysql2",t).createConnection,o=await a(e),i=n??J(e);if(i===void 0)throw new N("INTERNAL","A MySQL connection string must name a database (`mysql://host/<database>`), or pass `--schema`.");return{close:async()=>{await o.end()},execute:async(c,m)=>{const[u]=await o.execute(c,[...m]);return Array.isArray(u)?u:[]},schema:i}},h=new Set(["_creationTime","_id"]),X={bigint:"v.bigint()",bigserial:"v.bigint()",bit:"v.string()",bool:"v.boolean()",boolean:"v.boolean()",box:"v.string()",bpchar:"v.string()",bytea:"v.bytes()",char:"v.string()",character:"v.string()","character varying":"v.string()",cidr:"v.string()",circle:"v.string()",citext:"v.string()",date:"v.date()","double precision":"v.number()",float4:"v.number()",float8:"v.number()",inet:"v.string()",int:"v.number()",int2:"v.number()",int4:"v.number()",int8:"v.bigint()",integer:"v.number()",interval:"v.string()",json:"v.any()",jsonb:"v.any()",line:"v.string()",lseg:"v.string()",macaddr:"v.string()",money:"v.number()",name:"v.string()",numeric:"v.number()",path:"v.string()",point:"v.string()",polygon:"v.string()",real:"v.number()",serial:"v.number()",smallint:"v.number()",smallserial:"v.number()",text:"v.string()",time:"v.string()","time with time zone":"v.string()","time without time zone":"v.string()",timestamp:"v.timestamp()","timestamp with time zone":"v.timestamp()","timestamp without time zone":"v.timestamp()",timestamptz:"v.timestamp()",tsquery:"v.string()",tsvector:"v.string()",uuid:"v.string()",varbit:"v.string()",varchar:"v.string()",xml:"v.string()"},z={bigint:"v.bigint()",binary:"v.bytes()",bit:"v.number()",blob:"v.bytes()",char:"v.string()",date:"v.date()",datetime:"v.timestamp()",decimal:"v.number()",double:"v.number()",enum:"v.string()",float:"v.number()",int:"v.number()",integer:"v.number()",json:"v.any()",longblob:"v.bytes()",longtext:"v.string()",mediumblob:"v.bytes()",mediumint:"v.number()",mediumtext:"v.string()",numeric:"v.number()",set:"v.string()",smallint:"v.number()",text:"v.string()",time:"v.string()",timestamp:"v.timestamp()",tinyblob:"v.bytes()",tinyint:"v.number()",tinytext:"v.string()",varbinary:"v.bytes()",varchar:"v.string()",year:"v.number()"},w=(e,r)=>{const t=(r==="postgres"?X:z)[e.dataType],s=e.references===void 0?t:`v.id(${JSON.stringify(e.references.table)})`;let a=s??"v.any()";for(let o=0;o<e.arrayDepth;o+=1)a=`v.array(${a})`;return e.nullable&&(a=`v.optional(${a})`),{expression:a,known:s!==void 0}},M=/^[A-Z_$][\w$]*$/i,Z=/[^\dA-Z]+(.)?/gi,V=/^\d+/,ee=/[^\w-]/g,ne=/[\u003C\u003E\u002F\u2028\u2029]/g,te={"/":String.raw`\u002F`,"\u2028":String.raw`\u2028`,"\u2029":String.raw`\u2029`,"<":String.raw`\u003C`,">":String.raw`\u003E`},f=e=>JSON.stringify(e).replaceAll(ne,r=>te[r]??r),S=e=>M.test(e)?e:f(e),re=e=>M.test(e)?`.${e}`:`[${f(e)}]`,x=e=>e.replaceAll("*/",String.raw`*\/`),ae=e=>{const r=e.replaceAll(ee,"_");return r===""?"table":r},se=e=>{const n=e.replaceAll(Z,(t,s)=>s?.toUpperCase()??"").replace(V,"");return n===""?"table":`${n.charAt(0).toLowerCase()}${n.slice(1)}`},C=e=>e.columns.filter(r=>!h.has(r.name)),oe=e=>{const r=new Set(e.primaryKey);for(const n of e.indexes)for(const t of n.columns)r.add(t);for(const n of e.columns)n.references!==void 0&&r.add(n.name);return[...r].filter(n=>!h.has(n))},D=(e,r,n,t)=>e.references===void 0||r.has(e.references.table)?e:(t.push(`${n}.${e.name}: references \`${e.references.table}\`, which isn't in the generated schema — emitted as a plain column instead of \`v.id(...)\`.`),{arrayDepth:e.arrayDepth,dataType:e.dataType,name:e.name,nullable:e.nullable}),ie=(e,r,n,t)=>{const s=[];for(const o of e.columns){if(h.has(o.name)){t.push(`${e.name}.${o.name}: skipped — \`${o.name}\` is a Lunora system column. Rename it in the source database or map it by hand.`);continue}const i=D(o,n,e.name,t),{expression:c,known:m}=w(i,r);!m&&i.references===void 0&&(t.push(`${e.name}.${o.name}: no mapping for SQL type \`${o.dataType}\` — emitted as \`v.any()\`.`),s.push(` // TODO: \`${x(o.dataType)}\` has no direct validator; narrow this.`)),s.push(` ${S(o.name)}: ${c},`)}const a=[' .global({ backend: "hyperdrive" })'];if(e.primaryKey.length>0&&e.primaryKey.every(o=>!h.has(o))){const o=e.primaryKey.map(i=>f(i)).join(", ");a.push(` .index(${f(`by_${e.primaryKey.join("_")}`)}, [${o}], { unique: true })`)}for(const o of e.indexes){const i=o.columns.filter(u=>!h.has(u));if(i.length===0)continue;const c=i.map(u=>f(u)).join(", "),m=o.unique?", { unique: true }":"";a.push(` .index(${f(o.name)}, [${c}]${m})`)}return` ${S(e.name)}: defineTable({
|
|
6
6
|
${s.join(`
|
|
7
7
|
`)}
|
|
8
8
|
})
|
|
9
9
|
${a.join(`
|
|
10
|
-
`)},`},
|
|
10
|
+
`)},`},ce=(e,r,n)=>{const t=new Set(e.tables.map(a=>a.name)),s=e.tables.map(a=>ie(a,e.dialect,t,n)).join(`
|
|
11
11
|
`);return`/**
|
|
12
12
|
* Generated by \`lunora introspect\` from an existing ${e.dialect==="postgres"?"Postgres":"MySQL"} database.
|
|
13
13
|
*
|
|
@@ -17,13 +17,13 @@ ${a.join(`
|
|
|
17
17
|
* its rows live in the external database. Re-running \`lunora introspect\` will not
|
|
18
18
|
* overwrite it unless you pass \`--force\`.
|
|
19
19
|
*/
|
|
20
|
-
import { defineSchema, defineTable, v } from "${
|
|
20
|
+
import { defineSchema, defineTable, v } from "${r.serverImport}";
|
|
21
21
|
|
|
22
22
|
export default defineSchema({
|
|
23
23
|
${s}
|
|
24
24
|
});
|
|
25
|
-
`},
|
|
26
|
-
* Generated by \`lunora introspect\` for the \`${
|
|
25
|
+
`},me=(e,r,n,t=new Set([e.name]),s=[])=>{const a=se(e.name),o=oe(e),i=new Map(C(e).map(l=>[l.name,l])),c=o.map(l=>{const p=i.get(l);if(p===void 0)return;const v=D({...p,nullable:!1},t,e.name,[]),{expression:d,known:A}=w(v,r);return!A&&v.references===void 0&&s.push(`${e.name}.${l}: filter falls back to \`v.any()\` (no mapping for SQL type \`${p.dataType}\`).`),` ${S(l)}: ${d},`}).filter(l=>l!==void 0),m=[f("_creationTime"),...o.map(l=>f(l))].join(", "),u=`ctx.db${re(e.name)}`;return`/**
|
|
26
|
+
* Generated by \`lunora introspect\` for the \`${x(e.name)}\` table — a
|
|
27
27
|
* starting point you own and edit.
|
|
28
28
|
*
|
|
29
29
|
* Both procedures are RPC-only. Add \`.expose({ rest: true })\` once you've decided
|
|
@@ -50,13 +50,13 @@ ${c.join(`
|
|
|
50
50
|
export const list = c.query.input(${a}List.args).query(({ args, ctx }) => ${u}.findMany(${a}List.toQueryArgs(args)));
|
|
51
51
|
|
|
52
52
|
export const get = c.query.input({ id: v.id(${f(e.name)}) }).query(({ args, ctx }) => ${u}.get(args.id));
|
|
53
|
-
`},
|
|
53
|
+
`},le=(e,r)=>{const n=[],t=new Set(e.tables.map(a=>a.name)),s=[{contents:ce(e,r,n),path:"schema.ts"}];if(r.procedures){const a=new Map([["schema","the generated schema module"]]);for(const o of e.tables){if(C(o).length===0){n.push(`${o.name}: no usable columns — procedure module skipped.`);continue}const i=ae(o.name);i!==o.name&&n.push(`${o.name}: written as \`${i}.ts\` — the table name isn't usable as a filename.`);const c=a.get(i);if(c!==void 0){n.push(`${o.name}: procedure module skipped — its filename \`${i}.ts\` collides with table \`${c}\`.`);continue}a.set(i,o.name),s.push({contents:me(o,e.dialect,r,t,n),path:`${i}.ts`})}}return{files:s,warnings:n}},y=/^[A-Z_$][\w$]*$/i,ue=e=>e.startsWith("v.optional(")&&e.endsWith(")")?e.slice(11,-1):e,de=(e,r,n)=>{const t=[],s=new Set(r?.columns.map(a=>a.name));for(const a of e.columns){if(h.has(a.name)||s.has(a.name))continue;if(!y.test(a.name)){n.warnings.push(`${e.name}.${a.name}: skipped — column names must be bare identifiers to merge.`);continue}const o=a.references!==void 0&&!n.present.has(a.references.table),{expression:i}=w(o?{...a,references:void 0}:a,n.dialect);t.push({column:a.name,kind:"addOptionalColumn",table:e.name,validator:ue(i)}),!a.nullable&&r!==void 0&&n.warnings.push(`${e.name}.${a.name}: added as \`v.optional(...)\` — a required column on an existing table needs a backfill migration.`)}return t},pe=(e,r,n)=>{const t=[],s=new Set(r?.indexes.map(a=>a.name));for(const a of e.indexes){const o=a.columns.filter(i=>!h.has(i));if(!(s.has(a.name)||o.length===0)){if(!y.test(a.name)||!o.every(i=>y.test(i))){n.push(`${e.name}: index \`${a.name}\` skipped — index and column names must be bare identifiers to merge.`);continue}t.push({fields:o,kind:"addIndex",name:a.name,table:e.name,...a.unique?{unique:!0}:{}})}}return t},ge=(e,r,n)=>{const t=[],s=[],a=new Map(r.map(i=>[i.name,i])),o=new Set([...a.keys(),...e.tables.map(i=>i.name)]);for(const i of e.tables){if(!y.test(i.name)){s.push(`${i.name}: skipped — merging into an existing schema needs a table name that is a bare identifier.`);continue}const c=a.get(i.name);c===void 0&&t.push({global:{backend:"hyperdrive"},kind:"addTable",table:i.name}),t.push(...de(i,c,{dialect:n,present:o,warnings:s}),...pe(i,c,s))}return{edits:t,warnings:s}},fe=(e,r,n)=>{const t=Y(e);if(!t.ok)return{applied:0,warnings:[`lunora/schema.ts could not be parsed (${t.reason}) — leave it alone and merge by hand, or pass --force.`]};const s=ge(r,t.tables,n),a=[...s.warnings];let o=e,i=0;for(const c of s.edits){const m=q(o,c);m.ok?(o=m.text,i+=1):a.push(`${c.table}: skipped one edit (${m.reason}).`)}return{applied:i,warnings:a,...i===0?{}:{text:o}}},g=(e,r)=>{const n=e[r]??e[r.toLowerCase()]??e[r.toUpperCase()];return typeof n=="string"?n:typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):""},ve=`
|
|
54
54
|
SELECT c.table_name, c.column_name, c.is_nullable, c.data_type, c.udt_name
|
|
55
55
|
FROM information_schema.columns c
|
|
56
56
|
JOIN information_schema.tables t
|
|
57
57
|
ON t.table_schema = c.table_schema AND t.table_name = c.table_name
|
|
58
58
|
WHERE c.table_schema = $1 AND t.table_type = 'BASE TABLE'
|
|
59
|
-
ORDER BY c.table_name, c.ordinal_position`,
|
|
59
|
+
ORDER BY c.table_name, c.ordinal_position`,be=`
|
|
60
60
|
SELECT tc.table_name, kcu.column_name
|
|
61
61
|
FROM information_schema.table_constraints tc
|
|
62
62
|
JOIN information_schema.key_column_usage kcu
|
|
@@ -69,7 +69,7 @@ export const get = c.query.input({ id: v.id(${f(e.name)}) }).query(({ args, ctx
|
|
|
69
69
|
ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema
|
|
70
70
|
JOIN information_schema.constraint_column_usage ccu
|
|
71
71
|
ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
|
|
72
|
-
WHERE tc.table_schema = $1 AND tc.constraint_type = 'FOREIGN KEY'`,
|
|
72
|
+
WHERE tc.table_schema = $1 AND tc.constraint_type = 'FOREIGN KEY'`,Ee=`
|
|
73
73
|
SELECT t.relname AS table_name, i.relname AS index_name, ix.indisunique AS is_unique, a.attname AS column_name
|
|
74
74
|
FROM pg_class t
|
|
75
75
|
JOIN pg_index ix ON t.oid = ix.indrelid
|
|
@@ -78,22 +78,22 @@ export const get = c.query.input({ id: v.id(${f(e.name)}) }).query(({ args, ctx
|
|
|
78
78
|
CROSS JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, ord)
|
|
79
79
|
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
|
|
80
80
|
WHERE n.nspname = $1 AND t.relkind = 'r' AND NOT ix.indisprimary
|
|
81
|
-
ORDER BY t.relname, i.relname, k.ord`,
|
|
81
|
+
ORDER BY t.relname, i.relname, k.ord`,_e=`
|
|
82
82
|
SELECT c.TABLE_NAME, c.COLUMN_NAME, c.IS_NULLABLE, c.DATA_TYPE
|
|
83
83
|
FROM information_schema.COLUMNS c
|
|
84
84
|
JOIN information_schema.TABLES t
|
|
85
85
|
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME
|
|
86
86
|
WHERE c.TABLE_SCHEMA = ? AND t.TABLE_TYPE = 'BASE TABLE'
|
|
87
|
-
ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,
|
|
87
|
+
ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,ye=`
|
|
88
88
|
SELECT TABLE_NAME, COLUMN_NAME
|
|
89
89
|
FROM information_schema.KEY_COLUMN_USAGE
|
|
90
90
|
WHERE TABLE_SCHEMA = ? AND CONSTRAINT_NAME = 'PRIMARY'
|
|
91
|
-
ORDER BY TABLE_NAME, ORDINAL_POSITION`,
|
|
91
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION`,Ae=`
|
|
92
92
|
SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME AS foreign_table, REFERENCED_COLUMN_NAME AS foreign_column
|
|
93
93
|
FROM information_schema.KEY_COLUMN_USAGE
|
|
94
|
-
WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME IS NOT NULL`,
|
|
94
|
+
WHERE TABLE_SCHEMA = ? AND REFERENCED_TABLE_NAME IS NOT NULL`,Se=`
|
|
95
95
|
SELECT TABLE_NAME, INDEX_NAME AS index_name, NON_UNIQUE, COLUMN_NAME
|
|
96
96
|
FROM information_schema.STATISTICS
|
|
97
97
|
WHERE TABLE_SCHEMA = ? AND INDEX_NAME <> 'PRIMARY'
|
|
98
|
-
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX`,
|
|
99
|
-
`).length)} lines)`),!1):(await U(
|
|
98
|
+
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX`,E=e=>{const r=new Map;for(const n of e){const t=g(n,"table_name"),s=r.get(t);s===void 0?r.set(t,[n]):s.push(n)}return r},Ne=(e,r)=>{const n=g(e,"data_type").toLowerCase();if(r!=="postgres")return{arrayDepth:0,dataType:n};const t=g(e,"udt_name").toLowerCase();return n==="array"&&t.startsWith("_")?{arrayDepth:1,dataType:t.slice(1)}:{arrayDepth:0,dataType:t===""?n:t}},we=(e,r)=>{const n=new Map;for(const t of e){const s=g(t,"index_name"),a=r==="postgres"?g(t,"is_unique")==="true":g(t,"NON_UNIQUE")==="0",o=n.get(s);o===void 0?n.set(s,{columns:[g(t,"column_name")],unique:a}):o.columns.push(g(t,"column_name"))}return[...n.entries()].map(([t,s])=>({columns:s.columns,name:t,unique:s.unique}))},Re=async(e,r,n)=>{const t=r==="postgres",[s,a,o,i]=await Promise.all([e(t?ve:_e,[n]),e(t?be:ye,[n]),e(t?he:Ae,[n]),e(t?Ee:Se,[n])]),c=E(a),m=E(o),u=E(i),l=[];for(const[p,v]of E(s)){const d=new Map((m.get(p)??[]).map(b=>[g(b,"column_name"),{column:g(b,"foreign_column"),table:g(b,"foreign_table")}])),A=v.map(b=>{const R=g(b,"column_name"),$=d.get(R);return{...Ne(b,r),name:R,nullable:g(b,"is_nullable").toUpperCase()==="YES",...$===void 0?{}:{references:$}}});l.push({columns:A,indexes:we(u.get(p)??[],r),name:p,primaryKey:(c.get(p)??[]).map(b=>g(b,"column_name"))})}return l.sort((p,v)=>p.name.localeCompare(v.name)),{dialect:r,tables:l}},$e=e=>P(e)?"lunorash/server":"@lunora/server",Le=async(e,r,n)=>{const t=_(r,e.path);return O(t)&&n.force!==!0?(n.logger.warn(`skipped lunora/${e.path} — it already exists (pass --force to overwrite)`),!1):n.dryRun===!0?(n.logger.info(`would write lunora/${e.path} (${String(e.contents.split(`
|
|
99
|
+
`).length)} lines)`),!1):(await U(r,{recursive:!0}),await I(t,e.contents,"utf8"),n.logger.info(`wrote lunora/${e.path}`),!0)},Te=(e,r)=>{const n=r.tables;if(n===void 0||n.length===0)return e.tables;for(const t of n)e.tables.some(s=>s.name===t)||r.logger.warn(`--tables: no table named "${t}" exists in this schema — skipped.`);return e.tables.filter(t=>n.includes(t.name))},Oe=async(e,r,n,t)=>{const s=fe(k(e,"utf8"),r,n);for(const a of s.warnings)t.logger.warn(a);return s.text===void 0?(t.logger.info("lunora/schema.ts is already up to date with the database."),!1):t.dryRun===!0?(t.logger.info(`would merge ${String(s.applied)} addition(s) into lunora/schema.ts`),!1):(await I(e,s.text,"utf8"),t.logger.info(`merged ${String(s.applied)} addition(s) into lunora/schema.ts`),!0)},Ie=async e=>{const r=e.cwd??process.cwd();if(e.connection===void 0&&(e.url===void 0||e.url==="")){const d="`lunora introspect` needs a database URL: pass --url, or set DATABASE_URL.";return e.logger.error(d),{code:L.USAGE,error:d,written:[]}}const n=e.connection===void 0?Q(e.url):e.dialect??"postgres",t=e.connection??await W(e.url,n,e.schema,r);let s;try{s=await Re(t.execute,n,t.schema)}finally{e.connection===void 0&&await t.close()}const a=Te(s,e);if(a.length===0){const d="no tables found to introspect — check --schema and --tables.";return e.logger.error(d),{code:L.NOT_FOUND,error:d,written:[]}}const{files:o,warnings:i}=le({...s,tables:a},{procedures:e.procedures!==!1,serverImport:$e(r)}),c=_(r,"lunora"),m=[],u=_(c,"schema.ts"),l=O(u)&&e.force!==!0,p=l?o.filter(d=>d.path!=="schema.ts"):o;l&&await Oe(u,{...s,tables:a},n,e)&&m.push("schema.ts");for(const d of p)await Le(d,c,e)&&m.push(d.path);for(const d of i)e.logger.warn(d);e.logger.info(`introspected ${String(a.length)} table(s) from ${n}. Review the generated files before running \`lunora dev\`.`);const v=a.map(d=>d.name);return{code:0,dialect:n,tables:v,written:m}},Ye=H(async({cwd:e,format:r,logger:n,options:t})=>{const s=await Ie({cwd:e,dryRun:t.dryRun===!0,force:t.force===!0,format:r,logger:n,procedures:t.procedures!==!1,schema:t.schema,tables:t.tables===void 0?void 0:t.tables.split(",").map(a=>a.trim()),url:t.url??process.env.DATABASE_URL});return{code:s.code,data:s.dialect===void 0?void 0:{dialect:s.dialect,dryRun:t.dryRun===!0,tables:s.tables??[],written:s.written},error:s.error}});export{Ye as execute,$e as resolveServerImport,Ie as runIntrospectCommand};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{V as R}from"../packem_shared/cli-
|
|
1
|
+
import{V as R}from"../packem_shared/cli-6BRlq6nw.mjs";import{d as G}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as d}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{existsSync as f,readFileSync as h,mkdirSync as J,realpathSync as W,statSync as V,writeFileSync as z,chmodSync as H,renameSync as K,unlinkSync as q}from"node:fs";import{homedir as w}from"node:os";import{findWranglerFile as X}from"@lunora/config/cloudflare";import{join as s,dirname as B,relative as Z}from"@visulima/path";import{stringify as Q}from"smol-toml";import{parse as Y,modify as ee,applyEdits as re}from"jsonc-parser";import{detectPackageManager as te,execArgsFor as ne,readLiveDevServerState as S,DEV_STATE_DIR as oe}from"@lunora/config";import{resolveAdminToken as ae}from"@lunora/config/studio-host";import{connectLocalStdio as le}from"@lunora/mcp";import{resolve as y}from"node:path";import{i as ce}from"../packem_shared/path-containment-CgxYZggb.mjs";const m=e=>({args:[...e.args],command:e.command,...e.env===void 0?{}:{env:e.env}}),b=e=>e.transport==="http"?{type:"http",url:e.url}:m(e),se=e=>e.transport==="http"?{httpUrl:e.url}:m(e),ie=e=>e.transport==="http"?{serverUrl:e.url}:m(e),de=e=>({args:["-y","mcp-remote",e],command:"npx"}),ue=e=>e.transport==="http"?de(e.url):m(e),fe=e=>e.transport==="http"?{url:e.url}:m(e),me=e=>e.transport==="http"?{enabled:!0,type:"remote",url:e.url}:{command:[e.command,...e.args],enabled:!0,type:"local",...e.env===void 0?{}:{environment:e.env}},pe=e=>e.transport==="http"?{disabled:!1,type:"streamableHttp",url:e.url}:{disabled:!1,...m(e)},ge=({home:e,platform:r})=>{if(r==="darwin")return{global:s(e,"Library","Application Support","Claude","claude_desktop_config.json")};if(r==="win32"){const t=process.env.APPDATA;return t===void 0||t===""?{}:{global:s(t,"Claude","claude_desktop_config.json")}}return r==="linux"?{global:s(e,".config","Claude","claude_desktop_config.json")}:{}},ve=(e,r)=>Q({mcp_servers:{[e]:r.transport==="http"?{url:r.url}:{args:[...r.args],command:r.command,...r.env===void 0?{}:{env:r.env}}}}),v=[{buildEntry:b,paths:({projectRoot:e})=>({project:s(e,".mcp.json")}),format:"json",id:"claude-code",key:"mcpServers",label:"Claude Code"},{buildEntry:b,paths:({home:e,projectRoot:r})=>({global:s(e,".cursor","mcp.json"),project:s(r,".cursor","mcp.json")}),format:"json",id:"cursor",key:"mcpServers",label:"Cursor"},{buildEntry:b,paths:({projectRoot:e})=>({project:s(e,".vscode","mcp.json")}),format:"json",id:"vscode",key:"servers",label:"VS Code (GitHub Copilot)"},{buildEntry:se,paths:({home:e,projectRoot:r})=>({global:s(e,".gemini","settings.json"),project:s(r,".gemini","settings.json")}),format:"json",id:"gemini",key:"mcpServers",label:"Gemini CLI"},{buildEntry:ue,paths:ge,format:"json",id:"claude-desktop",key:"mcpServers",label:"Claude Desktop"},{buildEntry:ie,paths:({home:e})=>({global:s(e,".codeium","windsurf","mcp_config.json")}),format:"json",id:"windsurf",key:"mcpServers",label:"Windsurf"},{buildEntry:me,paths:({home:e,projectRoot:r})=>({global:s(e,".config","opencode","opencode.json"),project:s(r,"opencode.json")}),format:"json",id:"opencode",key:"mcp",label:"OpenCode"},{buildEntry:pe,paths:({home:e})=>({global:s(e,".cline","mcp.json")}),format:"json",id:"cline",key:"mcpServers",label:"Cline"},{buildEntry:fe,paths:({home:e})=>({global:s(e,".config","zed","settings.json")}),format:"json",id:"zed",key:"context_servers",label:"Zed"},{paths:({home:e,projectRoot:r})=>({global:s(e,".codex","config.toml"),project:s(r,".codex","config.toml")}),format:"manual",id:"codex",label:"Codex CLI",renderSnippet:ve}],x=v.map(e=>e.id),he=e=>v.find(r=>r.id===e.toLowerCase()),M=(e,r)=>{let t=e,n;try{t=W(e),n=V(t).mode}catch{}const a=`${t}.lunora-${Math.random().toString(36).slice(2,10)}.tmp`;try{z(a,r,"utf8"),n!==void 0&&H(a,n),K(a,t)}catch(o){try{q(a)}catch{}throw o}},ye={formattingOptions:{insertSpaces:!0,tabSize:4}},k=e=>{const r=[],t=Y(e,r,{allowTrailingComma:!0});return{errors:r,value:t}},g=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),E=(e,r,t)=>{if(!g(e))return;const n=e[r];return g(n)?n[t]:void 0},L=e=>{if(!f(e.path))return"absent";let r;try{r=h(e.path,"utf8")}catch{return"invalid"}if(r.trim().length===0)return"absent";const{errors:t,value:n}=k(r);return t.length>0||!g(n)?"invalid":E(n,e.key,e.name)===void 0?"absent":"present"},be=e=>L(e)==="present",U=(e,r,t,n,a)=>{try{const o=ee(e,[t,n],a,ye);o.length>0&&M(r,re(e,o));return}catch(o){return o instanceof Error?o.message:String(o)}},Se=e=>{const{entry:r,force:t=!1,key:n,name:a,path:o}=e,l=f(o)?h(o,"utf8"):"";if(l.trim().length===0)return J(B(o),{recursive:!0}),M(o,`${JSON.stringify({[n]:{[a]:r}},void 0,4)}
|
|
2
2
|
`),{action:"created",path:o};const{errors:c,value:i}=k(l);if(c.length>0)return{action:"invalid",error:`parse error at offset ${String(c[0]?.offset??0)}`,path:o};if(!g(i))return{action:"invalid",error:"the file's root is not a JSON object",path:o};const p=i[n];if(p!==void 0&&!g(p))return{action:"invalid",error:`"${n}" is not an object`,path:o};if(E(i,n,a)!==void 0&&!t)return{action:"skipped",path:o};const $=U(l,o,n,a,r);return $===void 0?{action:"updated",path:o}:{action:"invalid",error:$,path:o}},we=e=>{const{key:r,name:t,path:n}=e;if(!f(n))return{action:"absent",path:n};let a;try{a=h(n,"utf8")}catch(i){return{action:"invalid",error:i instanceof Error?i.message:String(i),path:n}}const{errors:o,value:l}=k(a);if(o.length>0)return{action:"invalid",error:`parse error at offset ${String(o[0]?.offset??0)}`,path:n};if(E(l,r,t)===void 0)return{action:"absent",path:n};const c=U(a,n,r,t,void 0);return c===void 0?{action:"removed",path:n}:{action:"invalid",error:c,path:n}},ke=["global","project"],u=(e,r)=>{const t=Z(r,e);return t.length>0&&!t.startsWith("..")?t:e},Ee=(e,r)=>{const t=e.paths(r);return ke.flatMap(n=>{const a=t[n];return a===void 0?[]:[{client:e,path:a,scope:n}]})},$e=(e,r,t,n)=>{const a=e.paths(r),o=t==="global"?"project":"global",l=a[t]??(n?void 0:a[o]);if(l!==void 0)return{client:e,path:l,scope:a[t]===void 0?o:t}},je=e=>v.filter(r=>Object.values(r.paths(e)).some(t=>f(t))),A=(e,r,t,n)=>{if(e.length===0)return r();const a=[];for(const o of e){const l=he(o);if(l===void 0){t.error(`mcp ${n}: unknown client "${o}". Known clients: ${x.join(", ")}.`);return}a.push(l)}return a},Oe=e=>e.client.format==="json",Ce="https://lunora.sh/mcp",N="lunora-docs",D="lunora",_e=e=>f(s(e,"lunora"))&&X(e)!==void 0,xe=e=>{const{args:r,command:t}=ne(e,"lunora",["mcp","serve"]);return{args:r,command:t,transport:"stdio"}},Me=e=>({transport:"http",url:e}),Le=e=>({home:e.home,platform:e.platform,projectRoot:e.cwd}),T=(e,r,t)=>$e(e,Le(t),t.scope??r.preferredScope,t.scope!==void 0||!r.allowScopeFallback),Ue=(e,r,t)=>{const n=T(e,r,t);if(n===void 0){t.logger.warn(`${e.label}: has no ${t.scope??r.preferredScope}-scoped config — skipped "${r.name}".`);return}const{path:a}=n,o=e.buildEntry(r.spec);if(t.print===!0){const i=u(a,t.cwd);return t.force!==!0&&be({key:e.key,name:r.name,path:a})?(t.logger.info(`${e.label}: "${r.name}" already configured in ${i} — a real install would skip it (re-run with --force to replace).`),{action:"printed",path:a}):(t.logger.info(`${e.label} → ${i}
|
|
3
3
|
${JSON.stringify({[e.key]:{[r.name]:o}},void 0,4)}`),{action:"printed",path:a})}const l=Se({entry:o,force:t.force===!0,key:e.key,name:r.name,path:a}),c=u(a,t.cwd);return l.action==="invalid"?t.logger.error(`${e.label}: ${c} is not valid JSON (${l.error??"unknown error"}) — left untouched.`):l.action==="skipped"?t.logger.info(`${e.label}: "${r.name}" already configured in ${c} — re-run with --force to replace it.`):t.logger.success(`${e.label}: ${l.action==="created"?"created":"updated"} ${c} with "${r.name}".`),{action:l.action,path:a}},Ae=(e,r,t)=>{const n=T(e,r,t)?.path;if(n===void 0){t.logger.warn(`${e.label}: no known config location on ${t.platform} — skipped.`);return}return t.logger.info(`${e.label}: add this to ${u(n,t.cwd)}
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as
|
|
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-CAXl2A1S.mjs";import{EXIT_CODE as f}from"../packem_shared/EXIT_CODE-D_XCixv8.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};
|