@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands
2 lines (1 loc) • 5.2 kB
JavaScript
import{writeFileSync as E}from"node:fs";import{findWranglerFile as b,readWranglerJsonc as x,applyModify as G}from"@lunora/config/cloudflare";import{d as R}from"../packem_shared/command-CAXl2A1S.mjs";import{EXIT_CODE as g,exitCodeForStatus as A}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{A as T,a as U}from"../packem_shared/variables-acxcvVQa.mjs";const p=256,D=e=>e.length>p?`${e.slice(0,p)}… (truncated)`:e,S="https://api.cloudflare.com/client/v4/accounts",m=64,_={byok:"https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/",unifiedBilling:"https://developers.cloudflare.com/ai-gateway/features/unified-billing/"},f=e=>typeof e=="string"&&e.trim().length>0?e.trim():void 0,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)}: ${D(t.length>0?t.join("; "):e.text)}`},L=(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}},k=(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 — ${_.unifiedBilling}`),e.info(` - a key stored on the gateway (Mistral, Perplexity, OpenRouter, …): ${_.byok}`),e.info(" Then redeploy (`lunora deploy`) so the Worker picks up the new vars.")},N=async(e,t,n,a,o,r)=>{const i=`${S}/${encodeURIComponent(n)}/ai-gateway/gateways`,c=await v(t,`${i}/${encodeURIComponent(o)}`,a);if(c.ok){const s=c.body?.result?.collect_logs;return{action:"existing",collectLogs:typeof s=="boolean"?s:r}}if(c.status===401||c.status===403)return l(e,A(c.status),`ai gateway: could not read AI Gateway "${o}" (${$(c)})`);const u=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 u.ok?{action:"created",collectLogs:r}:l(e,A(u.status),`ai gateway: could not create AI Gateway "${o}" (${$(u)})`)},j=e=>{const{logger:t}=e,n=b(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=f(e.id)??f(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>m?l(t,g.USAGE,`ai gateway: gateway id "${r}" is longer than ${String(m)} characters — pass a shorter --id.`):{gatewayId:r,parsed:a,text:o,wranglerPath:n}},h=e=>e?"on":"off",P=(e,t)=>(e.info(`ai gateway (dry run): would create or reuse AI Gateway "${t.gatewayId}" (log collection ${h(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}),W=async e=>{const{logger:t}=e,n=j(e);if("code"in n)return n;const{gatewayId:a,parsed:o,text:r,wranglerPath:i}=n,c=e.environment??process.env,u=e.logs!==!1,s=f(c.CLOUDFLARE_ACCOUNT_ID)??f(o.account_id),I={[U]:a,...s===void 0?{}:{[T]:s}},y=L(r,o.vars,I);if(e.dryRun===!0)return P(t,{accountId:s,action:"planned",collectLogs:u,dryRun:!0,gatewayId:a,varsWritten:y.changed,wranglerPath:i});const w=f(c.CLOUDFLARE_API_TOKEN);if(w===void 0||s===void 0){const O=[w===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(C=>C!==void 0);return l(t,g.AUTH,`ai gateway: missing ${O.join(" and ")}.`)}const d=await N(t,e.fetch??globalThis.fetch.bind(globalThis),s,w,a,u);return"code"in d?d:(d.action==="created"?t.success(`ai gateway: created AI Gateway "${a}" (log collection ${h(d.collectLogs)}).`):t.info(`ai gateway: AI Gateway "${a}" already exists — reusing it (log collection ${h(d.collectLogs)}; change it in the dashboard).`),y.changed.length>0?(E(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.`),k(t,a),{code:0,data:{accountId:s,action:d.action,collectLogs:d.collectLogs,dryRun:!1,gatewayId:a,varsWritten:y.changed,wranglerPath:i}})},F=async e=>e.subcommand!=="gateway"?l(e.logger,g.USAGE,`ai: unknown subcommand "${e.subcommand??""}" — expected: gateway. Example: lunora ai gateway`):W(e),V=R(({argument:e,cwd:t,logger:n,options:a})=>F({cwd:t,dryRun:a.dryRun===!0,id:a.id,logger:n,logs:a.logs,subcommand:e[0]}));export{V as execute,F as runAiCommand};