@lunora/cli 1.0.0-alpha.430 → 1.0.0-alpha.432
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/dispatch.mjs +1 -1
- package/dist/packem_chunks/handler.mjs +1 -1
- package/dist/packem_chunks/handler14.mjs +2 -2
- package/dist/packem_chunks/handler18.mjs +1 -1
- package/dist/packem_chunks/handler22.mjs +1 -1
- package/dist/packem_chunks/handler27.mjs +1 -1
- package/dist/packem_chunks/handler28.mjs +1 -1
- package/dist/packem_chunks/handler7.mjs +1 -1
- package/dist/packem_shared/{COMMANDS-D7MfgxzU.mjs → COMMANDS-CvN8UTJR.mjs} +1 -1
- package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-CyIsI3S1.mjs → DEFAULT_IMPORT_BATCH_SIZE-BwTREKH3.mjs} +1 -1
- package/dist/packem_shared/{cli-CDNZwrAT.mjs → cli-BGHRPHBJ.mjs} +2 -2
- package/dist/packem_shared/{import-BRM1hs7j.mjs → import-ZdIj7W66.mjs} +11 -10
- package/dist/packem_shared/{runMigrateGenerateCommand-BwemPIOz.mjs → runMigrateGenerateCommand-rZdsVcFj.mjs} +1 -1
- package/package.json +10 -10
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-BGHRPHBJ.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-BGHRPHBJ.mjs";import{runCodegenCommand as m}from"./packem_chunks/runCodegenCommand.mjs";import{r as p}from"./packem_chunks/handler.mjs";import{runDevCommand as f}from"./packem_chunks/runDevCommand.mjs";import{p as i}from"./packem_shared/plan-lzllz0Cn.mjs";import{runInitCommand as l}from"./packem_chunks/runInitCommand.mjs";import{runMigrateGenerateCommand as I}from"./packem_shared/runMigrateGenerateCommand-rZdsVcFj.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-4TujevyB.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-htqNZT8Q.mjs";import{default as N}from"./packem_shared/schemaIrToSnapshot-BHuNOfgs.mjs";import{createRecordingSpawner as j,defaultSpawner as q}from"./packem_shared/createRecordingSpawner-01ZngIwK.mjs";import{default as K}from"./packem_shared/parseManifest-CwPTKdtS.mjs";import{D as Y,r as Z}from"./packem_shared/import-ZdIj7W66.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-DvSfLvyt.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{d as u}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as c}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as m}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runMigrateGenerateCommand as l,runMigrateDataCommand as g,runMigrateToHyperdriveCommand as p,runMigrateCreateCommand as y}from"../packem_shared/runMigrateGenerateCommand-
|
|
1
|
+
import{d as u}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as c}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as m}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runMigrateGenerateCommand as l,runMigrateDataCommand as g,runMigrateToHyperdriveCommand as p,runMigrateCreateCommand as y}from"../packem_shared/runMigrateGenerateCommand-rZdsVcFj.mjs";const b=n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=l({cwd:r,logger:t,name:o[1]??a.name});return{code:e.code,data:{empty:e.empty,migrationFile:e.migrationFile===""?void 0:e.migrationFile,subcommand:"generate"},...e.error===void 0?{}:{error:e.error}}},f=async n=>{const{logger:o,options:r}=n,t=await p({batchSize:r.batchSize,fromToken:r.fromToken??r.token,fromUrl:r.fromUrl??r.url,logger:o,out:r.out,prod:r.prod===!0,tables:r.tables,toToken:r.toToken??r.token,toUrl:r.toUrl??r.url,yes:r.yes===!0});return{code:t.code,data:{bytes:t.bytes,exported:t.exported,imported:t.imported,subcommand:"d1-to-hyperdrive"},...t.error===void 0?{}:{error:t.error}}},w=async n=>{const{argument:o,cwd:r,logger:t,options:a}=n,e=o[1]??a.name;if(!e){const i="migrate create requires a name. Usage: lunora migrate create <name> [--table <table>]";return t.error(i),{code:c.USAGE,error:i}}const s=await y({cwd:r,logger:t,name:e,table:a.table});return{code:s.code,data:{file:s.file===""?void 0:s.file,name:e,subcommand:"create"}}},h=async(n,o)=>{const{argument:r,cwd:t,logger:a,options:e}=n,s=r[1]??e.name;if(!s){const d=`migrate ${o} requires a migration id. Usage: lunora migrate ${o} <id>`;return a.error(d),{code:c.USAGE,error:d}}const i=await g({batchSize:e.batchSize,cwd:t,dryRun:e.dryRun===!0,id:s,logger:a,maxBatches:e.steps,prod:e.prod===!0,subcommand:o,token:e.token,url:m({cwd:t,prod:e.prod===!0,url:e.url}),yes:e.yes===!0});return{code:i.code,data:{id:s,result:i.body,subcommand:o}}},C=u(async({argument:n,cwd:o,logger:r,options:t})=>{const a=n[0],e={argument:n,cwd:o,logger:r,options:t};switch(a){case"create":return await w(e);case"d1-to-hyperdrive":return await f(e);case"down":case"status":case"up":return await h(e,a);case"generate":return b(e);default:{const s=`unknown migrate subcommand: "${a??""}" — expected generate | create | up | down | status`;return e.logger.error(s),{code:c.USAGE,error:s}}}});export{C as execute};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{mkdirSync as xe,writeFileSync as H,existsSync as S,readFileSync as A,rmSync as W,mkdtempSync as kr,readdirSync as Me,realpathSync as br,statSync as Fe}from"node:fs";import{dirname as M,join as k,resolve as j,relative as Z,extname as Sr,sep as oe}from"node:path";import{PROJECT_CONFIG_FILENAMES as $r,isTestPath as ae,discoverMigrations as Er,readServiceBindings as le,runCodegen as Ar}from"@lunora/codegen";import{readLinkedProject as Ue,resolveSchemaDirectory as I,startDevProcess as Tr,acceptsConnection as Rr,resolveTargetOrThrow as se,detectFramework as Dr,DEFAULT_BUNDLE_META_PATH as _r,STRICT_ADVISORIES_ENV as Pr,LAZY_FUNCTIONS_ENV as Nr,BUNDLE_META_PATH_ENV as Cr,BUILD_TARGET_ENV as Or,BUILD_ROOT_ENV as jr,canonicalPath as Ir,COMPOSED_WORKER_ENTRY as be,discoverContainerInfo as Be,D1_PLACEHOLDER_ID as Se,writeLinkedProject as Lr,discoverSchemaInfo as He,DEV_VARS_FILE as P,parseDevVariableEntries as Ge,secretSourcesFromBindings as xr,inferLunoraBindings as Ve,requiredSecrets as Mr,mintableSecretKeysFor as Fr,generateSecretValue as Ur,upsertDevVariableLine as Br,writeDevVariablesFileAtomically as Hr,isPlaceholderValue as Gr,ensureGitignoreEntries as Vr,resolveLazyFunctions as z,resolveDeployDriver as J,MissingBuildOutputError as qr,clearApiSpecMarker as Wr,createDiscoveryCache as Kr,planToolchainInvocation as qe,probeCelldToolchain as zr,runPostCodegenHook as Jr,readApiSpecMarker as Yr}from"@lunora/config";import{bindingsNeedingDatabase as Xr,findWranglerFile as L,readWranglerJsonc as N,mergeWranglerEnvironment as Y,applyModify as We,HYPERDRIVE_PLACEHOLDER_ID as Qr,warnCloudflareCliConfigOnce as Zr,validateWranglerProject as et,wranglerCodegenInputs as rt,reconcileWranglerBindings as tt,reconcileWranglerCompatibilityDate as nt,reconcileWranglerCrons as ot,describePreservedCrons as at}from"@lunora/config/cloudflare";import{Spinner as st}from"@visulima/spinner";import{r as ue,a as it,e as ct}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as dt}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{i as lt,m as ut,g as gt,c as Ke,f as ft,e as mt}from"../packem_shared/vectorize-metadata-BDrKVEF4.mjs";import{w as ht}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as vt}from"../packem_shared/command-CB4JM-Jl.mjs";import{r as ge}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{a as pt}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as y}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{spawn as yt}from"node:child_process";import{randomUUID as ze,createHash as wt}from"node:crypto";import{setTimeout as kt}from"node:timers/promises";import{toolchainExecArgs as F,detectPackageManager as C,execArgsFor as fe}from"@lunora/config/cli";import{f as bt}from"../packem_shared/free-port-DjbFhaaT.mjs";import{containerBuildTag as St}from"@lunora/container";import{a as $t,i as Et}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as x}from"../packem_shared/createRecordingSpawner-01ZngIwK.mjs";import{r as Je}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{p as At,H as Tt,a as Rt,r as Dt}from"../packem_shared/health-probe-Bw9JFfIy.mjs";import{runAdvisor as _t,openShardAccess as $e}from"@lunora/advisor";import{join as b,relative as U,dirname as Ye}from"@visulima/path";import{Project as Xe,SyntaxKind as p,Node as w}from"ts-morph";import{r as Pt,c as Nt}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{runMigrateDataCommand as Ct}from"../packem_shared/runMigrateGenerateCommand-
|
|
1
|
+
import{mkdirSync as xe,writeFileSync as H,existsSync as S,readFileSync as A,rmSync as W,mkdtempSync as kr,readdirSync as Me,realpathSync as br,statSync as Fe}from"node:fs";import{dirname as M,join as k,resolve as j,relative as Z,extname as Sr,sep as oe}from"node:path";import{PROJECT_CONFIG_FILENAMES as $r,isTestPath as ae,discoverMigrations as Er,readServiceBindings as le,runCodegen as Ar}from"@lunora/codegen";import{readLinkedProject as Ue,resolveSchemaDirectory as I,startDevProcess as Tr,acceptsConnection as Rr,resolveTargetOrThrow as se,detectFramework as Dr,DEFAULT_BUNDLE_META_PATH as _r,STRICT_ADVISORIES_ENV as Pr,LAZY_FUNCTIONS_ENV as Nr,BUNDLE_META_PATH_ENV as Cr,BUILD_TARGET_ENV as Or,BUILD_ROOT_ENV as jr,canonicalPath as Ir,COMPOSED_WORKER_ENTRY as be,discoverContainerInfo as Be,D1_PLACEHOLDER_ID as Se,writeLinkedProject as Lr,discoverSchemaInfo as He,DEV_VARS_FILE as P,parseDevVariableEntries as Ge,secretSourcesFromBindings as xr,inferLunoraBindings as Ve,requiredSecrets as Mr,mintableSecretKeysFor as Fr,generateSecretValue as Ur,upsertDevVariableLine as Br,writeDevVariablesFileAtomically as Hr,isPlaceholderValue as Gr,ensureGitignoreEntries as Vr,resolveLazyFunctions as z,resolveDeployDriver as J,MissingBuildOutputError as qr,clearApiSpecMarker as Wr,createDiscoveryCache as Kr,planToolchainInvocation as qe,probeCelldToolchain as zr,runPostCodegenHook as Jr,readApiSpecMarker as Yr}from"@lunora/config";import{bindingsNeedingDatabase as Xr,findWranglerFile as L,readWranglerJsonc as N,mergeWranglerEnvironment as Y,applyModify as We,HYPERDRIVE_PLACEHOLDER_ID as Qr,warnCloudflareCliConfigOnce as Zr,validateWranglerProject as et,wranglerCodegenInputs as rt,reconcileWranglerBindings as tt,reconcileWranglerCompatibilityDate as nt,reconcileWranglerCrons as ot,describePreservedCrons as at}from"@lunora/config/cloudflare";import{Spinner as st}from"@visulima/spinner";import{r as ue,a as it,e as ct}from"../packem_shared/platform-diagnostics-Cn2g6Jh-.mjs";import{p as dt}from"../packem_shared/api-spec-lYyq4vL6.mjs";import{i as lt,m as ut,g as gt,c as Ke,f as ft,e as mt}from"../packem_shared/vectorize-metadata-BDrKVEF4.mjs";import{w as ht}from"../packem_shared/binding-manifest-file-CQ1eQYy1.mjs";import{d as vt}from"../packem_shared/command-CB4JM-Jl.mjs";import{r as ge}from"../packem_shared/wrangler-name-Dsk5K1f-.mjs";import{a as pt}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{EXIT_CODE as y}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{spawn as yt}from"node:child_process";import{randomUUID as ze,createHash as wt}from"node:crypto";import{setTimeout as kt}from"node:timers/promises";import{toolchainExecArgs as F,detectPackageManager as C,execArgsFor as fe}from"@lunora/config/cli";import{f as bt}from"../packem_shared/free-port-DjbFhaaT.mjs";import{containerBuildTag as St}from"@lunora/container";import{a as $t,i as Et}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as x}from"../packem_shared/createRecordingSpawner-01ZngIwK.mjs";import{r as Je}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{p as At,H as Tt,a as Rt,r as Dt}from"../packem_shared/health-probe-Bw9JFfIy.mjs";import{runAdvisor as _t,openShardAccess as $e}from"@lunora/advisor";import{join as b,relative as U,dirname as Ye}from"@visulima/path";import{Project as Xe,SyntaxKind as p,Node as w}from"ts-morph";import{r as Pt,c as Nt}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{runMigrateDataCommand as Ct}from"../packem_shared/runMigrateGenerateCommand-rZdsVcFj.mjs";import{c as Ot}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{l as jt}from"../packem_shared/wrangler-secrets-DlSlWvRH.mjs";const It=Object.freeze({edges:[],nodes:[],modules:[],unresolved:[],version:1}),Lt=20,Ee=e=>`${e.from} -${e.kind}-> ${e.to}`,V=(e,r)=>{const t=new Set(e);return r.filter(n=>!t.has(n)).toSorted((n,o)=>n.localeCompare(o))},xt=(e,r)=>{const t=e.modules.map(s=>s.name),n=r.modules.map(s=>s.name),o=e.edges.map(s=>Ee(s)),a=r.edges.map(s=>Ee(s));return{addedEdges:V(o,a),addedModules:V(t,n),removedEdges:V(a,o),removedModules:V(n,t)}},q=(e,r,t)=>{if(t.length===0)return[];const n=t.slice(0,Lt).map(a=>` ${e} ${a}`),o=t.length-n.length;return[`${r}:`,...n,...o>0?[` … and ${o.toString()} more`]:[]]},Mt=e=>{const r=[...q("+","modules added",e.addedModules),...q("-","modules removed",e.removedModules),...q("+","edges added",e.addedEdges),...q("-","edges removed",e.removedEdges)];return r.length===0?void 0:`architecture changes since the last deploy:
|
|
2
2
|
${r.join(`
|
|
3
3
|
`)}`},Ft=(e,r)=>k(e,".lunora",r===void 0?"architecture.json":`architecture.${r}.json`),Ut=e=>{if(S(e))try{const r=JSON.parse(A(e,"utf8"));return r.version===1&&Array.isArray(r.modules)&&Array.isArray(r.edges)?r:void 0}catch{return}},Bt=e=>{const{cwd:r,environment:t,logger:n}=e,o=Ft(r,t),a=Ut(o),s=e.current??(a===void 0?void 0:It);if(s===void 0)return;const i=a===void 0?void 0:Mt(xt(a,s));return i!==void 0&&n.info(i),()=>{try{xe(M(o),{recursive:!0}),H(o,`${JSON.stringify(s,void 0,2)}
|
|
4
4
|
`,"utf8")}catch(c){n.warn(`could not record the deployed architecture at ${o}: ${c instanceof Error?c.message:String(c)}`)}}},Ht=e=>{const{cwd:r,env:t,logger:n,mintedSecretsFile:o}=e;try{const a=Ue(r),s=a?.workerName??ge(r),i=e.url??a?.workerUrl;n.success("deploy complete"),n.info(` worker: ${s??"(see wrangler output above)"}`),t!==void 0&&n.info(` env: ${t}`),i===void 0?n.info(" url: run `lunora link --url <https://your-worker>` to record it"):n.info(` url: ${i}`),o!==void 0&&n.info(` secrets: generated value(s) recorded in ${o}`),e.logsAvailable&&n.info(" logs: lunora logs")}catch{}},Gt=/\/index$/u,Vt=/[^\dA-Za-z]/gu,qt=e=>e.replace(Gt,"").split("/").map(r=>r.replaceAll(Vt,"_")),Wt=e=>qt(e).join("_"),Kt=18787,Ae=3,Qe=12e4,zt=5e3,ee=10,Jt=new Set([".js",".mjs",".mts",".ts",".tsx"]),Yt=/\(void 0\)|is not a function|before initialization|of undefined/u,Xt=e=>typeof e=="object"&&e!==null&&typeof e.functionPath=="string"&&typeof e.message=="string",Qt=e=>{const r=I(e),t=new Map;let n;try{n=Me(k(e,r),{recursive:!0,encoding:"utf8"})}catch{return t}for(const o of n){const a=Sr(o);Jt.has(a)&&!o.split(oe).some(s=>s==="_generated"||s==="node_modules")&&t.set(Wt(o.slice(0,-a.length).split(oe).join("/")),k(r,o))}return t},Zt=(e,r)=>{const t=Qt(e),n=new Map;for(const{functionPath:o,message:a}of r){const s=o.lastIndexOf(":"),i=o.slice(0,s),c=`${i}\0${a}`,d=n.get(c)??{exports:[],message:a,namespace:i};d.exports.push(o.slice(s+1)),n.set(c,d)}return[...n.values()].map(({exports:o,message:a,namespace:s})=>{const i=Yt.test(a)?`
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{existsSync as S}from"node:fs";import{mkdtemp as v,writeFile as k,rm as j}from"node:fs/promises";import{tmpdir as R}from"node:os";import{discoverSchema as C,schemaFromIr as T}from"@lunora/codegen";import{seedPlan as
|
|
1
|
+
import{existsSync as S}from"node:fs";import{mkdtemp as v,writeFile as k,rm as j}from"node:fs/promises";import{tmpdir as R}from"node:os";import{discoverSchema as C,schemaFromIr as T}from"@lunora/codegen";import{seedPlan as O}from"@lunora/seed";import{join as w}from"@visulima/path";import{Project as $}from"ts-morph";import{t as y}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{d as A}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as m}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as E}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{b as F}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{runResetCommand as N}from"./runResetCommand.mjs";import{r as U}from"../packem_shared/import-ZdIj7W66.mjs";const I=(e,r)=>typeof r=="bigint"?Number(r):r instanceof ArrayBuffer?[...new Uint8Array(r)]:r,f=(e,r)=>({code:e,error:r,ndjson:""}),p=(e,r)=>{if(!S(r)){const o=`schema not found: ${r} — run \`vis generate lunora-table --name=<name>\` to create one`;return e.logger.error(o),f(m.NOT_FOUND,o)}if(e.reset===!0&&y({prod:e.prod,url:e.url})){const o="--reset only clears local .wrangler/state and cannot be combined with --prod or a remote --url";return e.logger.error(o),f(m.USAGE,o)}},x=async(e,r,o,t,n)=>{const s=await v(w(R(),"lunora-seed-")),d=w(s,"rows.ndjson");await k(d,e,"utf8");try{const a=await U({batchSize:n.batchSize,cwd:t,fetchImpl:n.fetchImpl,file:d,logger:n.logger,prod:n.prod,token:n.token,url:n.url,yes:!0}),i=a.body?.conflicts??0;return i>0&&n.logger.warn(`${String(i)} row(s) skipped — their _id already exists. Seeding is deterministic; re-run with --reset to wipe local state first, or a different --seed for fresh ids.`),{code:a.code,data:{conflicts:i,generated:r,inserted:a.inserted,tables:o},ndjson:e,...a.error===void 0?{}:{error:a.error}}}finally{await j(s,{force:!0,recursive:!0}).catch(()=>{})}},z=(e,r)=>{if(e.table===void 0||r.tables.some(n=>n.name===e.table))return;const o=r.tables.map(n=>n.name).join(", "),t=`unknown table "${e.table}" — schema defines: ${o||"(no tables)"}`;return e.logger.error(t),f(m.USAGE,t)},D=async(e,r)=>{if(!y({prod:e.prod,url:e.url})||e.yes===!0)return;if(!process.stdin.isTTY&&e.confirm===void 0){const s="seed: refusing to insert into a non-local target without confirmation — re-run with --yes";return e.logger.error(s),f(m.USAGE,s)}if(!await(e.confirm??F)(`This will insert ${String(r)} generated row(s) into ${e.url??"the production worker"}. Continue?`))return e.logger.info("seed: aborted"),f(m.CANCELLED,"seed: aborted at the confirmation prompt")},_=e=>{const r=[];for(const{rows:o,table:t}of e)for(const n of o)r.push(JSON.stringify({doc:n,table:t},I));return{lines:r,ndjson:r.length>0?`${r.join(`
|
|
2
2
|
`)}
|
|
3
|
-
`:""}},
|
|
3
|
+
`:""}},G=async e=>{const r=e.cwd??process.cwd(),o=e.format==="json",t=w(r,"lunora","schema.ts"),n=p(e,t);if(n!==void 0)return n;const s=new $({skipAddingFilesFromTsConfig:!0}),d=C(s,t),a=z(e,d);if(a!==void 0)return a;const i=T(d),l=O(i,{allowOrphans:e.allowOrphans===!0,defaultCount:e.count??10,now:e.now,only:e.table===void 0?void 0:[e.table],seed:e.seed??0}),{lines:h,ndjson:c}=_(l),u=h.length;if(e.dryRun===!0)return!o&&c.length>0&&process.stdout.write(c),e.logger.info(`generated ${String(u)} row(s) across ${String(l.length)} table(s) — dry run, nothing inserted`),{code:0,data:{conflicts:0,generated:u,inserted:0,rows:o?h.map(g=>JSON.parse(g)):void 0,tables:l.length},ndjson:c};if(u===0)return e.logger.warn("no rows generated — nothing to insert"),{code:0,data:{conflicts:0,generated:0,inserted:0,tables:l.length},ndjson:c};if(e.reset===!0){const g=await N({confirm:e.confirm,cwd:r,logger:e.logger,yes:e.yes});if(g.code!==0)return{code:g.code,error:"seed: --reset could not clear local state",ndjson:c}}const b=await D(e,u);return b!==void 0?b:x(c,u,l.length,r,e)},ee=A(async({cwd:e,format:r,logger:o,options:t})=>{const n=await G({allowOrphans:t.allowOrphans===!0,batchSize:t.batchSize,count:t.count,cwd:e,dryRun:t.dryRun===!0,format:r,logger:o,prod:t.prod===!0,reset:t.reset===!0,now:t.now,seed:t.seed,table:t.table,token:t.token,url:E({cwd:e,prod:t.prod===!0,url:t.url}),yes:t.yes===!0});return{code:n.code,data:n.data,error:n.error}});export{ee as execute,G as runSeedCommand};
|
|
@@ -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{resolveLazyFunctions as M}from"@lunora/config";import{wranglerCodegenInputs as F}from"@lunora/config/cloudflare";import{d as D}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as E}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{resolveLazyFunctions as M}from"@lunora/config";import{wranglerCodegenInputs as F}from"@lunora/config/cloudflare";import{d as D}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as E}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{D as u}from"../packem_shared/cli-BGHRPHBJ.mjs";const l={clean:"·",exempt:"–",failing:"✗",warned:"!"},d=(e,r)=>e.length>=r?e:e+" ".repeat(r-e.length),R=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}`,k=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},C=(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("",...k(r)),o.join(`
|
|
2
2
|
`)},T=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})`}),B=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))},I=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 B(e.procedures)){n.push("",` ${t}`);for(const i of o)n.push(g(i,r),...T(i))}return n.join(`
|
|
3
3
|
`)},L=(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=[`${R(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,3 +1,3 @@
|
|
|
1
|
-
import z,{join as m,resolve as I}from"node:path";import{isInteractive as J,promptYesNo as W}from"@lunora/config";import{backupObjectKey as v,isBackupManifestEntry as D,normalizeBackupPrefix as Y,BACKUP_KEY_PREFIX as X,backupManifestKey as Q,isBackupManifestKey as V,backupObjectKeyOfManifest as G}from"@lunora/runtime";import{r as E,t as Z}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{r as A,a as T}from"../packem_shared/admin-url-DJB8P6Jh.mjs";import{d as ee}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as u,isRefusal as p,exitCodeForStatus as k}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as re}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runExportCommand as te}from"../packem_shared/runExportCommand-slAq5BCF.mjs";import{b as ae,l as oe,M as U,u as ne,r as ie}from"../packem_shared/import-
|
|
1
|
+
import z,{join as m,resolve as I}from"node:path";import{isInteractive as J,promptYesNo as W}from"@lunora/config";import{backupObjectKey as v,isBackupManifestEntry as D,normalizeBackupPrefix as Y,BACKUP_KEY_PREFIX as X,backupManifestKey as Q,isBackupManifestKey as V,backupObjectKeyOfManifest as G}from"@lunora/runtime";import{r as E,t as Z}from"../packem_shared/admin-token-VdUnvnKW.mjs";import{r as A,a as T}from"../packem_shared/admin-url-DJB8P6Jh.mjs";import{d as ee}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as u,isRefusal as p,exitCodeForStatus as k}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as re}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{runExportCommand as te}from"../packem_shared/runExportCommand-slAq5BCF.mjs";import{b as ae,l as oe,M as U,u as ne,r as ie}from"../packem_shared/import-ZdIj7W66.mjs";import{readAndLogBody as y}from"./runRpcCommand.mjs";import{randomUUID as j,createHash as se}from"node:crypto";import{existsSync as N,createReadStream as ce,createWriteStream as de}from"node:fs";import{mkdir as ue,rm as b,rename as C,readFile as q,writeFile as le,realpath as _,mkdtemp as x}from"node:fs/promises";import{LunoraError as w}from"@lunora/errors";import{i as R}from"../packem_shared/path-containment-CgxYZggb.mjs";import{tmpdir as O}from"node:os";import{Readable as fe}from"node:stream";import{pipeline as me}from"node:stream/promises";const S="manifest.json",L=async e=>{const t=se("sha256");let r=0;for await(const a of ce(e)){const o=a;r+=o.byteLength,t.update(o)}return{bytes:r,sha256:t.digest("hex")}},P=async e=>{const t=m(e,S);if(!N(t))return[];let r;try{r=JSON.parse(await q(t,"utf8"))}catch(a){const o=a instanceof Error?a.message:String(a);throw new w("INTERNAL",`backup: ${t} exists but is not valid JSON (${o}) — refusing to overwrite it; fix or remove it manually`,{cause:a})}if(!Array.isArray(r))throw new TypeError(`backup: ${t} exists but is not a JSON array — refusing to overwrite it; fix or remove it manually`);return r.filter(a=>D(a))},ge=async(e,t)=>{const r=m(e,`${S}.${j()}.tmp`);try{await le(r,`${JSON.stringify(t,void 0,2)}
|
|
2
2
|
`,"utf8"),await C(r,m(e,S))}catch(a){throw await b(r,{force:!0}),a}},he=async(e,t)=>{const r=I(e),a=I(r,t.file),o=()=>{throw new w("INTERNAL",`backup: entry ${t.id} points outside ${r} (${t.file}) — refusing to read it`)};if(R(r,a)||o(),!!N(a))return R(await _(r),await _(a))||o(),a},we=e=>{const t=r=>v("",r);return{commit:async(r,a)=>{await C(a,m(e,r))},label:e,list:async()=>P(e),locate:t,materialize:async(r,a)=>{const o=i=>i===void 0?void 0:{path:i,release:()=>Promise.resolve()};return o(r===void 0?N(a)?a:void 0:await he(e,r))},record:async r=>{const a=await P(e);a.push(r),await ge(e,a)},stage:async r=>{await ue(e,{recursive:!0});const a=m(e,`${t(r)}.${j()}.partial`);return{path:a,release:()=>b(a,{force:!0})}}}},be="/_lunora/admin/storage/object",ke="/_lunora/admin/storage",B=8,ye=(e,t=z)=>{const r=t.basename(e);return r===""||r==="."||r===".."?"snapshot.ndjson":r},pe=e=>{const{context:t,logger:r}=e,a=Y(e.prefix??X),o=`bucket ${t.bucket??"(default)"} under ${a}`,i=ae(t),n=c=>`${t.baseUrl}${be}?key=${encodeURIComponent(c)}${i}`,f=async c=>{const s=await t.fetchImpl(n(c),{headers:{authorization:`Bearer ${t.token}`},method:"GET"});if(!s.ok){const l=await s.text().catch(()=>"<no body>");throw new w("INTERNAL",`backup: could not read ${c} (HTTP ${String(s.status)}): ${l}`)}return s.text()},H=async c=>{try{const s=JSON.parse(await f(c));if(D(s))return s;r.warn(`backup: ignoring ${c} — not a backup manifest`)}catch(s){r.warn(`backup: ignoring ${c} — ${s instanceof Error?s.message:String(s)}`)}};return{commit:async(c,s,l)=>{if(l.bytes>U)throw new w("INTERNAL",`backup: the snapshot is ${String(l.bytes)} bytes, above the ${String(U)}-byte limit for a checksum-verified upload — nothing was written. Narrow it with \`--tables\`, or write it to a directory with \`--dir\` and move the file yourself (\`wrangler r2 object put\`).`);const d=await q(s);await ne(t,c,d,{contentType:"application/x-ndjson",id:c,sha256:l.sha256,size:l.bytes},r)},label:o,list:async()=>{const s=(await oe(t,a)).map(d=>d.key).filter(d=>V(d)),l=[];for(let d=0;d<s.length;d+=B){const g=await Promise.all(s.slice(d,d+B).map(async h=>H(h)));l.push(...g.filter(h=>h!==void 0))}return l.toSorted((d,g)=>d.id.localeCompare(g.id))},locate:c=>v(a,c),materialize:async(c,s)=>{const l=c?.file??s,d=await t.fetchImpl(n(l),{headers:{authorization:`Bearer ${t.token}`},method:"GET"});if(d.status===404)return;if(!d.ok||d.body===null){const $=await d.text().catch(()=>"<no body>");throw new w("INTERNAL",`backup: could not download ${l} (HTTP ${String(d.status)}): ${$}`)}const g=await x(m(O(),"lunora-backup-")),h=m(g,ye(l));try{await me(fe.from(d.body),de(h))}catch($){throw await b(g,{force:!0,recursive:!0}),$}return{path:h,release:async()=>b(g,{force:!0,recursive:!0})}},record:async c=>{const s=Q(c.file),l=await t.fetchImpl(`${t.baseUrl}${ke}?key=${encodeURIComponent(s)}${i}`,{body:`${JSON.stringify(c,void 0,2)}
|
|
3
3
|
`,headers:{authorization:`Bearer ${t.token}`,"content-type":"application/json"},method:"PUT"});if(!l.ok){const d=await l.text().catch(()=>"<no body>");throw new w("INTERNAL",`backup: snapshot ${c.file} was written but its manifest was not (HTTP ${String(l.status)}): ${d}`)}},stage:async c=>{const s=await x(m(O(),"lunora-backup-"));return{path:m(s,v("",c)),release:async()=>b(s,{force:!0,recursive:!0})}}}},$e=".lunora-backups",F="/_lunora/admin/backup/retention",ve="/_lunora/admin/backup/prune",Se="/_lunora/admin/pitr",Ee="__lunora_admin__:getPitrBookmark",Ae="__lunora_admin__:pitrRestore",Te=async(e,t)=>{const r=(e.now??(()=>new Date))().toISOString(),a=await t.stage(r);try{const o=await te({cwd:e.cwd,fetchImpl:e.fetchImpl,logger:e.logger,out:a.path,prod:e.prod,tables:e.tables,token:e.token,url:e.url});if(o.code!==0)return{code:o.code};const i=t.locate(r),n=await L(a.path);await t.commit(i,a.path,n);const f={bytes:n.bytes,createdAt:r,file:i,id:r,rows:o.rows,sha256:n.sha256,tables:e.tables};return await t.record(f),e.logger.success(`backup created: ${i} (${o.rows.toString()} rows, ${n.bytes.toString()} bytes)`),{code:0,entry:f}}finally{await a.release().catch(o=>{e.logger.warn(`backup: could not clean up staging (${o instanceof Error?o.message:String(o)})`)})}},Ne=async(e,t,r)=>{if(t?.sha256===void 0)return r.error(t===void 0?"--verify needs a recorded checksum, and a snapshot restored by path/key has no manifest entry — restore it by its backup id instead":`--verify: backup ${t.id} carries no recorded checksum — it was taken by a release before checksums existed`),{code:u.USAGE};const a=await L(e);if(a.sha256!==t.sha256)return r.error(`--verify: ${t.file} does not match its recorded checksum (expected ${t.sha256}, got ${a.sha256}) — nothing was imported`),{code:1};r.success(`verified ${t.file} against its recorded sha256`)},Ie=async(e,t)=>{const r=await t.list();if(r.length===0)return e.logger.info(`no backups found in ${t.label}`),{code:0,entries:[]};for(const a of r)e.logger.info(`${a.id} ${a.rows.toString()} rows ${a.bytes.toString()} bytes ${a.file}`);return{code:0,entries:r}},Ue=async(e,t)=>{const{target:r}=e;if(r===void 0||r.length===0)return e.logger.error("restore requires a backup id or file path. Usage: lunora backup restore <id|file>"),{code:u.USAGE};const o=(await t.list()).find(n=>n.id===r),i=await t.materialize(o,r);if(i===void 0)return e.logger.error(`backup not found: ${r}`),{code:u.NOT_FOUND};try{if(e.verify===!0){const f=await Ne(i.path,o,e.logger);if(f!==void 0)return f}const n=await ie({cwd:e.cwd,fetchImpl:e.fetchImpl,file:i.path,logger:e.logger,prod:e.prod,token:e.token,url:e.url,yes:e.yes});return{code:n.code,entry:o,restored:n.inserted}}finally{await i.release().catch(n=>{e.logger.warn(`backup: could not clean up the downloaded snapshot (${n instanceof Error?n.message:String(n)})`)})}},_e=e=>{if(e.prod&&e.url===void 0)return e.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)"),{refused:u.USAGE};if(e.restore===!0&&e.at===void 0&&e.bookmark===void 0)return e.logger.error("pitr --restore requires --at <time> or --bookmark <bookmark>"),{refused:u.USAGE};const t=A(e.url,e.logger,e.cwd);if(t===void 0)return{refused:u.USAGE};if(e.restore===!0&&Z({prod:e.prod,url:t})&&e.yes!==!0)return e.logger.error(`pitr --restore restores data in place at ${t}, which is not local. Re-run with --yes to confirm.`),{refused:u.USAGE};const{token:r}=E({cwd:e.cwd??process.cwd(),token:e.token,url:t});return r?{fetchImpl:e.adminFetch??T,requestUrl:`${t}${Se}`,token:r}:(e.logger.error("admin token required — pass --token, set LUNORA_ADMIN_TOKEN, or add it to .dev.vars (local targets only)"),{refused:u.AUTH})},xe=(e,t)=>{const r={};return e.at!==void 0&&(r.time=e.at),t&&e.bookmark!==void 0&&(r.bookmark=e.bookmark),t&&e.restart===!0&&(r.restart=!0),r},Re=async e=>{const t=_e(e);if(p(t))return{code:t.refused};const r=e.restore===!0,a=r?Ae:Ee,o=xe(e,r),i=r?"restore":"bookmark";e.logger.info(`POST ${t.requestUrl} -> pitr ${i}${e.shard===void 0?"":` (shard "${e.shard}")`}`);const n=await t.fetchImpl(t.requestUrl,{body:JSON.stringify({args:o,functionPath:a,shardKey:e.shard}),headers:{authorization:`Bearer ${t.token}`,"content-type":"application/json"},method:"POST"});return{body:await y(n,e.logger),code:n.ok?0:k(n.status)}},Oe=(e,t)=>{if(e.bucket===void 0)return e.prefix!==void 0?(e.logger.error("--prefix applies only to an R2 destination — pass --bucket alongside it, or use --dir to point at a local directory."),{refused:u.USAGE}):we(m(t,e.dir??$e));if(e.dir!==void 0)return e.logger.error("--dir applies only to a local destination and does not apply alongside --bucket — drop one."),{refused:u.USAGE};if(e.prod===!0&&e.url===void 0)return e.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)"),{refused:u.USAGE};const r=A(e.url,e.logger,e.cwd);if(r===void 0)return{refused:u.USAGE};const{token:a}=E({cwd:t,token:e.token,url:r});if(!a)return e.logger.error("admin token required — pass --token, set LUNORA_ADMIN_TOKEN, or add it to .dev.vars (local targets only)"),{refused:u.AUTH};const o=e.fetchImpl??T;return pe({context:{baseUrl:r,bucket:e.bucket==="default"?void 0:e.bucket,fetchImpl:o,token:a},logger:e.logger,prefix:e.prefix})},M=e=>{const t=[e.bucket===void 0?void 0:"--bucket",e.prefix===void 0?void 0:"--prefix",e.dir===void 0?void 0:"--dir"].filter(i=>i!==void 0);if(t.length>0)return e.logger.error(`${t.join(" / ")} ${t.length===1?"does":"do"} not apply here — retention and prune act on the store the worker itself is configured with (\`backupStore\`), not a destination you name.`),{refused:u.USAGE};if(e.prod===!0&&e.url===void 0)return e.logger.error("--prod requires an explicit --url (refusing to target the implicit localhost worker)"),{refused:u.USAGE};const r=A(e.url,e.logger,e.cwd);if(r===void 0)return{refused:u.USAGE};const{token:a}=E({cwd:e.cwd??process.cwd(),token:e.token,url:r});if(!a)return e.logger.error("admin token required — pass --token, set LUNORA_ADMIN_TOKEN, or add it to .dev.vars (local targets only)"),{refused:u.AUTH};const o=e.adminFetch??T;return{baseUrl:r,fetchImpl:o,token:a}},K=(e,t)=>{e.logger.info(`backup retention would keep the newest ${t.keep.toString()} of ${t.eligible.toString()} under ${t.prefix} and delete ${t.wouldDelete.length.toString()}`);for(const r of t.wouldDelete)e.logger.info(` ${G(r)}`)},Pe=async e=>{const t=M(e);if(p(t))return{code:t.refused};const r=await t.fetchImpl(`${t.baseUrl}${F}`,{headers:{authorization:`Bearer ${t.token}`},method:"GET"});if(!r.ok)return await y(r,e.logger),{code:k(r.status)};const a=await r.json();return a.cron===void 0?(e.logger.info("no scheduled backup is configured (`backupCron`), so retention would delete nothing"),{code:0,preview:a}):a.keep<=0?(e.logger.info(`retention is off for cron "${a.cron}" (\`backupRetain\` unset) — every snapshot under ${a.prefix} is kept`),{code:0,preview:a}):(K(e,a),a.eligible===0&&e.logger.info(`no snapshot under ${a.prefix} was written by this cron, so retention has nothing of its own to remove`),{code:0,preview:a})},Be=(e,t)=>{e.logger.success(`deleted ${t.deleted.length.toString()} backup(s)`);for(const r of t.deleted)e.logger.info(` ${G(r)}`);t.failed.length>0&&e.logger.warn(`${t.failed.length.toString()} could not be removed — the snapshot may be gone with its manifest left behind; run again to retry`),t.ignored>0&&e.logger.info(`${t.ignored.toString()} were already gone by the time the delete ran`),t.remaining>0&&e.logger.info(`${t.remaining.toString()} more are past the window — run \`lunora backup prune\` again`)},De=async e=>{const t=M(e);if(p(t))return{code:t.refused};const r=await t.fetchImpl(`${t.baseUrl}${F}`,{headers:{authorization:`Bearer ${t.token}`},method:"GET"});if(!r.ok)return await y(r,e.logger),{code:k(r.status)};const a=await r.json();if(a.keep<=0||a.cron===void 0)return e.logger.error("backup prune needs a retention window: set `backupRetain` (and `backupCron`) on the worker. Without one there is nothing past the window to remove."),{code:u.USAGE,preview:a};if(a.wouldDelete.length===0)return e.logger.info(`nothing to prune — ${a.eligible.toString()} snapshot(s) under ${a.prefix} and retention keeps the newest ${a.keep.toString()}`),{code:0,preview:a};if(K(e,a),e.yes!==!0){const n=e.confirm??(J()?W:void 0);if(n===void 0)return e.logger.error("refusing to delete backups without confirmation — re-run with --yes (there is no undo for an object-store delete)"),{code:u.USAGE,preview:a};if(!await n(`delete ${a.wouldDelete.length.toString()} backup(s)? This cannot be undone. [y/N] `))return e.logger.info("nothing was deleted"),{code:0,preview:a}}const o=await t.fetchImpl(`${t.baseUrl}${ve}`,{body:JSON.stringify({confirm:a.wouldDelete}),headers:{authorization:`Bearer ${t.token}`,"content-type":"application/json"},method:"POST"});if(!o.ok)return await y(o,e.logger),{code:k(o.status),preview:a};const i=await o.json();return Be(e,i),{code:i.failed.length>0?1:0,deleted:i.deleted,preview:a}},Ge=async(e,t)=>{try{if(e.subcommand==="pitr")return await Re(e);if(e.subcommand==="retention")return await Pe(e);if(e.subcommand==="prune")return await De(e);const r=Oe(e,t);return p(r)?{code:r.refused}:e.subcommand==="create"?await Te(e,r):e.subcommand==="list"?await Ie(e,r):await Ue(e,r)}catch(r){return e.logger.error(r instanceof Error?r.message:String(r)),{code:1}}},je=async e=>{const t=e.cwd??process.cwd(),r=await Ge(e,t);return{...r,data:{subcommand:e.subcommand,...r.entries===void 0?{}:{entries:r.entries},...r.entry===void 0?{}:{entry:r.entry},...r.restored===void 0?{}:{restored:r.restored},...r.preview===void 0?{}:{preview:r.preview},...r.deleted===void 0?{}:{deleted:r.deleted},...r.body===void 0?{}:{result:r.body}}}},Ce=e=>e==="create"||e==="list"||e==="pitr"||e==="prune"||e==="restore"||e==="retention",nr=ee(async({argument:e,cwd:t,format:r,logger:a,options:o})=>{const i=e[0];if(!Ce(i)){const f=`backup: unknown subcommand "${i??""}" — expected create | list | restore | retention | prune | pitr`;return a.error(f),{code:u.USAGE,error:f}}const n=await je({at:o.at,bookmark:o.bookmark,bucket:o.bucket,cwd:t,dir:o.dir,format:r,logger:a,prefix:o.prefix,prod:o.prod===!0,restart:o.restart===!0,restore:o.restore===!0,shard:o.shard,subcommand:i,tables:o.tables,target:e[1],token:o.token,url:re({cwd:t,prod:o.prod===!0,url:o.url}),verify:o.verify===!0,yes:o.yes===!0});return{code:n.code,data:n.data,error:n.error}});export{nr as execute,je as runBackupCommand};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{V as te}from"../packem_shared/cli-
|
|
1
|
+
import{V as te}from"../packem_shared/cli-BGHRPHBJ.mjs";import{d as re}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as f}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{existsSync as m,readFileSync as F,statSync as ne,watch as oe}from"node:fs";import{homedir as C}from"node:os";import{findWranglerFile as le}from"@lunora/config/cloudflare";import{join as i,relative as ae}from"@visulima/path";import{stringify as ce}from"smol-toml";import{h as se,u as ie,i as de,r as ue}from"../packem_shared/mcp-config-file-GR1Y_CVw.mjs";import{detectPackageManager as me,execArgsFor as fe}from"@lunora/config/cli";import{resolve as $,join as pe,dirname as L}from"node:path";import{readLiveDevServerState as b,DEV_STATE_DIR as ge}from"@lunora/config";import{resolveAdminToken as he}from"@lunora/config/studio-host";import{connectLocalStdio as ve,connectFunctionStdio as ye}from"@lunora/mcp";import{i as we}from"../packem_shared/path-containment-CgxYZggb.mjs";const g=e=>({args:[...e.args],command:e.command,...e.env===void 0?{}:{env:e.env}}),j=e=>e.transport==="http"?{type:"http",url:e.url}:g(e),be=e=>e.transport==="http"?{httpUrl:e.url}:g(e),Se=e=>e.transport==="http"?{serverUrl:e.url}:g(e),ke=e=>({args:["-y","mcp-remote",e],command:"npx"}),Ee=e=>e.transport==="http"?ke(e.url):g(e),$e=e=>e.transport==="http"?{url:e.url}:g(e),je=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}},Oe=e=>e.transport==="http"?{disabled:!1,type:"streamableHttp",url:e.url}:{disabled:!1,...g(e)},Ce=({home:e,platform:t})=>{if(t==="darwin")return{global:i(e,"Library","Application Support","Claude","claude_desktop_config.json")};if(t==="win32"){const r=process.env.APPDATA;return r===void 0||r===""?{}:{global:i(r,"Claude","claude_desktop_config.json")}}return t==="linux"?{global:i(e,".config","Claude","claude_desktop_config.json")}:{}},_e=(e,t)=>ce({mcp_servers:{[e]:t.transport==="http"?{url:t.url}:{args:[...t.args],command:t.command,...t.env===void 0?{}:{env:t.env}}}}),v=[{buildEntry:j,paths:({projectRoot:e})=>({project:i(e,".mcp.json")}),format:"json",id:"claude-code",key:"mcpServers",label:"Claude Code"},{buildEntry:j,paths:({home:e,projectRoot:t})=>({global:i(e,".cursor","mcp.json"),project:i(t,".cursor","mcp.json")}),format:"json",id:"cursor",key:"mcpServers",label:"Cursor"},{buildEntry:j,paths:({projectRoot:e})=>({project:i(e,".vscode","mcp.json")}),format:"json",id:"vscode",key:"servers",label:"VS Code (GitHub Copilot)"},{buildEntry:be,paths:({home:e,projectRoot:t})=>({global:i(e,".gemini","settings.json"),project:i(t,".gemini","settings.json")}),format:"json",id:"gemini",key:"mcpServers",label:"Gemini CLI"},{buildEntry:Ee,paths:Ce,format:"json",id:"claude-desktop",key:"mcpServers",label:"Claude Desktop"},{buildEntry:Se,paths:({home:e})=>({global:i(e,".codeium","windsurf","mcp_config.json")}),format:"json",id:"windsurf",key:"mcpServers",label:"Windsurf"},{buildEntry:je,paths:({home:e,projectRoot:t})=>({global:i(e,".config","opencode","opencode.json"),project:i(t,"opencode.json")}),format:"json",id:"opencode",key:"mcp",label:"OpenCode"},{buildEntry:Oe,paths:({home:e})=>({global:i(e,".cline","mcp.json")}),format:"json",id:"cline",key:"mcpServers",label:"Cline"},{buildEntry:$e,paths:({home:e})=>({global:i(e,".config","zed","settings.json")}),format:"json",id:"zed",key:"context_servers",label:"Zed"},{paths:({home:e,projectRoot:t})=>({global:i(e,".codex","config.toml"),project:i(t,".codex","config.toml")}),format:"manual",id:"codex",label:"Codex CLI",renderSnippet:_e}],P=v.map(e=>e.id),Me=e=>v.find(t=>t.id===e.toLowerCase()),Ue=["global","project"],p=(e,t)=>{const r=ae(t,e);return r.length>0&&!r.startsWith("..")?r:e},xe=(e,t)=>{const r=e.paths(t);return Ue.flatMap(n=>{const o=r[n];return o===void 0?[]:[{client:e,path:o,scope:n}]})},Le=(e,t,r,n)=>{const o=e.paths(t),l=r==="global"?"project":"global",a=o[r]??(n?void 0:o[l]);if(a!==void 0)return{client:e,path:a,scope:o[r]===void 0?l:r}},Ne=e=>v.filter(t=>Object.values(t.paths(e)).some(r=>m(r))),I=(e,t,r,n)=>{if(e.length===0)return t();const o=[];for(const l of e){const a=Me(l);if(a===void 0){r.error(`mcp ${n}: unknown client "${l}". Known clients: ${P.join(", ")}.`);return}o.push(a)}return o},Ae=e=>e.client.format==="json",De="https://lunora.sh/mcp",R="lunora-docs",H="lunora",Te=e=>m(i(e,"lunora"))&&le(e)!==void 0,Fe=e=>{const{args:t,command:r}=fe(e,"lunora",["mcp","serve"]);return{args:t,command:r,transport:"stdio"}},Pe=e=>({transport:"http",url:e}),Ie=e=>({home:e.home,platform:e.platform,projectRoot:e.cwd}),W=(e,t,r)=>Le(e,Ie(r),r.scope??t.preferredScope,r.scope!==void 0||!t.allowScopeFallback),Re=(e,t,r)=>{const n=W(e,t,r);if(n===void 0){r.logger.warn(`${e.label}: has no ${r.scope??t.preferredScope}-scoped config — skipped "${t.name}".`);return}const{path:o}=n,l=e.buildEntry(t.spec);if(r.print===!0){const d=p(o,r.cwd);return r.force!==!0&&se({key:e.key,name:t.name,path:o})?(r.logger.info(`${e.label}: "${t.name}" already configured in ${d} — a real install would skip it (re-run with --force to replace).`),{action:"printed",path:o}):(r.logger.info(`${e.label} → ${d}
|
|
2
2
|
${JSON.stringify({[e.key]:{[t.name]:l}},void 0,4)}`),{action:"printed",path:o})}const a=ie({entry:l,force:r.force===!0,key:e.key,name:t.name,path:o}),c=p(o,r.cwd);return a.action==="invalid"?r.logger.error(`${e.label}: ${c} is not valid JSON (${a.error??"unknown error"}) — left untouched.`):a.action==="skipped"?r.logger.info(`${e.label}: "${t.name}" already configured in ${c} — re-run with --force to replace it.`):r.logger.success(`${e.label}: ${a.action==="created"?"created":"updated"} ${c} with "${t.name}".`),{action:a.action,path:o}},He=(e,t,r)=>{const n=W(e,t,r)?.path;if(n===void 0){r.logger.warn(`${e.label}: no known config location on ${r.platform} — skipped.`);return}return r.logger.info(`${e.label}: add this to ${p(n,r.cwd)}
|
|
3
3
|
|
|
4
4
|
${e.renderSnippet(t.name,t.spec)}`),{action:"printed",path:n}},We=e=>{const t=[];return e.localOnly!==!0&&t.push({allowScopeFallback:!0,name:R,preferredScope:"global",spec:Pe(e.docsUrl??De)}),e.docsOnly!==!0&&Te(e.cwd)&&t.push({allowScopeFallback:!1,name:H,preferredScope:"project",spec:Fe(me(e.cwd))}),t},Ge=e=>{const t=e.home??C(),r=e.platform??process.platform;e.logger.info("Supported MCP clients:");for(const n of v){const o=n.paths({home:t,platform:r,projectRoot:e.cwd}),l=a=>{const c=o[a];return c===void 0?"—":p(c,e.cwd)};e.logger.info(` ${n.id.padEnd(15)} ${n.label.padEnd(24)} project: ${l("project").padEnd(30)} global: ${l("global")}`)}return{code:0,written:[]}},Je=e=>e.docsOnly===!0&&e.localOnly===!0?"`--docs-only` and `--local-only` are mutually exclusive — pass at most one.":"`--local-only` was set but this directory is not a Lunora project (no `lunora/` directory and wrangler config).",Ve=(e,t,r)=>{const n=[];for(const o of e)for(const l of t){const a=o.format==="json"?Re(o,l,r):He(o,l,r);a!==void 0&&n.push({action:a.action,client:o.id,path:a.path,server:l.name})}return n},ze=e=>{const t=e.home??C(),r=e.platform??process.platform,n=I(e.clients,()=>Ne({home:t,platform:r,projectRoot:e.cwd}),e.logger,"install");if(n===void 0)return{code:f.USAGE,written:[]};if(n.length===0)return e.logger.error(`mcp install: no MCP client config found — name one explicitly, e.g. \`lunora mcp install claude-code\`. Known clients: ${P.join(", ")}.`),{code:f.NOT_FOUND,written:[]};e.clients.length===0&&e.logger.info(`Detected ${String(n.length)} configured client(s): ${n.map(a=>a.label).join(", ")}.`);const o=We(e);if(o.length===0)return e.logger.error(`mcp install: nothing to install — ${Je(e)}`),{code:f.USAGE,written:[]};const l=Ve(n,o,{...e,home:t,platform:r});return l.some(a=>a.action==="created"||a.action==="updated")&&e.logger.info("Restart your editor (or reload its MCP servers) to pick up the change."),{code:l.some(a=>a.action==="invalid")?1:0,written:l}},G=100,J=1e3,N=e=>({content:[{text:JSON.stringify(e,void 0,2),type:"text"}]}),Ke=e=>({content:[{text:e,type:"text"}]}),qe=e=>{const t=typeof e=="string"?Number(e):e;return typeof t!="number"||!Number.isFinite(t)?G:Math.min(J,Math.max(1,Math.floor(t)))},A=(e,t)=>{if(t===void 0)return;const r=$(e,ge);return we(r,$(e,t))?$(e,t):void 0},Be=(e,t)=>{const r=F(e,"utf8").split(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as Ue}from"../packem_shared/command-CB4JM-Jl.mjs";import{r as De}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{exitCodeForStatus as j,EXIT_CODE as u}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{writeFileSync as ve,mkdirSync as Re,renameSync as Me,rmSync as je}from"node:fs";import{findWranglerFile as J,readWranglerJsonc as W,applyModify as Le}from"@lunora/config/cloudflare";import{c as L}from"../packem_shared/cap-error-body-YBKO32BF.mjs";import{r as H,n as X,c as Pe}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{c as Ge,b as Fe}from"../packem_shared/variables-CPYyJtsL.mjs";import{b as xe}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{i as qe}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as be}from"../packem_shared/createRecordingSpawner-01ZngIwK.mjs";import{execArgsFor as $e,detectPackageManager as Ae}from"@lunora/config/cli";import{a as O}from"../packem_shared/cli-CDNZwrAT.mjs";import{resolve as Be,dirname as Je}from"node:path";const We="https://api.cloudflare.com/client/v4/accounts",Q=64,Z={byok:"https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/",unifiedBilling:"https://developers.cloudflare.com/ai-gateway/features/unified-billing/"},$=(e,t,r)=>(e.error(r),{code:t,error:r}),ee=async(e,t,r,o={})=>{const n=await e(t,{...o,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}}),a=await n.text();let s;try{s=JSON.parse(a)}catch{}return{body:s,ok:n.ok&&s?.success!==!1,status:n.status,text:a}},te=e=>{const t=(e.body?.errors??[]).map(r=>r.message).filter(r=>typeof r=="string");return`${String(e.status)}: ${L(t.length>0?t.join("; "):e.text)}`},He=(e,t,r)=>{let o=e;const n=[];for(const[a,s]of Object.entries(r))t?.[a]!==s&&(o=Le(o,["vars",a],s),n.push(a));return{changed:n,text:o}},Ye=(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 — ${Z.unifiedBilling}`),e.info(` - a key stored on the gateway (Mistral, Perplexity, OpenRouter, …): ${Z.byok}`),e.info(" Then redeploy (`lunora deploy`) so the Worker picks up the new vars.")},Ke=async(e,t,r,o,n,a)=>{const s=`${We}/${encodeURIComponent(r)}/ai-gateway/gateways`,i=await ee(t,`${s}/${encodeURIComponent(n)}`,o);if(i.ok){const d=i.body?.result?.collect_logs;return{action:"existing",collectLogs:typeof d=="boolean"?d:a}}if(i.status===401||i.status===403)return $(e,j(i.status),`ai-gateway: could not read AI Gateway "${n}" (${te(i)})`);const c=await ee(t,s,o,{body:JSON.stringify({cache_invalidate_on_update:!0,cache_ttl:0,collect_logs:a,id:n,rate_limiting_interval:0,rate_limiting_limit:0}),method:"POST"});return c.ok?{action:"created",collectLogs:a}:$(e,j(c.status),`ai-gateway: could not create AI Gateway "${n}" (${te(c)})`)},Ve=e=>{const{logger:t}=e,r=J(e.cwd);if(r===void 0)return $(t,u.NOT_FOUND,"ai-gateway: no wrangler.jsonc found — run `lunora init` (or `lunora dev`) first.");const{parsed:o,text:n}=W(r);if(o===void 0)return $(t,u.USAGE,`ai-gateway: could not parse ${r} as JSONC.`);const a=X(e.id)??X(o.name);return a===void 0?$(t,u.USAGE,"ai-gateway: no gateway id — pass --id <id>, or set `name` in wrangler.jsonc."):a.length>Q?$(t,u.USAGE,`ai-gateway: gateway id "${a}" is longer than ${String(Q)} characters — pass a shorter --id.`):{gatewayId:a,parsed:o,text:n,wranglerPath:r}},P=e=>e?"on":"off",ze=(e,t)=>(e.info(`ai-gateway (dry run): would create or reuse AI Gateway "${t.gatewayId}" (log collection ${P(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}),Xe=async e=>{const{logger:t}=e,r=Ve(e);if("code"in r)return r;const{gatewayId:o,parsed:n,text:a,wranglerPath:s}=r,i=e.logs!==!1,{accountId:c,token:d}=H(e.environment??process.env,n.account_id),f={[Fe]:o,...c===void 0?{}:{[Ge]:c}},l=He(a,n.vars,f);if(e.dryRun===!0)return ze(t,{accountId:c,action:"planned",collectLogs:i,dryRun:!0,gatewayId:o,varsWritten:l.changed,wranglerPath:s});if(d===void 0||c===void 0){const g=[d===void 0?"CLOUDFLARE_API_TOKEN (an API token with AI Gateway Write)":void 0,c===void 0?"CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc)":void 0].filter(y=>y!==void 0);return $(t,u.AUTH,`ai-gateway: missing ${g.join(" and ")}.`)}const h=await Ke(t,e.fetch??globalThis.fetch.bind(globalThis),c,d,o,i);return"code"in h?h:(h.action==="created"?t.success(`ai-gateway: created AI Gateway "${o}" (log collection ${P(h.collectLogs)}).`):t.info(`ai-gateway: AI Gateway "${o}" already exists — reusing it (log collection ${P(h.collectLogs)}; change it in the dashboard).`),l.changed.length>0?(ve(s,l.text,"utf8"),t.success(`ai-gateway: wrote ${l.changed.join(", ")} into ${s} vars.`)):t.info(`ai-gateway: ${s} vars are already up to date.`),Ye(t,o),{code:0,data:{accountId:c,action:h.action,collectLogs:h.collectLogs,dryRun:!1,gatewayId:o,varsWritten:l.changed,wranglerPath:s}})},Qe=15e3,Ze="https://api.cloudflare.com/client/v4/graphql",E={analytics:"Account Analytics Read",notificationsRead:"Notifications Read",notificationsWrite:"Notifications Write"},et={auth:u.AUTH,failure:u.FAILURE,"not-found":u.NOT_FOUND,permission:u.PERMISSION,"rate-limited":u.RATE_LIMITED,rejected:u.USAGE,unavailable:u.UNAVAILABLE};class _ extends Error{kind;status;constructor(t,r,o){super(t),this.name="CloudflareApiError",this.kind=r,this.status=o}}const tt=e=>e===401?"auth":e===403?"permission":e===404?"not-found":e===429?"rate-limited":e>=500?"unavailable":"rejected",Se=e=>{try{const r=JSON.parse(e);if(Array.isArray(r.errors)){const o=r.errors.map(n=>typeof n=="object"&&n!==null&&"message"in n?String(n.message):void 0).filter(n=>n!==void 0&&n.length>0);if(o.length>0)return o}}catch{}const t=e.trim().slice(0,200);return t.length>0?[t]:[]},rt=9106,ot=(e,t)=>{try{const r=JSON.parse(e);return Array.isArray(r.errors)&&r.errors.some(o=>typeof o=="object"&&o!==null&&"code"in o&&o.code===t)}catch{return!1}},re=(e,t,r,o)=>{const n=t===400&&ot(r,rt)?"auth":tt(t),a=Se(r).join("; ");let s="";return n==="auth"?s=` — check that CLOUDFLARE_API_TOKEN is a valid token; it needs the "${o}" permission on this account`:n==="permission"&&(s=` — the API token needs the "${o}" permission on this account`),new _(`${e} failed (HTTP ${String(t)})${a.length>0?`: ${a}`:""}${s}.`,n,t)},oe=(e,t)=>{const r=t instanceof Error&&(t.name==="TimeoutError"||t.name==="AbortError");let o=t instanceof Error?t.message:String(t);return r&&(o="no answer in time"),new _(`${e} failed: ${o}.`,"unavailable")},nt=10,at=e=>{const{accountId:t,token:r}=e,o=e.timeoutMs??Qe,n=e.fetch??globalThis.fetch.bind(globalThis),a=async(s,i,c,d)=>{let f;try{f=await Pe({accountId:t,apiToken:r,fetch:n,init:{method:i,signal:AbortSignal.timeout(o),...d===void 0?{}:{body:JSON.stringify(d)}},path:c})}catch(l){throw oe(s,l)}if(!f.ok)throw re(s,f.status,f.text,i==="GET"?E.notificationsRead:E.notificationsWrite);return f.body};return{accountId:t,graphql:async(s,i,c={})=>{let d;try{d=await n(Ze,{body:JSON.stringify({query:i,variables:c}),headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},method:"POST",signal:AbortSignal.timeout(o)})}catch(l){throw oe(s,l)}const f=await d.text();if(!d.ok)throw re(s,d.status,f,E.analytics);try{const l=JSON.parse(f);return{data:l.data??void 0,errors:Array.isArray(l.errors)&&l.errors.length>0?Se(f):[]}}catch{throw new _(`${s} failed: the GraphQL API answered with something that is not JSON.`,"unavailable",d.status)}},notifications:async(s,i,c,d)=>(await a(s,i,c,d)).result,notificationsList:async(s,i,c=nt)=>{const d=async f=>{const l=await a(s,"GET",`${i}${i.includes("?")?"&":"?"}page=${String(f)}`),h=Array.isArray(l.result)?l.result:[],g=l.result_info;return typeof g?.total_pages=="number"&&f<g.total_pages&&f<c?[...h,...await d(f+1)]:h};return d(1)}}},A="billing_usage_alert",ne={"do-duration":"worker_durable_objects_duration","do-requests":"worker_durable_objects_requests","do-rows-read":"worker_durable_objects_storage_reads","do-rows-written":"worker_durable_objects_storage_writes","workers-requests":"worker_requests"},st=6,C=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)?e:void 0,U=(e,t)=>{const r=t.toLowerCase(),o=Object.keys(e).find(n=>n.toLowerCase()===r);return o===void 0?void 0:e[o]},G=(e,t)=>{for(const r of t){const o=U(e,r);if(typeof o=="string"&&o.length>0)return o}},it=e=>{if(typeof e=="string"&&e.length>0)return{id:e,source:"available-alerts"};const t=C(e),r=t===void 0?void 0:G(t,["id","value","key","name"]);if(t===void 0||r===void 0)return;const o=G(t,["description","label","display_name","displayName","name"]);return{id:r,source:"available-alerts",...o===void 0||o===r?{}:{label:o}}},ct=["AvailableValues","available_values","values","options","enum","choices"],_e=e=>e.map(t=>it(t)).filter(t=>t!==void 0),dt=e=>G(e,["key","name","filter","id"])!=="product"?[]:ct.flatMap(t=>{const r=U(e,t);return Array.isArray(r)?_e(r):[]}),F=(e,t,r)=>{if(t>st)return;if(Array.isArray(e)){for(const n of e)F(n,t+1,r);return}const o=C(e);if(o!==void 0){r.push(...dt(o));for(const[n,a]of Object.entries(o))n.toLowerCase()==="product"&&Array.isArray(a)?r.push(..._e(a)):typeof a=="object"&&a!==null&&F(a,t+1,r)}},lt=e=>{const t=C(e);if(t!==void 0){for(const r of Object.values(t))if(Array.isArray(r)){const o=r.map(n=>C(n)).find(n=>n!==void 0&&U(n,"type")===A);if(o!==void 0)return o}}},ut=e=>e.filter(t=>t.alert_type===A&&Array.isArray(t.filters?.product)).flatMap(t=>(t.filters?.product).filter(r=>typeof r=="string"&&r.length>0)).map(t=>({id:t,source:"existing-policy"})),ft=(e,t,r)=>{const o=lt(e),n=[];o!==void 0&&F(U(o,"filter_options"),0,n);const a=new Map;for(const s of[...n,...ut(r)])a.has(s.id)||a.set(s.id,s);return{eligible:t?o!==void 0:void 0,products:[...a.values()]}},mt=/[^a-z\d]+/u,ht=e=>`${e.id} ${e.label??""}`.toLowerCase().split(mt).filter(t=>t.length>0),gt={"d1-rows-read":{all:[["d1"],["read"]]},"d1-rows-written":{all:[["d1"],["writ"]]},"do-duration":{all:[["durable"],["duration"]]},"do-requests":{all:[["durable"],["request"]]},"do-rows-read":{all:[["durable"],["read"]],none:["request"]},"do-rows-written":{all:[["durable"],["writ"]]},"workers-cpu":{all:[["worker"],["cpu"]],none:["durable"]},"workers-requests":{all:[["worker"],["request"]],none:["durable","cpu","kv","d1"]}},pt=(e,t)=>{const r=ht(t),o=s=>r.some(i=>i.startsWith(s)),{all:n,none:a=[]}=gt[e];return n.every(s=>s.some(i=>o(i)))&&!a.some(s=>o(s))},Ee=(e,t)=>{const r=ne[e],o=r===void 0?void 0:t.find(s=>s.id===r);if(o!==void 0)return{product:o,status:"matched"};const n=new Set(Object.values(ne)),a=t.filter(s=>!n.has(s.id)&&pt(e,s));return a.length===1&&a[0]!==void 0?{product:a[0],status:"matched"}:a.length>1?{candidates:a.map(s=>s.id),status:"ambiguous"}:r===void 0?{status:"no-product"}:{product:{id:r,source:"documented"},status:"matched"}},ae="workersInvocationsAdaptive",yt="durableObjectsInvocationsAdaptiveGroups",M="durableObjectsPeriodicGroups",se="d1AnalyticsAdaptiveGroups",S=[{candidates:[{field:"requests",unit:"count"}],dataset:ae,filter:"datetime",id:"workers-requests",label:"Workers requests"},{candidates:[{field:"cpuTimeUs",unit:"microseconds"}],dataset:ae,filter:"datetime",id:"workers-cpu",label:"Workers CPU time"},{candidates:[{field:"requests",unit:"count"}],dataset:yt,filter:"date",id:"do-requests",label:"Durable Objects requests"},{candidates:[{field:"activeTime",unit:"unknown"}],dataset:M,filter:"date",id:"do-duration",label:"Durable Objects duration"},{candidates:[{field:"rowsRead",unit:"count"}],dataset:M,filter:"date",id:"do-rows-read",label:"Durable Objects rows read"},{candidates:[{field:"rowsWritten",unit:"count"}],dataset:M,filter:"date",id:"do-rows-written",label:"Durable Objects rows written"},{candidates:[{field:"rowsRead",unit:"count"}],dataset:se,filter:"datetime-hour",id:"d1-rows-read",label:"D1 rows read"},{candidates:[{field:"rowsWritten",unit:"count"}],dataset:se,filter:"datetime-hour",id:"d1-rows-written",label:"D1 rows written"}],ie=e=>String(e).padStart(2,"0"),wt=e=>{const t=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth()-1,1)),r=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),0)),o=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),1)),n=a=>`${String(a.getUTCFullYear())}-${ie(a.getUTCMonth()+1)}-${ie(a.getUTCDate())}`;return{endDate:n(r),nextStartDate:n(o),startDate:n(t)}},vt=(e,t)=>{const r=JSON.stringify(`${t.startDate}T00:00:00Z`),o=JSON.stringify(`${t.nextStartDate}T00:00:00Z`);switch(e){case"date":return`{ date_geq: ${JSON.stringify(t.startDate)}, date_lt: ${JSON.stringify(t.nextStartDate)} }`;case"datetime":return`{ datetime_geq: ${r}, datetime_lt: ${o} }`;default:return`{ AND: [{ datetimeHour_geq: ${r}, datetimeHour_lt: ${o} }] }`}},bt=e=>`query LunoraTypeFields { __type(name: ${JSON.stringify(e)}) { fields { name type { name ofType { name ofType { name ofType { name } } } } } } }`,$t=e=>{let t=e;for(;t!=null;){if(typeof t.name=="string"&&t.name.length>0)return t.name;t=t.ofType}},At=e=>{const t=new Map;return async r=>{const o=t.get(r);if(o!==void 0)return o;const n=e.graphql(`Reading the analytics schema (${r})`,bt(r)).then(a=>{if(a.errors.length>0)throw new _(`Reading the analytics schema failed: ${a.errors.join("; ")}.`,"rejected");const i=a.data?.__type?.fields??[];return new Map(i.map(c=>[c.name,$t(c.type)]))});return t.set(r,n),n}},Te=async(e,t,r)=>{const o=await e(t),[n,...a]=r;if(n===void 0)return o;const s=o.get(n);if(s===void 0)throw new _(`the analytics schema has no \`${n}\` on \`${t}\``,"rejected");return Te(e,s,a)},St=e=>e instanceof Error?e.message:String(e),_t=async(e,t,r,o,n)=>{const{id:a,label:s}=o;try{const i=await Te(t,r,["viewer","accounts",o.dataset,"sum"]),c=o.candidates.find(y=>i.has(y.field));if(c===void 0){const y=o.candidates.map(m=>`\`${m.field}\``).join(", ");return{id:a,label:s,reason:`\`${o.dataset}.sum\` exposes no ${y} field`,status:"unavailable"}}const d=`query LunoraUsage($accountTag: string) { viewer { accounts(filter: { accountTag: $accountTag }) { ${o.dataset}(limit: 1, filter: ${vt(o.filter,n)}) { sum { ${c.field} } } } } }`,f=await e.graphql(`Reading ${s}`,d,{accountTag:e.accountId});if(f.errors.length>0)return{id:a,label:s,reason:f.errors.join("; "),status:"unavailable"};const l=f.data?.viewer?.accounts?.[0]?.[o.dataset],h=Array.isArray(l)?l[0]:void 0,g=h===void 0?0:h.sum?.[c.field];return typeof g!="number"||!Number.isFinite(g)?{id:a,label:s,reason:`the response carried no number for \`${c.field}\``,status:"unavailable"}:{field:`${o.dataset}.sum.${c.field}`,id:a,label:s,status:"ok",unit:c.unit,value:g}}catch(i){return{id:a,label:s,reason:St(i),status:"unavailable"}}},Et=async(e,t)=>{const r=await e.graphql("Reading the analytics schema","query LunoraQueryRoot { __schema { queryType { name } } }"),o=r.data?.__schema?.queryType?.name;if(r.errors.length>0||o===void 0){const a=r.errors.length>0?r.errors.join("; "):"the schema root could not be read";return S.map(({id:s,label:i})=>({id:s,label:i,reason:a,status:"unavailable"}))}const n=At(e);return Promise.all(S.map(async a=>_t(e,n,o,a,t)))},D="Lunora usage: ",Tt=3,Y="Cloudflare does not document the unit of a usage alert's limit; limits are sent as plain request counts. Check the stored limit in the dashboard.",It={"do-requests":1e6,"workers-requests":1e7},kt=100,Ot=e=>{const t=Math.ceil(e);if(t<=0)return 0;const r=10**Math.max(0,Math.floor(Math.log10(t))-1);return Math.ceil(t/r)*r},Ct=(e,t,r)=>{const o=Ot(e*t);return o>r?{basis:"history",threshold:o}:{basis:"floor",threshold:r}},x=e=>{const t=e.filters?.product;return Array.isArray(t)?t.filter(r=>typeof r=="string"):[]},Nt=e=>{const t=e.filters?.limit,r=Array.isArray(t)?t[0]:void 0,o=typeof r=="string"?Number(r):Number.NaN;return Number.isFinite(o)?o:void 0},K=e=>{const t={};for(const[r,o]of Object.entries(e?.mechanisms??{}))Array.isArray(o)&&(t[r]=o.filter(n=>typeof n=="object"&&n!==null&&"id"in n&&typeof n.id=="string"));return t},Ut=(e,t)=>{const r={};for(const[o,n]of[...Object.entries(e),...Object.entries(t)]){const a=new Set((r[o]??[]).map(s=>s.id));r[o]=[...r[o]??[],...n.filter(s=>!a.has(s.id))]}return Object.fromEntries(Object.entries(r).filter(([,o])=>o.length>0))},N=e=>Object.entries(e).flatMap(([t,r])=>r.map(o=>`${t}:${o.id}`)),Dt=(e,t)=>JSON.stringify(N(e).toSorted((r,o)=>r.localeCompare(o)))===JSON.stringify(N(t).toSorted((r,o)=>r.localeCompare(o))),Rt=(e,t)=>e.enabled===!0&&e.name===t.name&&JSON.stringify(e.filters?.limit)===JSON.stringify(t.filters.limit)&&JSON.stringify(x(e))===JSON.stringify(t.filters.product)&&Dt(K(e),t.mechanisms),Mt=(e,t)=>{const r={};return e.length>0&&(r.email=e.map(o=>({id:o}))),t.length>0&&(r.webhooks=t.map(o=>({id:o}))),r},jt=(e,t,r)=>{const o=r.thresholds?.[e],n=r.usage.find(c=>c.id===e),a=n?.status==="ok"?n.value:void 0;if(o!==void 0)return{basis:"explicit",lastMonth:a,threshold:o};const s=It[e];return s===void 0?{kind:"unit-unverified",reason:`the unit Cloudflare meters "${t.id}" in is not documented and cannot be matched to the usage reading; set one yourself with --threshold ${e}=<n>`}:a!==void 0?{...Ct(a,r.multiplier,s),lastMonth:a}:r.allowFloor===!0?{basis:"floor-usage-unavailable",lastMonth:void 0,threshold:s}:{kind:"usage-unavailable",reason:`last month's usage could not be read (${n?.status==="unavailable"?n.reason:"no reading"}), and an alert at the floor could fire every month on an account that already exceeds it; pass --threshold ${e}=<n>, or --allow-floor`}},Lt=(e,t)=>{const r=Nt(e);if(r===void 0||t===void 0||r<=0||t<=0)return;const o=Math.max(r/t,t/r);return o>=kt?`its limit ${String(r)} differs from the ${String(t)} this would plan by ${String(Math.round(o))}× — one of the two may be in the wrong unit`:void 0},q=e=>`${D}${e}`,Pt=(e,t)=>e===void 0?"create":Rt(e,t)?"unchanged":"update",Gt=(e,t)=>{switch(e){case"explicit":return"set with --threshold";case"floor-usage-unavailable":return"the included monthly allowance, applied with --allow-floor because usage could not be read";case"history":return`${String(t)}× last month`;default:return"the included monthly allowance (floor)"}},Ft=(e,t,r)=>{const{id:o,label:n}=e,a=Ee(o,t.products);if(a.status==="no-product")return{kind:"no-product",metric:o,reason:"Cloudflare has no usage alert for this product; the account-wide budget alert is its guard"};if(a.status==="ambiguous")return{kind:"ambiguous",metric:o,reason:`several offered products could mean this metric (${a.candidates.join(", ")})`};const{product:s}=a,i=r.find(p=>p.name===q(n)),c=r.find(p=>p.enabled===!0&&p.name?.startsWith(D)!==!0&&x(p).includes(s.id)),d=jt(o,s,t);if(i===void 0&&c!==void 0){const p=Lt(c,"threshold"in d?d.threshold:void 0);return{kind:"covered",metric:o,reason:`already alerted by the existing policy "${c.name??c.id??"?"}"`,...p===void 0?{}:{warning:p}}}if(!("threshold"in d))return{...d,metric:o};const f=Mt(t.emails,t.webhooks),l=K(i),h=i===void 0||t.replaceRecipients===!0?f:Ut(l,f),g=new Set(N(l)),y=new Set(N(h)),m={alert_type:A,description:`Managed by \`lunora cloudflare alerts setup\`: ${Gt(d.basis,t.multiplier)}.`,enabled:!0,filters:{limit:[String(d.threshold)],product:[s.id]},mechanisms:h,name:q(n)},w=i===void 0?[]:x(i);return{action:Pt(i,m),basis:d.basis,body:m,lastMonth:d.lastMonth,metric:o,...i?.id===void 0?{}:{policyId:i.id},...i!==void 0&&JSON.stringify(w)!==JSON.stringify([s.id])?{previousProduct:w}:{},product:s.id,productSource:s.source,recipients:{added:[...y].filter(p=>!g.has(p)),removed:[...g].filter(p=>!y.has(p))},threshold:d.threshold}},xt=e=>{const t=e.policies.filter(n=>n.alert_type===A),r=new Set(S.map(n=>q(n.label))),o={alerts:[],duplicates:[],orphans:t.filter(n=>n.name?.startsWith(D)===!0&&!r.has(n.name)),skipped:[]};for(const n of r)o.duplicates.push(...t.filter(a=>a.name===n).slice(1));for(const n of S){const a=Ft(n,e,t);"action"in a?o.alerts.push(a):o.skipped.push(a)}return o},qt="Cloudflare's API has no endpoint that sends a test notification — not for webhooks and not for email — so `lunora cloudflare alerts test` sends nothing.",Bt="To test a webhook for real, create it in the dashboard (Alerts > Destinations > Webhooks > Create) and finish with Save and Test.",Jt="Email destinations cannot be tested at all: Cloudflare offers no test send for them. Check the address on each policy below instead.",ce=e=>Array.isArray(e)?e:[],de=30,Wt=async(e,t,r,o,n)=>{const a=t.filter(m=>m.alert_type===A&&m.name?.startsWith(D)===!0),s=a.map(m=>K(m)),i=new Set([...s.flatMap(m=>(m.webhooks??[]).map(w=>w.id)),...r]),c=[...new Set(s.flatMap(m=>(m.email??[]).map(w=>w.id)))],d=new Set(a.map(m=>m.id)),f=new Date(n.getTime()-de*24*60*60*1e3).toISOString(),[l,h]=await Promise.all([e.notifications("Listing webhook destinations","GET","/alerting/v3/destinations/webhooks"),a.length===0?Promise.resolve([]):e.notificationsList("Reading notification history",`/alerting/v3/history?per_page=25&since=${encodeURIComponent(f)}`)]),g=ce(l).map(m=>({...m,referenced:m.id!==void 0&&i.has(m.id)})),y=ce(h).filter(m=>m.alert_type===A&&m.policy_id!==void 0&&d.has(m.policy_id));o.warn(qt),a.length===0&&o.info("No `lunora cloudflare alerts setup` policies exist on this account yet — run `lunora cloudflare alerts setup` first.");for(const m of[...i].filter(w=>!g.some(p=>p.id===w)))o.warn(`Webhook destination ${m} is referenced but does not exist on this account.`);for(const m of g.filter(w=>w.referenced))o.info(`webhook "${m.name??m.id??"?"}" (${m.type??"generic"}): last delivered ${m.last_success??"never"}, last failed ${m.last_failure??"never"}`);if(o.info(Bt),c.length>0&&o.info(`${Jt} Addresses on the policies: ${c.join(", ")}.`),y.length===0)o.info(`Cloudflare's notification history shows nothing sent for these policies in the last ${String(de)} days (they only fire once a threshold is crossed).`);else for(const m of y)o.info(`sent ${m.sent??"?"}: "${m.name??"?"}" via ${m.mechanism_type??"?"} to ${m.mechanism??"?"}`);return{emails:c,history:y,testSent:!1,webhooks:g}},R="Budget alert (account-wide USD threshold): Cloudflare documents no API to create or read one, so this cannot be checked. Create it in the dashboard: Manage Account > Billing > Billable Usage > Create budget alert.",Ht="Alerts > Overview > Create an Alert, alert type Usage Based Billing",b=(e,t,r,o)=>(e.error(r),{code:t,error:r,...o===void 0?{}:{data:o}}),V=(e,t,r)=>{if(t instanceof _)return b(e,et[t.kind],t.message,r);throw t},z=(e,t)=>({accountId:e.accountId,budgetAlert:{checkable:!1,dashboardPath:"Manage Account > Billing > Billable Usage"},limitUnitCaveat:Y,...t===void 0?{}:{eligible:t.discovery.eligible,period:t.period,products:t.discovery.products}}),I=e=>e.toLocaleString("en-US",{maximumFractionDigits:0}),Ie=(e,t)=>{t===!1&&e.warn("This account is not eligible for Usage Based Billing notifications (they are offered to Pay-as-you-go accounts; most Enterprise contracts are not supported).")},ke=e=>({code:u.SUCCESS,data:e}),Yt=/^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/u,Oe=new Set(S.map(e=>e.id)),Kt=e=>Oe.has(e),Vt=e=>{const t={};for(const r of e.flatMap(o=>o.split(","))){const[o="",n=""]=r.split("=").map(s=>s.trim()),a=Number(n);if(!Kt(o))return{error:`--threshold "${r}": unknown metric "${o}" — one of ${[...Oe].join(", ")}.`};if(!Number.isFinite(a)||a<=0)return{error:`--threshold "${r}": the limit must be a positive number.`};t[o]=Math.ceil(a)}return t},zt=e=>{const t=(e.emails??[]).flatMap(s=>s.split(",")).map(s=>s.trim()).filter(s=>s.length>0),r=(e.webhooks??[]).map(s=>s.trim()).filter(s=>s.length>0),o=e.multiplier===void 0?Tt:Number(e.multiplier),n=Vt(e.thresholds??[]);if(t.length===0&&r.length===0)return{error:"alerts setup needs somewhere to send the alerts: pass --email <address> and/or --webhook <destination-id>."};const a=t.find(s=>!Yt.test(s));return a!==void 0?{error:`--email "${a}" is not an email address.`}:!Number.isFinite(o)||o<1?{error:`--multiplier must be a number of at least 1 (got "${e.multiplier??""}").`}:"error"in n?n:{allowFloor:e.allowFloor===!0,emails:t,multiplier:o,replaceRecipients:e.replaceRecipients===!0,thresholds:n,webhooks:r}},Xt={explicit:()=>"set with --threshold",floor:e=>`floor: included monthly allowance; last month ${I(e.lastMonth??0)}`,"floor-usage-unavailable":()=>"floor: usage could not be read, applied with --allow-floor",history:e=>`last month ${I(e.lastMonth??0)}`},Qt=e=>{const t=[...e.added.map(r=>`+${r}`),...e.removed.map(r=>`-${r}`)];return t.length===0?"recipients unchanged":`recipients ${t.join(" ")}`},Zt=e=>{const t=e.productSource==="documented"?"documented id":"offered by the account",r=e.previousProduct===void 0?"":`, product was ${e.previousProduct.join(", ")||"none"}`;return`${e.action.padEnd(9)} ${e.body.name} — product ${e.product} (${t}${r}), limit ${I(e.threshold)} (${Xt[e.basis](e)}); ${Qt(e.recipients)}`},B=e=>S.find(t=>t.id===e)?.label??e,er=(e,t,r,o)=>{o.info(`Usage alerts at ${String(t)}× last month (${r.startDate} to ${r.endDate}), never below the included allowance:`);for(const n of e.alerts)o.info(` ${Zt(n)}`);for(const n of e.skipped)o.info(` skipped ${B(n.metric)} — ${n.reason}`),n.warning!==void 0&&o.warn(`${B(n.metric)}: the existing alert ${n.warning}.`);for(const n of e.orphans)o.warn(`"${n.name??"?"}" (${n.id??"?"}) is named like a \`lunora cloudflare alerts\` policy but matches no metric; it was left alone.`);for(const n of e.duplicates)o.warn(`"${n.name??"?"}" exists more than once; ${n.id??"?"} was left alone — delete the extra one in the dashboard.`);e.skipped.some(n=>n.kind==="unit-unverified"||n.kind==="ambiguous")&&o.info(`To set the skipped ones by hand: ${Ht}.`),o.info(Y),o.info(R)},tr=async(e,t)=>{if(e.yes===!0)return;if(e.confirm===void 0&&(!process.stdin.isTTY||e.format==="json"))return b(e.logger,u.USAGE,"alerts setup: refusing to change notification policies without confirmation — re-run with --yes.");const r=t.flatMap(a=>a.recipients.removed),o=r.length===0?"":` This REMOVES recipients: ${[...new Set(r)].join(", ")}.`;if(!await(e.confirm??xe)(`Apply ${String(t.length)} notification policy change(s)?${o}`))return e.logger.info("alerts setup: aborted"),{code:u.CANCELLED,error:"alerts setup: aborted at the confirmation prompt"}},rr=async(e,t,r,o)=>{const a=(await Promise.all(t.map(async s=>e.notifications(`Reading webhook destination ${s}`,"GET",`/alerting/v3/destinations/webhooks/${encodeURIComponent(s)}`).then(()=>{},i=>({error:i,id:s}))))).find(s=>s!==void 0);if(a!==void 0)return a.error instanceof _&&a.error.kind==="not-found"?b(r,u.USAGE,`--webhook ${a.id}: no such webhook destination on account ${e.accountId}.`,o):V(r,a.error,o)},or=async(e,t)=>{const r=t.action==="create"?await e.notifications(`Creating "${t.body.name}"`,"POST","/alerting/v3/policies",t.body):await e.notifications(`Updating "${t.body.name}"`,"PUT",`/alerting/v3/policies/${encodeURIComponent(t.policyId??"")}`,t.body);return typeof r=="object"&&r!==null&&"id"in r&&typeof r.id=="string"?r.id:t.policyId},nr=async(e,t,r)=>await Promise.all(t.map(async({alert:n,id:a})=>{try{const s=await e.notifications(`Reading back "${n.body.name}"`,"GET",`/alerting/v3/policies/${encodeURIComponent(a)}`),i={enabled:s?.enabled,id:a,limit:s?.filters?.limit,name:s?.name,product:s?.filters?.product};return r.info(`Cloudflare stored "${i.name??n.body.name}": product ${JSON.stringify(i.product)}, limit ${JSON.stringify(i.limit)}.`),JSON.stringify(i.limit)!==JSON.stringify(n.body.filters.limit)&&r.warn(`"${n.body.name}": Cloudflare stored limit ${JSON.stringify(i.limit)}, not the ${JSON.stringify(n.body.filters.limit)} sent.`),i}catch(s){return r.warn(`Could not read "${n.body.name}" back: ${s instanceof Error?s.message:String(s)}`),{id:a}}})),ar=async(e,t,r,o)=>{const n=[],a=[];let s;for(const c of t)try{const d=await or(e,c);n.push({action:c.action,metric:c.metric,...d===void 0?{}:{policyId:d}}),d!==void 0&&a.push({alert:c,id:d}),r.success(`${c.action==="create"?"Created":"Updated"} "${c.body.name}".`)}catch(d){s=d;break}const i={...o,applied:n,stored:await nr(e,a,r)};return s===void 0?i:V(r,s,i)},le=(e,t,r)=>{const o=t.skipped.filter(n=>n.kind==="usage-unavailable");return o.length>0?b(r,u.FAILURE,`alerts setup: no alert was set for ${o.map(n=>B(n.metric)).join(", ")} because last month's usage could not be read — pass --threshold <metric>=<n>, or --allow-floor to accept the floor.`,e):{code:u.SUCCESS,data:e}},sr=async(e,t,r,o)=>{const{logger:n}=r,a=z(e,t),s=await rr(e,o.webhooks,n,a);if(s!==void 0)return s;if(t.discovery.eligible===!1)return Ie(n,!1),n.info(R),b(n,u.PERMISSION,"alerts setup: the account cannot create Usage Based Billing notifications.",a);const i=xt({...o,policies:t.policies,products:t.discovery.products,usage:t.usage});a.plan=i,er(i,o.multiplier,t.period,n);const c=i.alerts.filter(l=>l.action!=="unchanged");if(r.dryRun===!0||c.length===0)return n.info(r.dryRun===!0?"Dry run: nothing was changed.":"Every alert is already up to date."),le(a,i,n);const d=await tr(r,c);if(d!==void 0)return{...d,data:a};const f=await ar(e,c,n,a);return"code"in f?f:le(f,i,n)},ir=e=>e.status!=="ok"?`unavailable (${e.reason})`:e.unit==="microseconds"?`${I(e.value)} µs`:I(e.value),cr=(e,t,r)=>t?"no-product":e===void 0?"unknown":r.some(o=>o.enabled)?"active":r.length>0?"disabled-only":"none",ue=e=>e.map(t=>`"${t.name??t.id??"?"}" (limit ${JSON.stringify(t.limit)})${t.enabled?"":" — disabled, not protecting you"}`).join(", "),dr={active:e=>`alert: ${ue(e.alerts)}`,"disabled-only":e=>`alert: none active; ${ue(e.alerts)}`,"no-product":()=>"alert: none possible — Cloudflare has no usage alert for this; the budget alert is its guard",none:()=>"alert: none",unknown:()=>"alert: unknown (several offered products could mean this)"},lr=(e,t,r)=>{const o=Ee(e.id,t.discovery.products),n=o.status==="matched"?o.product:void 0,a=(n===void 0?[]:r.filter(s=>Array.isArray(s.filters?.product)&&s.filters.product.includes(n.id))).map(s=>({enabled:s.enabled===!0,id:s.id,limit:s.filters?.limit,name:s.name}));return{alerts:a,coverage:cr(n?.id,o.status==="no-product",a),id:e.id,label:e.label,...n===void 0?{}:{product:n.id,productSource:n.source},usage:e}},ur=(e,t,r)=>{const o=t.policies.filter(a=>a.alert_type===A),n=t.usage.map(a=>lr(a,t,o));r.info(`Cloudflare account ${e.accountId} — usage ${t.period.startDate} to ${t.period.endDate}`);for(const a of n)r.info(` ${a.label}: ${ir(a.usage)} — ${dr[a.coverage](a)}`);return r.info(`Usage Based Billing notifications on the account: ${String(o.length)} (${String(o.filter(a=>a.enabled===!0).length)} enabled).`),r.info(Y),Ie(r,t.discovery.eligible),r.info(R),ke({...z(e,t),metrics:n})},fr=e=>{const t=J(e);return t===void 0?void 0:W(t).parsed?.account_id},mr=e=>{const{accountId:t,token:r}=H(e.environment??process.env,fr(e.cwd));return r===void 0?b(e.logger,u.AUTH,`CLOUDFLARE_API_TOKEN is not set. \`lunora cloudflare alerts\` calls the Cloudflare API with an API token (a wrangler login session is not reused); create one with the "${E.notificationsWrite}" and "${E.analytics}" permissions ("${E.notificationsRead}" is enough for status and test).`):t===void 0?b(e.logger,u.USAGE,"No Cloudflare account: set CLOUDFLARE_ACCOUNT_ID or `account_id` in wrangler.jsonc."):at({accountId:t,token:r,...e.fetch===void 0?{}:{fetch:e.fetch}})},Ce=async e=>{const t=await e.notifications("Listing notification policies","GET","/alerting/v3/policies");return Array.isArray(t)?t:[]},hr=async(e,t)=>{const r=wt(t.now??new Date),{logger:o}=t,[n,a,s]=await Promise.all([Ce(e),e.notifications("Listing the alert types this account is eligible for","GET","/alerting/v3/available_alerts").then(i=>({ok:!0,value:i}),i=>({error:i,ok:!1})),Et(e,r).catch(i=>{const c=i instanceof Error?i.message:String(i);return o.warn(`Usage could not be read: ${c}`),S.map(({id:d,label:f})=>({id:d,label:f,reason:c,status:"unavailable"}))})]);return a.ok||o.warn(`Could not read which alert types the account is eligible for: ${a.error instanceof Error?a.error.message:String(a.error)}`),{discovery:ft(a.ok?a.value:void 0,a.ok,n),period:r,policies:n,usage:s}},gr=async(e,t)=>{const r=await Ce(e),o=await Wt(e,r,t.webhooks??[],t.logger,t.now??new Date);return t.logger.info(R),ke({...z(e),deliveries:o})},pr=async e=>{const t=e.subcommand==="setup"?zt(e):void 0;if(t!==void 0&&"error"in t)return b(e.logger,u.USAGE,t.error);const r=mr(e);if(!("graphql"in r))return r;try{if(e.subcommand==="test")return await gr(r,e);const o=await hr(r,e);return t===void 0?ur(r,o,e.logger):await sr(r,o,e,t)}catch(o){return V(e.logger,o)}},fe=new Set(["build","delete","images","info","list","push"]),yr=new Set(["build","push"]),me=new Set(["images list","info","list"]),wr=async e=>{const[t,...r]=e.argument;if(t===void 0||!fe.has(t)){const l=`lunora cloudflare containers requires a subcommand: ${[...fe].toSorted((h,g)=>h.localeCompare(g)).join(" | ")}. Example: lunora cloudflare containers build ./containers/app --tag app:v1 --push`;return e.logger.error(l),{code:u.USAGE,error:l}}const o=e.format==="json",n=t==="images"?`images ${r[0]??""}`.trim():t;if(o&&!me.has(n)){const l=`containers ${n}: --format json is only available for the read subcommands (${[...me].toSorted((h,g)=>h.localeCompare(g)).join(" | ")}) — wrangler has no JSON rendering for the rest.`;return e.logger.error(l),{code:u.USAGE,error:l}}if(yr.has(t)&&!(e.dockerAvailable??qe)()){const l=`containers ${t} needs a running Docker-compatible engine (it builds/pushes images locally). Start Docker or Colima and retry. Note: container images must target linux/amd64.`;return e.logger.error(l),{code:u.MISSING_DEPENDENCY,error:l}}const a=["containers",t,...r];o&&a.push("--json"),e.tag!==void 0&&a.push("--tag",e.tag),e.push===!0&&a.push("--push"),e.env!==void 0&&a.push("--env",e.env);const s=e.cwd??process.cwd(),i=$e(Ae(s),"wrangler",a),c={args:i.args,command:i.command,cwd:s};return e.logger.info(`running ${c.command} ${c.args.join(" ")}`),{code:(await(e.spawner??be)(c)).code,delegated:o,descriptor:c}},k=(e,t)=>(t!==void 0&&e.push("--env",t),e),vr=e=>{const t=k(["deployments","list"],e.env);return e.format==="json"&&t.push("--json"),t},br=e=>{switch(e.subcommand){case"inspect":return e.versionId===void 0?{error:"deployments inspect requires a version id. Usage: lunora cloudflare deployments inspect <version-id>"}:{args:k(["versions","view",e.versionId],e.env)};case"list":return{args:vr(e)};case"promote":{if(e.versionId===void 0)return{error:"deployments promote requires a version id. Usage: lunora cloudflare deployments promote <version-id> --yes"};if(!e.yes)return{error:"deployments promote shifts 100% of live traffic. Re-run with --yes to confirm."};const t=k(["versions","deploy",`${e.versionId}@100%`,"--yes"],e.env);return e.message!==void 0&&t.push("--message",e.message),{args:t}}case"rollback":{if(!e.yes)return{error:"deployments rollback changes the live version. Re-run with --yes to confirm."};const t=k(["rollback"],e.env);return e.versionId!==void 0&&t.push(e.versionId),t.push("--yes"),e.message!==void 0&&t.push("--message",e.message),{args:t}}default:return{error:`deployments: unknown subcommand "${e.subcommand}"`}}},$r=async e=>{if(e.format==="json"&&e.subcommand!=="list"){const d=`deployments ${e.subcommand}: --format json is only available for \`deployments list\` — wrangler has no JSON rendering for the others.`;return e.logger.error(d),{code:u.USAGE,descriptor:void 0,error:d}}const{args:t,error:r}=br(e);if(r!==void 0||t===void 0)return e.logger.error(r??"deployments: nothing to run"),{code:u.USAGE,descriptor:void 0,error:r};const{logger:o}=e,n=e.cwd??process.cwd(),a=$e(Ae(n),"wrangler",t),s={args:a.args,command:a.command,cwd:n};return o.info(`${s.command} ${s.args.join(" ")}`),{code:(await(e.spawner??be)(s)).code,delegated:e.format==="json",descriptor:s}},Ar=e=>e==="list"||e==="inspect"||e==="rollback"||e==="promote",Sr="https://api.cloudflare.com/client/v4/accounts",Ne="Workers Scripts Read",he=1e3,ge=5e4,_r=1e4,Er=3e4,pe=[31,139],ye=["cpu","heap"],Tr=/^[\da-f]{64}$/iu,v=(e,t,r)=>(e.error(r),{code:t,error:r}),we=e=>{try{const t=JSON.parse(e);if(Array.isArray(t.errors)){const r=t.errors.map(o=>typeof o=="object"&&o!==null&&"message"in o?String(o.message):"").filter(o=>o.length>0);if(r.length>0)return L(r.join("; "))}}catch{}return L(e.trim())},Ir=e=>{switch(e){case 401:case 403:return` — check that CLOUDFLARE_API_TOKEN is valid and has the "${Ne}" permission on this account`;case 404:return" — check the worker name, version, and (for a Durable Object) the namespace and actor ids";case 429:return" — rate limited; wait and retry";default:return""}},kr=e=>{const t=e.type??"cpu";if(!ye.includes(t))return{error:`profile: invalid --type "${e.type??""}" — expected ${ye.join(" | ")}`};const r=e.durationMs===void 0?_r:Number(e.durationMs);return!Number.isInteger(r)||r<he||r>ge?{error:`profile: invalid --duration-ms "${e.durationMs??""}" — expected an integer from ${String(he)} to ${String(ge)}`}:e.namespaceId===void 0!=(e.actorId===void 0)?{error:"profile: --namespace-id and --actor-id must be passed together to profile a Durable Object"}:e.actorId!==void 0&&!Tr.test(e.actorId)?{error:"profile: --actor-id must be a 64-character hexadecimal Durable Object id"}:{durationMs:r,profileType:t}},Or=e=>{const{logger:t}=e,r=J(e.cwd),o=r===void 0?void 0:W(r).parsed,{accountId:n,token:a}=H(e.environment??process.env,o?.account_id);if(a===void 0)return v(t,u.AUTH,`CLOUDFLARE_API_TOKEN is not set. \`lunora cloudflare profile\` calls the Cloudflare API with an API token (a wrangler login session is not reused); create one with the "${Ne}" permission.`);if(n===void 0)return v(t,u.USAGE,"No Cloudflare account: set CLOUDFLARE_ACCOUNT_ID or `account_id` in wrangler.jsonc.");const s=typeof o?.name=="string"&&o.name.length>0?o.name:void 0,i=e.worker??s;if(i===void 0)return v(t,u.USAGE,"profile: no Worker name — pass one, or set `name` in wrangler.jsonc.");const c=e.env===void 0?void 0:o?.env?.[e.env]?.name;let d=i;return e.worker===void 0&&e.env!==void 0&&(d=typeof c=="string"&&c.length>0?c:`${i}-${e.env}`),{accountId:n,sourceMapsMissing:o!==void 0&&o.upload_source_maps!==!0,token:a,worker:d}},Cr=(e,t)=>{const r=`${e}.${String(process.pid)}.tmp`;try{Re(Je(e),{recursive:!0}),ve(r,t),Me(r,e);return}catch(o){try{je(r,{force:!0})}catch{}return o instanceof Error?o.message:String(o)}},Nr=async(e,{accountId:t,token:r,worker:o},{durationMs:n,profileType:a,version:s})=>{const{logger:i}=e,c={duration_ms:n,profile_type:a,...e.namespaceId===void 0||e.actorId===void 0?{}:{actor_id:e.actorId,namespace_id:e.namespaceId}},d=`${Sr}/${encodeURIComponent(t)}/workers/workers/${encodeURIComponent(o)}/versions/${encodeURIComponent(s)}/profile`,f=e.fetch??globalThis.fetch.bind(globalThis);let l,h;try{l=await f(d,{body:JSON.stringify(c),headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},method:"POST",signal:AbortSignal.timeout(n+Er)}),h=new Uint8Array(await l.arrayBuffer())}catch(g){return v(i,u.UNAVAILABLE,`profile failed: ${g instanceof Error?g.message:String(g)}.`)}if(!l.ok){const g=we(new TextDecoder().decode(h));return v(i,j(l.status),`profile failed (HTTP ${String(l.status)})${g.length>0?`: ${g}`:""}${Ir(l.status)}.`)}if(h[0]!==pe[0]||h[1]!==pe[1]){const g=we(new TextDecoder().decode(h));return v(i,u.FAILURE,`profile failed: the API answered 200 with something that is not a gzip profile${g.length>0?`: ${g}`:""}.`)}return{bytes:h}},Ur=async e=>{const{logger:t}=e,r=kr(e);if("error"in r)return v(t,u.USAGE,r.error);const o=Or(e);if("code"in o)return o;const{durationMs:n,profileType:a}=r,{worker:s}=o,i=e.version??"latest";t.info(`Profiling ${s}@${i} (${a}) for ${String(n)} ms — keep traffic flowing to it…`);const c=await Nr(e,o,{durationMs:n,profileType:a,version:i});if("code"in c)return c;const{bytes:d}=c,f=(e.now??new Date).toISOString().replaceAll(/[:.]/gu,"-"),l=Be(e.cwd,e.out??`${s}-${a}-${f}.pprof.gz`),h=Cr(l,d);return h!==void 0?v(t,u.FAILURE,`profile failed: could not write ${l}: ${h}.`):(t.success(`Wrote ${String(d.byteLength)} bytes to ${l}`),t.info("Open it with `go tool pprof -http=: <file>` (pprof reads gzip directly) or upload it to a flamegraph viewer."),o.sourceMapsMissing&&t.warn("wrangler config has no `upload_source_maps: true` — TypeScript function names in the profile may be mangled."),{code:u.SUCCESS,data:{bytes:d.byteLength,durationMs:n,file:l,profileType:a,version:i,worker:s}})},Dr={ai:{absorbs:"gateway",tool:"ai-gateway"},alert:{tool:"alerts"}},Rr=(e,t)=>{const r=Dr[e];if(r!==void 0)return{rest:r.absorbs!==void 0&&t[0]===r.absorbs?t.slice(1):t,tool:r.tool};const o=O.find(n=>n.name===e)?.name;return o===void 0?void 0:{rest:t,tool:o}},Mr={celld:"celld",node:"Node"},jr=e=>e==="status"||e==="setup"||e==="test",T=(e,t,r)=>(e.error(r),{code:t,error:r}),Lr=e=>{const t=Math.max(...O.map(r=>r.name.length));e.info("lunora cloudflare <tool> — tools for an app deployed to your own Cloudflare account:");for(const r of O)e.info(` ${r.name.padEnd(t)} ${r.summary}`),r.usage!==""&&e.info(` ${" ".repeat(t)} lunora cloudflare ${r.name} ${r.usage}`);return e.info("Run `lunora cloudflare --help` for every tool's arguments and options."),{code:0}},Pr=(e,t,r)=>{const{error:o,target:n}=De(e,r);return n===void 0?o??"no deploy target":n==="cloudflare"?void 0:`${r===void 0?`this project deploys to ${Mr[n]??`"${n}"`}`:`\`--target ${n}\``} — \`lunora cloudflare ${t}\` only applies to Cloudflare.`},Gr=(e,{logger:t,rest:r})=>r.length===0?void 0:T(t,u.USAGE,`cloudflare ${e}: takes no arguments, got "${r.join(" ")}"`),Fr=async e=>{const{cwd:t,logger:r,options:o}=e;return Gr("ai-gateway",e)??Xe({cwd:t,dryRun:o.dryRun===!0,id:o.id,logger:r,logs:o.logs})},xr=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const a=n[0]??"status";return jr(a)?pr({allowFloor:o.allowFloor===!0,cwd:e,dryRun:o.dryRun===!0,format:t,logger:r,replaceRecipients:o.replaceRecipients===!0,subcommand:a,yes:o.yes===!0,...o.email===void 0?{}:{emails:o.email},...o.webhook===void 0?{}:{webhooks:o.webhook},...o.multiplier===void 0?{}:{multiplier:o.multiplier},...o.threshold===void 0?{}:{thresholds:o.threshold}}):T(r,u.USAGE,`cloudflare alerts: unknown subcommand "${a}" — expected status | setup | test`)},qr=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const a=await wr({argument:n,cwd:e,env:o.env,format:t,logger:r,push:o.push===!0,tag:o.tag});return{code:a.code,delegated:a.delegated,error:a.error}},Br=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const[a,s]=n;if(!Ar(a))return T(r,u.USAGE,`cloudflare deployments: unknown subcommand "${a??""}" — expected list | inspect | rollback | promote`);const i=await $r({cwd:e,env:o.env,format:t,logger:r,message:o.message,subcommand:a,versionId:s,yes:o.yes===!0});return{code:i.code,delegated:i.delegated,error:i.error}},Jr=async({cwd:e,logger:t,options:r,rest:o})=>o.length>1?T(t,u.USAGE,`cloudflare profile: takes at most one argument (the worker), got "${o.join(" ")}"`):Ur({actorId:r.actorId,cwd:e,durationMs:r.durationMs,env:r.env,logger:t,namespaceId:r.namespaceId,out:r.out,type:r.type,version:r.versionId,worker:o[0]}),Wr={"ai-gateway":Fr,alerts:xr,containers:qr,deployments:Br,profile:Jr},so=Ue(async({argument:e,cwd:t,format:r,logger:o,options:n})=>{const[a,...s]=e;if(a===void 0||a==="")return Lr(o);const i=Rr(a,s);if(i===void 0)return T(o,u.USAGE,`cloudflare: unknown tool "${a}" — expected ${O.map(f=>f.name).join(" | ")}`);const{tool:c}=i,d=Pr(t,c,n.target);return d!==void 0?T(o,u.USAGE,d):Wr[c]({cwd:t,format:r,logger:o,options:n,rest:i.rest})});export{so as execute};
|
|
1
|
+
import{d as Ue}from"../packem_shared/command-CB4JM-Jl.mjs";import{r as De}from"../packem_shared/deploy-target-resolve-BTCW1Iv4.mjs";import{exitCodeForStatus as j,EXIT_CODE as u}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{writeFileSync as ve,mkdirSync as Re,renameSync as Me,rmSync as je}from"node:fs";import{findWranglerFile as J,readWranglerJsonc as W,applyModify as Le}from"@lunora/config/cloudflare";import{c as L}from"../packem_shared/cap-error-body-YBKO32BF.mjs";import{r as H,n as X,c as Pe}from"../packem_shared/cloudflare-credentials-BVmZnDZk.mjs";import{c as Ge,b as Fe}from"../packem_shared/variables-CPYyJtsL.mjs";import{b as xe}from"../packem_shared/tui-prompts-B3YwUhGw.mjs";import{i as qe}from"../packem_shared/docker-DpVxvYpL.mjs";import{defaultSpawner as be}from"../packem_shared/createRecordingSpawner-01ZngIwK.mjs";import{execArgsFor as $e,detectPackageManager as Ae}from"@lunora/config/cli";import{a as O}from"../packem_shared/cli-BGHRPHBJ.mjs";import{resolve as Be,dirname as Je}from"node:path";const We="https://api.cloudflare.com/client/v4/accounts",Q=64,Z={byok:"https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/",unifiedBilling:"https://developers.cloudflare.com/ai-gateway/features/unified-billing/"},$=(e,t,r)=>(e.error(r),{code:t,error:r}),ee=async(e,t,r,o={})=>{const n=await e(t,{...o,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"}}),a=await n.text();let s;try{s=JSON.parse(a)}catch{}return{body:s,ok:n.ok&&s?.success!==!1,status:n.status,text:a}},te=e=>{const t=(e.body?.errors??[]).map(r=>r.message).filter(r=>typeof r=="string");return`${String(e.status)}: ${L(t.length>0?t.join("; "):e.text)}`},He=(e,t,r)=>{let o=e;const n=[];for(const[a,s]of Object.entries(r))t?.[a]!==s&&(o=Le(o,["vars",a],s),n.push(a));return{changed:n,text:o}},Ye=(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 — ${Z.unifiedBilling}`),e.info(` - a key stored on the gateway (Mistral, Perplexity, OpenRouter, …): ${Z.byok}`),e.info(" Then redeploy (`lunora deploy`) so the Worker picks up the new vars.")},Ke=async(e,t,r,o,n,a)=>{const s=`${We}/${encodeURIComponent(r)}/ai-gateway/gateways`,i=await ee(t,`${s}/${encodeURIComponent(n)}`,o);if(i.ok){const d=i.body?.result?.collect_logs;return{action:"existing",collectLogs:typeof d=="boolean"?d:a}}if(i.status===401||i.status===403)return $(e,j(i.status),`ai-gateway: could not read AI Gateway "${n}" (${te(i)})`);const c=await ee(t,s,o,{body:JSON.stringify({cache_invalidate_on_update:!0,cache_ttl:0,collect_logs:a,id:n,rate_limiting_interval:0,rate_limiting_limit:0}),method:"POST"});return c.ok?{action:"created",collectLogs:a}:$(e,j(c.status),`ai-gateway: could not create AI Gateway "${n}" (${te(c)})`)},Ve=e=>{const{logger:t}=e,r=J(e.cwd);if(r===void 0)return $(t,u.NOT_FOUND,"ai-gateway: no wrangler.jsonc found — run `lunora init` (or `lunora dev`) first.");const{parsed:o,text:n}=W(r);if(o===void 0)return $(t,u.USAGE,`ai-gateway: could not parse ${r} as JSONC.`);const a=X(e.id)??X(o.name);return a===void 0?$(t,u.USAGE,"ai-gateway: no gateway id — pass --id <id>, or set `name` in wrangler.jsonc."):a.length>Q?$(t,u.USAGE,`ai-gateway: gateway id "${a}" is longer than ${String(Q)} characters — pass a shorter --id.`):{gatewayId:a,parsed:o,text:n,wranglerPath:r}},P=e=>e?"on":"off",ze=(e,t)=>(e.info(`ai-gateway (dry run): would create or reuse AI Gateway "${t.gatewayId}" (log collection ${P(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}),Xe=async e=>{const{logger:t}=e,r=Ve(e);if("code"in r)return r;const{gatewayId:o,parsed:n,text:a,wranglerPath:s}=r,i=e.logs!==!1,{accountId:c,token:d}=H(e.environment??process.env,n.account_id),f={[Fe]:o,...c===void 0?{}:{[Ge]:c}},l=He(a,n.vars,f);if(e.dryRun===!0)return ze(t,{accountId:c,action:"planned",collectLogs:i,dryRun:!0,gatewayId:o,varsWritten:l.changed,wranglerPath:s});if(d===void 0||c===void 0){const g=[d===void 0?"CLOUDFLARE_API_TOKEN (an API token with AI Gateway Write)":void 0,c===void 0?"CLOUDFLARE_ACCOUNT_ID (or `account_id` in wrangler.jsonc)":void 0].filter(y=>y!==void 0);return $(t,u.AUTH,`ai-gateway: missing ${g.join(" and ")}.`)}const h=await Ke(t,e.fetch??globalThis.fetch.bind(globalThis),c,d,o,i);return"code"in h?h:(h.action==="created"?t.success(`ai-gateway: created AI Gateway "${o}" (log collection ${P(h.collectLogs)}).`):t.info(`ai-gateway: AI Gateway "${o}" already exists — reusing it (log collection ${P(h.collectLogs)}; change it in the dashboard).`),l.changed.length>0?(ve(s,l.text,"utf8"),t.success(`ai-gateway: wrote ${l.changed.join(", ")} into ${s} vars.`)):t.info(`ai-gateway: ${s} vars are already up to date.`),Ye(t,o),{code:0,data:{accountId:c,action:h.action,collectLogs:h.collectLogs,dryRun:!1,gatewayId:o,varsWritten:l.changed,wranglerPath:s}})},Qe=15e3,Ze="https://api.cloudflare.com/client/v4/graphql",E={analytics:"Account Analytics Read",notificationsRead:"Notifications Read",notificationsWrite:"Notifications Write"},et={auth:u.AUTH,failure:u.FAILURE,"not-found":u.NOT_FOUND,permission:u.PERMISSION,"rate-limited":u.RATE_LIMITED,rejected:u.USAGE,unavailable:u.UNAVAILABLE};class _ extends Error{kind;status;constructor(t,r,o){super(t),this.name="CloudflareApiError",this.kind=r,this.status=o}}const tt=e=>e===401?"auth":e===403?"permission":e===404?"not-found":e===429?"rate-limited":e>=500?"unavailable":"rejected",Se=e=>{try{const r=JSON.parse(e);if(Array.isArray(r.errors)){const o=r.errors.map(n=>typeof n=="object"&&n!==null&&"message"in n?String(n.message):void 0).filter(n=>n!==void 0&&n.length>0);if(o.length>0)return o}}catch{}const t=e.trim().slice(0,200);return t.length>0?[t]:[]},rt=9106,ot=(e,t)=>{try{const r=JSON.parse(e);return Array.isArray(r.errors)&&r.errors.some(o=>typeof o=="object"&&o!==null&&"code"in o&&o.code===t)}catch{return!1}},re=(e,t,r,o)=>{const n=t===400&&ot(r,rt)?"auth":tt(t),a=Se(r).join("; ");let s="";return n==="auth"?s=` — check that CLOUDFLARE_API_TOKEN is a valid token; it needs the "${o}" permission on this account`:n==="permission"&&(s=` — the API token needs the "${o}" permission on this account`),new _(`${e} failed (HTTP ${String(t)})${a.length>0?`: ${a}`:""}${s}.`,n,t)},oe=(e,t)=>{const r=t instanceof Error&&(t.name==="TimeoutError"||t.name==="AbortError");let o=t instanceof Error?t.message:String(t);return r&&(o="no answer in time"),new _(`${e} failed: ${o}.`,"unavailable")},nt=10,at=e=>{const{accountId:t,token:r}=e,o=e.timeoutMs??Qe,n=e.fetch??globalThis.fetch.bind(globalThis),a=async(s,i,c,d)=>{let f;try{f=await Pe({accountId:t,apiToken:r,fetch:n,init:{method:i,signal:AbortSignal.timeout(o),...d===void 0?{}:{body:JSON.stringify(d)}},path:c})}catch(l){throw oe(s,l)}if(!f.ok)throw re(s,f.status,f.text,i==="GET"?E.notificationsRead:E.notificationsWrite);return f.body};return{accountId:t,graphql:async(s,i,c={})=>{let d;try{d=await n(Ze,{body:JSON.stringify({query:i,variables:c}),headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},method:"POST",signal:AbortSignal.timeout(o)})}catch(l){throw oe(s,l)}const f=await d.text();if(!d.ok)throw re(s,d.status,f,E.analytics);try{const l=JSON.parse(f);return{data:l.data??void 0,errors:Array.isArray(l.errors)&&l.errors.length>0?Se(f):[]}}catch{throw new _(`${s} failed: the GraphQL API answered with something that is not JSON.`,"unavailable",d.status)}},notifications:async(s,i,c,d)=>(await a(s,i,c,d)).result,notificationsList:async(s,i,c=nt)=>{const d=async f=>{const l=await a(s,"GET",`${i}${i.includes("?")?"&":"?"}page=${String(f)}`),h=Array.isArray(l.result)?l.result:[],g=l.result_info;return typeof g?.total_pages=="number"&&f<g.total_pages&&f<c?[...h,...await d(f+1)]:h};return d(1)}}},A="billing_usage_alert",ne={"do-duration":"worker_durable_objects_duration","do-requests":"worker_durable_objects_requests","do-rows-read":"worker_durable_objects_storage_reads","do-rows-written":"worker_durable_objects_storage_writes","workers-requests":"worker_requests"},st=6,C=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)?e:void 0,U=(e,t)=>{const r=t.toLowerCase(),o=Object.keys(e).find(n=>n.toLowerCase()===r);return o===void 0?void 0:e[o]},G=(e,t)=>{for(const r of t){const o=U(e,r);if(typeof o=="string"&&o.length>0)return o}},it=e=>{if(typeof e=="string"&&e.length>0)return{id:e,source:"available-alerts"};const t=C(e),r=t===void 0?void 0:G(t,["id","value","key","name"]);if(t===void 0||r===void 0)return;const o=G(t,["description","label","display_name","displayName","name"]);return{id:r,source:"available-alerts",...o===void 0||o===r?{}:{label:o}}},ct=["AvailableValues","available_values","values","options","enum","choices"],_e=e=>e.map(t=>it(t)).filter(t=>t!==void 0),dt=e=>G(e,["key","name","filter","id"])!=="product"?[]:ct.flatMap(t=>{const r=U(e,t);return Array.isArray(r)?_e(r):[]}),F=(e,t,r)=>{if(t>st)return;if(Array.isArray(e)){for(const n of e)F(n,t+1,r);return}const o=C(e);if(o!==void 0){r.push(...dt(o));for(const[n,a]of Object.entries(o))n.toLowerCase()==="product"&&Array.isArray(a)?r.push(..._e(a)):typeof a=="object"&&a!==null&&F(a,t+1,r)}},lt=e=>{const t=C(e);if(t!==void 0){for(const r of Object.values(t))if(Array.isArray(r)){const o=r.map(n=>C(n)).find(n=>n!==void 0&&U(n,"type")===A);if(o!==void 0)return o}}},ut=e=>e.filter(t=>t.alert_type===A&&Array.isArray(t.filters?.product)).flatMap(t=>(t.filters?.product).filter(r=>typeof r=="string"&&r.length>0)).map(t=>({id:t,source:"existing-policy"})),ft=(e,t,r)=>{const o=lt(e),n=[];o!==void 0&&F(U(o,"filter_options"),0,n);const a=new Map;for(const s of[...n,...ut(r)])a.has(s.id)||a.set(s.id,s);return{eligible:t?o!==void 0:void 0,products:[...a.values()]}},mt=/[^a-z\d]+/u,ht=e=>`${e.id} ${e.label??""}`.toLowerCase().split(mt).filter(t=>t.length>0),gt={"d1-rows-read":{all:[["d1"],["read"]]},"d1-rows-written":{all:[["d1"],["writ"]]},"do-duration":{all:[["durable"],["duration"]]},"do-requests":{all:[["durable"],["request"]]},"do-rows-read":{all:[["durable"],["read"]],none:["request"]},"do-rows-written":{all:[["durable"],["writ"]]},"workers-cpu":{all:[["worker"],["cpu"]],none:["durable"]},"workers-requests":{all:[["worker"],["request"]],none:["durable","cpu","kv","d1"]}},pt=(e,t)=>{const r=ht(t),o=s=>r.some(i=>i.startsWith(s)),{all:n,none:a=[]}=gt[e];return n.every(s=>s.some(i=>o(i)))&&!a.some(s=>o(s))},Ee=(e,t)=>{const r=ne[e],o=r===void 0?void 0:t.find(s=>s.id===r);if(o!==void 0)return{product:o,status:"matched"};const n=new Set(Object.values(ne)),a=t.filter(s=>!n.has(s.id)&&pt(e,s));return a.length===1&&a[0]!==void 0?{product:a[0],status:"matched"}:a.length>1?{candidates:a.map(s=>s.id),status:"ambiguous"}:r===void 0?{status:"no-product"}:{product:{id:r,source:"documented"},status:"matched"}},ae="workersInvocationsAdaptive",yt="durableObjectsInvocationsAdaptiveGroups",M="durableObjectsPeriodicGroups",se="d1AnalyticsAdaptiveGroups",S=[{candidates:[{field:"requests",unit:"count"}],dataset:ae,filter:"datetime",id:"workers-requests",label:"Workers requests"},{candidates:[{field:"cpuTimeUs",unit:"microseconds"}],dataset:ae,filter:"datetime",id:"workers-cpu",label:"Workers CPU time"},{candidates:[{field:"requests",unit:"count"}],dataset:yt,filter:"date",id:"do-requests",label:"Durable Objects requests"},{candidates:[{field:"activeTime",unit:"unknown"}],dataset:M,filter:"date",id:"do-duration",label:"Durable Objects duration"},{candidates:[{field:"rowsRead",unit:"count"}],dataset:M,filter:"date",id:"do-rows-read",label:"Durable Objects rows read"},{candidates:[{field:"rowsWritten",unit:"count"}],dataset:M,filter:"date",id:"do-rows-written",label:"Durable Objects rows written"},{candidates:[{field:"rowsRead",unit:"count"}],dataset:se,filter:"datetime-hour",id:"d1-rows-read",label:"D1 rows read"},{candidates:[{field:"rowsWritten",unit:"count"}],dataset:se,filter:"datetime-hour",id:"d1-rows-written",label:"D1 rows written"}],ie=e=>String(e).padStart(2,"0"),wt=e=>{const t=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth()-1,1)),r=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),0)),o=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),1)),n=a=>`${String(a.getUTCFullYear())}-${ie(a.getUTCMonth()+1)}-${ie(a.getUTCDate())}`;return{endDate:n(r),nextStartDate:n(o),startDate:n(t)}},vt=(e,t)=>{const r=JSON.stringify(`${t.startDate}T00:00:00Z`),o=JSON.stringify(`${t.nextStartDate}T00:00:00Z`);switch(e){case"date":return`{ date_geq: ${JSON.stringify(t.startDate)}, date_lt: ${JSON.stringify(t.nextStartDate)} }`;case"datetime":return`{ datetime_geq: ${r}, datetime_lt: ${o} }`;default:return`{ AND: [{ datetimeHour_geq: ${r}, datetimeHour_lt: ${o} }] }`}},bt=e=>`query LunoraTypeFields { __type(name: ${JSON.stringify(e)}) { fields { name type { name ofType { name ofType { name ofType { name } } } } } } }`,$t=e=>{let t=e;for(;t!=null;){if(typeof t.name=="string"&&t.name.length>0)return t.name;t=t.ofType}},At=e=>{const t=new Map;return async r=>{const o=t.get(r);if(o!==void 0)return o;const n=e.graphql(`Reading the analytics schema (${r})`,bt(r)).then(a=>{if(a.errors.length>0)throw new _(`Reading the analytics schema failed: ${a.errors.join("; ")}.`,"rejected");const i=a.data?.__type?.fields??[];return new Map(i.map(c=>[c.name,$t(c.type)]))});return t.set(r,n),n}},Te=async(e,t,r)=>{const o=await e(t),[n,...a]=r;if(n===void 0)return o;const s=o.get(n);if(s===void 0)throw new _(`the analytics schema has no \`${n}\` on \`${t}\``,"rejected");return Te(e,s,a)},St=e=>e instanceof Error?e.message:String(e),_t=async(e,t,r,o,n)=>{const{id:a,label:s}=o;try{const i=await Te(t,r,["viewer","accounts",o.dataset,"sum"]),c=o.candidates.find(y=>i.has(y.field));if(c===void 0){const y=o.candidates.map(m=>`\`${m.field}\``).join(", ");return{id:a,label:s,reason:`\`${o.dataset}.sum\` exposes no ${y} field`,status:"unavailable"}}const d=`query LunoraUsage($accountTag: string) { viewer { accounts(filter: { accountTag: $accountTag }) { ${o.dataset}(limit: 1, filter: ${vt(o.filter,n)}) { sum { ${c.field} } } } } }`,f=await e.graphql(`Reading ${s}`,d,{accountTag:e.accountId});if(f.errors.length>0)return{id:a,label:s,reason:f.errors.join("; "),status:"unavailable"};const l=f.data?.viewer?.accounts?.[0]?.[o.dataset],h=Array.isArray(l)?l[0]:void 0,g=h===void 0?0:h.sum?.[c.field];return typeof g!="number"||!Number.isFinite(g)?{id:a,label:s,reason:`the response carried no number for \`${c.field}\``,status:"unavailable"}:{field:`${o.dataset}.sum.${c.field}`,id:a,label:s,status:"ok",unit:c.unit,value:g}}catch(i){return{id:a,label:s,reason:St(i),status:"unavailable"}}},Et=async(e,t)=>{const r=await e.graphql("Reading the analytics schema","query LunoraQueryRoot { __schema { queryType { name } } }"),o=r.data?.__schema?.queryType?.name;if(r.errors.length>0||o===void 0){const a=r.errors.length>0?r.errors.join("; "):"the schema root could not be read";return S.map(({id:s,label:i})=>({id:s,label:i,reason:a,status:"unavailable"}))}const n=At(e);return Promise.all(S.map(async a=>_t(e,n,o,a,t)))},D="Lunora usage: ",Tt=3,Y="Cloudflare does not document the unit of a usage alert's limit; limits are sent as plain request counts. Check the stored limit in the dashboard.",It={"do-requests":1e6,"workers-requests":1e7},kt=100,Ot=e=>{const t=Math.ceil(e);if(t<=0)return 0;const r=10**Math.max(0,Math.floor(Math.log10(t))-1);return Math.ceil(t/r)*r},Ct=(e,t,r)=>{const o=Ot(e*t);return o>r?{basis:"history",threshold:o}:{basis:"floor",threshold:r}},x=e=>{const t=e.filters?.product;return Array.isArray(t)?t.filter(r=>typeof r=="string"):[]},Nt=e=>{const t=e.filters?.limit,r=Array.isArray(t)?t[0]:void 0,o=typeof r=="string"?Number(r):Number.NaN;return Number.isFinite(o)?o:void 0},K=e=>{const t={};for(const[r,o]of Object.entries(e?.mechanisms??{}))Array.isArray(o)&&(t[r]=o.filter(n=>typeof n=="object"&&n!==null&&"id"in n&&typeof n.id=="string"));return t},Ut=(e,t)=>{const r={};for(const[o,n]of[...Object.entries(e),...Object.entries(t)]){const a=new Set((r[o]??[]).map(s=>s.id));r[o]=[...r[o]??[],...n.filter(s=>!a.has(s.id))]}return Object.fromEntries(Object.entries(r).filter(([,o])=>o.length>0))},N=e=>Object.entries(e).flatMap(([t,r])=>r.map(o=>`${t}:${o.id}`)),Dt=(e,t)=>JSON.stringify(N(e).toSorted((r,o)=>r.localeCompare(o)))===JSON.stringify(N(t).toSorted((r,o)=>r.localeCompare(o))),Rt=(e,t)=>e.enabled===!0&&e.name===t.name&&JSON.stringify(e.filters?.limit)===JSON.stringify(t.filters.limit)&&JSON.stringify(x(e))===JSON.stringify(t.filters.product)&&Dt(K(e),t.mechanisms),Mt=(e,t)=>{const r={};return e.length>0&&(r.email=e.map(o=>({id:o}))),t.length>0&&(r.webhooks=t.map(o=>({id:o}))),r},jt=(e,t,r)=>{const o=r.thresholds?.[e],n=r.usage.find(c=>c.id===e),a=n?.status==="ok"?n.value:void 0;if(o!==void 0)return{basis:"explicit",lastMonth:a,threshold:o};const s=It[e];return s===void 0?{kind:"unit-unverified",reason:`the unit Cloudflare meters "${t.id}" in is not documented and cannot be matched to the usage reading; set one yourself with --threshold ${e}=<n>`}:a!==void 0?{...Ct(a,r.multiplier,s),lastMonth:a}:r.allowFloor===!0?{basis:"floor-usage-unavailable",lastMonth:void 0,threshold:s}:{kind:"usage-unavailable",reason:`last month's usage could not be read (${n?.status==="unavailable"?n.reason:"no reading"}), and an alert at the floor could fire every month on an account that already exceeds it; pass --threshold ${e}=<n>, or --allow-floor`}},Lt=(e,t)=>{const r=Nt(e);if(r===void 0||t===void 0||r<=0||t<=0)return;const o=Math.max(r/t,t/r);return o>=kt?`its limit ${String(r)} differs from the ${String(t)} this would plan by ${String(Math.round(o))}× — one of the two may be in the wrong unit`:void 0},q=e=>`${D}${e}`,Pt=(e,t)=>e===void 0?"create":Rt(e,t)?"unchanged":"update",Gt=(e,t)=>{switch(e){case"explicit":return"set with --threshold";case"floor-usage-unavailable":return"the included monthly allowance, applied with --allow-floor because usage could not be read";case"history":return`${String(t)}× last month`;default:return"the included monthly allowance (floor)"}},Ft=(e,t,r)=>{const{id:o,label:n}=e,a=Ee(o,t.products);if(a.status==="no-product")return{kind:"no-product",metric:o,reason:"Cloudflare has no usage alert for this product; the account-wide budget alert is its guard"};if(a.status==="ambiguous")return{kind:"ambiguous",metric:o,reason:`several offered products could mean this metric (${a.candidates.join(", ")})`};const{product:s}=a,i=r.find(p=>p.name===q(n)),c=r.find(p=>p.enabled===!0&&p.name?.startsWith(D)!==!0&&x(p).includes(s.id)),d=jt(o,s,t);if(i===void 0&&c!==void 0){const p=Lt(c,"threshold"in d?d.threshold:void 0);return{kind:"covered",metric:o,reason:`already alerted by the existing policy "${c.name??c.id??"?"}"`,...p===void 0?{}:{warning:p}}}if(!("threshold"in d))return{...d,metric:o};const f=Mt(t.emails,t.webhooks),l=K(i),h=i===void 0||t.replaceRecipients===!0?f:Ut(l,f),g=new Set(N(l)),y=new Set(N(h)),m={alert_type:A,description:`Managed by \`lunora cloudflare alerts setup\`: ${Gt(d.basis,t.multiplier)}.`,enabled:!0,filters:{limit:[String(d.threshold)],product:[s.id]},mechanisms:h,name:q(n)},w=i===void 0?[]:x(i);return{action:Pt(i,m),basis:d.basis,body:m,lastMonth:d.lastMonth,metric:o,...i?.id===void 0?{}:{policyId:i.id},...i!==void 0&&JSON.stringify(w)!==JSON.stringify([s.id])?{previousProduct:w}:{},product:s.id,productSource:s.source,recipients:{added:[...y].filter(p=>!g.has(p)),removed:[...g].filter(p=>!y.has(p))},threshold:d.threshold}},xt=e=>{const t=e.policies.filter(n=>n.alert_type===A),r=new Set(S.map(n=>q(n.label))),o={alerts:[],duplicates:[],orphans:t.filter(n=>n.name?.startsWith(D)===!0&&!r.has(n.name)),skipped:[]};for(const n of r)o.duplicates.push(...t.filter(a=>a.name===n).slice(1));for(const n of S){const a=Ft(n,e,t);"action"in a?o.alerts.push(a):o.skipped.push(a)}return o},qt="Cloudflare's API has no endpoint that sends a test notification — not for webhooks and not for email — so `lunora cloudflare alerts test` sends nothing.",Bt="To test a webhook for real, create it in the dashboard (Alerts > Destinations > Webhooks > Create) and finish with Save and Test.",Jt="Email destinations cannot be tested at all: Cloudflare offers no test send for them. Check the address on each policy below instead.",ce=e=>Array.isArray(e)?e:[],de=30,Wt=async(e,t,r,o,n)=>{const a=t.filter(m=>m.alert_type===A&&m.name?.startsWith(D)===!0),s=a.map(m=>K(m)),i=new Set([...s.flatMap(m=>(m.webhooks??[]).map(w=>w.id)),...r]),c=[...new Set(s.flatMap(m=>(m.email??[]).map(w=>w.id)))],d=new Set(a.map(m=>m.id)),f=new Date(n.getTime()-de*24*60*60*1e3).toISOString(),[l,h]=await Promise.all([e.notifications("Listing webhook destinations","GET","/alerting/v3/destinations/webhooks"),a.length===0?Promise.resolve([]):e.notificationsList("Reading notification history",`/alerting/v3/history?per_page=25&since=${encodeURIComponent(f)}`)]),g=ce(l).map(m=>({...m,referenced:m.id!==void 0&&i.has(m.id)})),y=ce(h).filter(m=>m.alert_type===A&&m.policy_id!==void 0&&d.has(m.policy_id));o.warn(qt),a.length===0&&o.info("No `lunora cloudflare alerts setup` policies exist on this account yet — run `lunora cloudflare alerts setup` first.");for(const m of[...i].filter(w=>!g.some(p=>p.id===w)))o.warn(`Webhook destination ${m} is referenced but does not exist on this account.`);for(const m of g.filter(w=>w.referenced))o.info(`webhook "${m.name??m.id??"?"}" (${m.type??"generic"}): last delivered ${m.last_success??"never"}, last failed ${m.last_failure??"never"}`);if(o.info(Bt),c.length>0&&o.info(`${Jt} Addresses on the policies: ${c.join(", ")}.`),y.length===0)o.info(`Cloudflare's notification history shows nothing sent for these policies in the last ${String(de)} days (they only fire once a threshold is crossed).`);else for(const m of y)o.info(`sent ${m.sent??"?"}: "${m.name??"?"}" via ${m.mechanism_type??"?"} to ${m.mechanism??"?"}`);return{emails:c,history:y,testSent:!1,webhooks:g}},R="Budget alert (account-wide USD threshold): Cloudflare documents no API to create or read one, so this cannot be checked. Create it in the dashboard: Manage Account > Billing > Billable Usage > Create budget alert.",Ht="Alerts > Overview > Create an Alert, alert type Usage Based Billing",b=(e,t,r,o)=>(e.error(r),{code:t,error:r,...o===void 0?{}:{data:o}}),V=(e,t,r)=>{if(t instanceof _)return b(e,et[t.kind],t.message,r);throw t},z=(e,t)=>({accountId:e.accountId,budgetAlert:{checkable:!1,dashboardPath:"Manage Account > Billing > Billable Usage"},limitUnitCaveat:Y,...t===void 0?{}:{eligible:t.discovery.eligible,period:t.period,products:t.discovery.products}}),I=e=>e.toLocaleString("en-US",{maximumFractionDigits:0}),Ie=(e,t)=>{t===!1&&e.warn("This account is not eligible for Usage Based Billing notifications (they are offered to Pay-as-you-go accounts; most Enterprise contracts are not supported).")},ke=e=>({code:u.SUCCESS,data:e}),Yt=/^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/u,Oe=new Set(S.map(e=>e.id)),Kt=e=>Oe.has(e),Vt=e=>{const t={};for(const r of e.flatMap(o=>o.split(","))){const[o="",n=""]=r.split("=").map(s=>s.trim()),a=Number(n);if(!Kt(o))return{error:`--threshold "${r}": unknown metric "${o}" — one of ${[...Oe].join(", ")}.`};if(!Number.isFinite(a)||a<=0)return{error:`--threshold "${r}": the limit must be a positive number.`};t[o]=Math.ceil(a)}return t},zt=e=>{const t=(e.emails??[]).flatMap(s=>s.split(",")).map(s=>s.trim()).filter(s=>s.length>0),r=(e.webhooks??[]).map(s=>s.trim()).filter(s=>s.length>0),o=e.multiplier===void 0?Tt:Number(e.multiplier),n=Vt(e.thresholds??[]);if(t.length===0&&r.length===0)return{error:"alerts setup needs somewhere to send the alerts: pass --email <address> and/or --webhook <destination-id>."};const a=t.find(s=>!Yt.test(s));return a!==void 0?{error:`--email "${a}" is not an email address.`}:!Number.isFinite(o)||o<1?{error:`--multiplier must be a number of at least 1 (got "${e.multiplier??""}").`}:"error"in n?n:{allowFloor:e.allowFloor===!0,emails:t,multiplier:o,replaceRecipients:e.replaceRecipients===!0,thresholds:n,webhooks:r}},Xt={explicit:()=>"set with --threshold",floor:e=>`floor: included monthly allowance; last month ${I(e.lastMonth??0)}`,"floor-usage-unavailable":()=>"floor: usage could not be read, applied with --allow-floor",history:e=>`last month ${I(e.lastMonth??0)}`},Qt=e=>{const t=[...e.added.map(r=>`+${r}`),...e.removed.map(r=>`-${r}`)];return t.length===0?"recipients unchanged":`recipients ${t.join(" ")}`},Zt=e=>{const t=e.productSource==="documented"?"documented id":"offered by the account",r=e.previousProduct===void 0?"":`, product was ${e.previousProduct.join(", ")||"none"}`;return`${e.action.padEnd(9)} ${e.body.name} — product ${e.product} (${t}${r}), limit ${I(e.threshold)} (${Xt[e.basis](e)}); ${Qt(e.recipients)}`},B=e=>S.find(t=>t.id===e)?.label??e,er=(e,t,r,o)=>{o.info(`Usage alerts at ${String(t)}× last month (${r.startDate} to ${r.endDate}), never below the included allowance:`);for(const n of e.alerts)o.info(` ${Zt(n)}`);for(const n of e.skipped)o.info(` skipped ${B(n.metric)} — ${n.reason}`),n.warning!==void 0&&o.warn(`${B(n.metric)}: the existing alert ${n.warning}.`);for(const n of e.orphans)o.warn(`"${n.name??"?"}" (${n.id??"?"}) is named like a \`lunora cloudflare alerts\` policy but matches no metric; it was left alone.`);for(const n of e.duplicates)o.warn(`"${n.name??"?"}" exists more than once; ${n.id??"?"} was left alone — delete the extra one in the dashboard.`);e.skipped.some(n=>n.kind==="unit-unverified"||n.kind==="ambiguous")&&o.info(`To set the skipped ones by hand: ${Ht}.`),o.info(Y),o.info(R)},tr=async(e,t)=>{if(e.yes===!0)return;if(e.confirm===void 0&&(!process.stdin.isTTY||e.format==="json"))return b(e.logger,u.USAGE,"alerts setup: refusing to change notification policies without confirmation — re-run with --yes.");const r=t.flatMap(a=>a.recipients.removed),o=r.length===0?"":` This REMOVES recipients: ${[...new Set(r)].join(", ")}.`;if(!await(e.confirm??xe)(`Apply ${String(t.length)} notification policy change(s)?${o}`))return e.logger.info("alerts setup: aborted"),{code:u.CANCELLED,error:"alerts setup: aborted at the confirmation prompt"}},rr=async(e,t,r,o)=>{const a=(await Promise.all(t.map(async s=>e.notifications(`Reading webhook destination ${s}`,"GET",`/alerting/v3/destinations/webhooks/${encodeURIComponent(s)}`).then(()=>{},i=>({error:i,id:s}))))).find(s=>s!==void 0);if(a!==void 0)return a.error instanceof _&&a.error.kind==="not-found"?b(r,u.USAGE,`--webhook ${a.id}: no such webhook destination on account ${e.accountId}.`,o):V(r,a.error,o)},or=async(e,t)=>{const r=t.action==="create"?await e.notifications(`Creating "${t.body.name}"`,"POST","/alerting/v3/policies",t.body):await e.notifications(`Updating "${t.body.name}"`,"PUT",`/alerting/v3/policies/${encodeURIComponent(t.policyId??"")}`,t.body);return typeof r=="object"&&r!==null&&"id"in r&&typeof r.id=="string"?r.id:t.policyId},nr=async(e,t,r)=>await Promise.all(t.map(async({alert:n,id:a})=>{try{const s=await e.notifications(`Reading back "${n.body.name}"`,"GET",`/alerting/v3/policies/${encodeURIComponent(a)}`),i={enabled:s?.enabled,id:a,limit:s?.filters?.limit,name:s?.name,product:s?.filters?.product};return r.info(`Cloudflare stored "${i.name??n.body.name}": product ${JSON.stringify(i.product)}, limit ${JSON.stringify(i.limit)}.`),JSON.stringify(i.limit)!==JSON.stringify(n.body.filters.limit)&&r.warn(`"${n.body.name}": Cloudflare stored limit ${JSON.stringify(i.limit)}, not the ${JSON.stringify(n.body.filters.limit)} sent.`),i}catch(s){return r.warn(`Could not read "${n.body.name}" back: ${s instanceof Error?s.message:String(s)}`),{id:a}}})),ar=async(e,t,r,o)=>{const n=[],a=[];let s;for(const c of t)try{const d=await or(e,c);n.push({action:c.action,metric:c.metric,...d===void 0?{}:{policyId:d}}),d!==void 0&&a.push({alert:c,id:d}),r.success(`${c.action==="create"?"Created":"Updated"} "${c.body.name}".`)}catch(d){s=d;break}const i={...o,applied:n,stored:await nr(e,a,r)};return s===void 0?i:V(r,s,i)},le=(e,t,r)=>{const o=t.skipped.filter(n=>n.kind==="usage-unavailable");return o.length>0?b(r,u.FAILURE,`alerts setup: no alert was set for ${o.map(n=>B(n.metric)).join(", ")} because last month's usage could not be read — pass --threshold <metric>=<n>, or --allow-floor to accept the floor.`,e):{code:u.SUCCESS,data:e}},sr=async(e,t,r,o)=>{const{logger:n}=r,a=z(e,t),s=await rr(e,o.webhooks,n,a);if(s!==void 0)return s;if(t.discovery.eligible===!1)return Ie(n,!1),n.info(R),b(n,u.PERMISSION,"alerts setup: the account cannot create Usage Based Billing notifications.",a);const i=xt({...o,policies:t.policies,products:t.discovery.products,usage:t.usage});a.plan=i,er(i,o.multiplier,t.period,n);const c=i.alerts.filter(l=>l.action!=="unchanged");if(r.dryRun===!0||c.length===0)return n.info(r.dryRun===!0?"Dry run: nothing was changed.":"Every alert is already up to date."),le(a,i,n);const d=await tr(r,c);if(d!==void 0)return{...d,data:a};const f=await ar(e,c,n,a);return"code"in f?f:le(f,i,n)},ir=e=>e.status!=="ok"?`unavailable (${e.reason})`:e.unit==="microseconds"?`${I(e.value)} µs`:I(e.value),cr=(e,t,r)=>t?"no-product":e===void 0?"unknown":r.some(o=>o.enabled)?"active":r.length>0?"disabled-only":"none",ue=e=>e.map(t=>`"${t.name??t.id??"?"}" (limit ${JSON.stringify(t.limit)})${t.enabled?"":" — disabled, not protecting you"}`).join(", "),dr={active:e=>`alert: ${ue(e.alerts)}`,"disabled-only":e=>`alert: none active; ${ue(e.alerts)}`,"no-product":()=>"alert: none possible — Cloudflare has no usage alert for this; the budget alert is its guard",none:()=>"alert: none",unknown:()=>"alert: unknown (several offered products could mean this)"},lr=(e,t,r)=>{const o=Ee(e.id,t.discovery.products),n=o.status==="matched"?o.product:void 0,a=(n===void 0?[]:r.filter(s=>Array.isArray(s.filters?.product)&&s.filters.product.includes(n.id))).map(s=>({enabled:s.enabled===!0,id:s.id,limit:s.filters?.limit,name:s.name}));return{alerts:a,coverage:cr(n?.id,o.status==="no-product",a),id:e.id,label:e.label,...n===void 0?{}:{product:n.id,productSource:n.source},usage:e}},ur=(e,t,r)=>{const o=t.policies.filter(a=>a.alert_type===A),n=t.usage.map(a=>lr(a,t,o));r.info(`Cloudflare account ${e.accountId} — usage ${t.period.startDate} to ${t.period.endDate}`);for(const a of n)r.info(` ${a.label}: ${ir(a.usage)} — ${dr[a.coverage](a)}`);return r.info(`Usage Based Billing notifications on the account: ${String(o.length)} (${String(o.filter(a=>a.enabled===!0).length)} enabled).`),r.info(Y),Ie(r,t.discovery.eligible),r.info(R),ke({...z(e,t),metrics:n})},fr=e=>{const t=J(e);return t===void 0?void 0:W(t).parsed?.account_id},mr=e=>{const{accountId:t,token:r}=H(e.environment??process.env,fr(e.cwd));return r===void 0?b(e.logger,u.AUTH,`CLOUDFLARE_API_TOKEN is not set. \`lunora cloudflare alerts\` calls the Cloudflare API with an API token (a wrangler login session is not reused); create one with the "${E.notificationsWrite}" and "${E.analytics}" permissions ("${E.notificationsRead}" is enough for status and test).`):t===void 0?b(e.logger,u.USAGE,"No Cloudflare account: set CLOUDFLARE_ACCOUNT_ID or `account_id` in wrangler.jsonc."):at({accountId:t,token:r,...e.fetch===void 0?{}:{fetch:e.fetch}})},Ce=async e=>{const t=await e.notifications("Listing notification policies","GET","/alerting/v3/policies");return Array.isArray(t)?t:[]},hr=async(e,t)=>{const r=wt(t.now??new Date),{logger:o}=t,[n,a,s]=await Promise.all([Ce(e),e.notifications("Listing the alert types this account is eligible for","GET","/alerting/v3/available_alerts").then(i=>({ok:!0,value:i}),i=>({error:i,ok:!1})),Et(e,r).catch(i=>{const c=i instanceof Error?i.message:String(i);return o.warn(`Usage could not be read: ${c}`),S.map(({id:d,label:f})=>({id:d,label:f,reason:c,status:"unavailable"}))})]);return a.ok||o.warn(`Could not read which alert types the account is eligible for: ${a.error instanceof Error?a.error.message:String(a.error)}`),{discovery:ft(a.ok?a.value:void 0,a.ok,n),period:r,policies:n,usage:s}},gr=async(e,t)=>{const r=await Ce(e),o=await Wt(e,r,t.webhooks??[],t.logger,t.now??new Date);return t.logger.info(R),ke({...z(e),deliveries:o})},pr=async e=>{const t=e.subcommand==="setup"?zt(e):void 0;if(t!==void 0&&"error"in t)return b(e.logger,u.USAGE,t.error);const r=mr(e);if(!("graphql"in r))return r;try{if(e.subcommand==="test")return await gr(r,e);const o=await hr(r,e);return t===void 0?ur(r,o,e.logger):await sr(r,o,e,t)}catch(o){return V(e.logger,o)}},fe=new Set(["build","delete","images","info","list","push"]),yr=new Set(["build","push"]),me=new Set(["images list","info","list"]),wr=async e=>{const[t,...r]=e.argument;if(t===void 0||!fe.has(t)){const l=`lunora cloudflare containers requires a subcommand: ${[...fe].toSorted((h,g)=>h.localeCompare(g)).join(" | ")}. Example: lunora cloudflare containers build ./containers/app --tag app:v1 --push`;return e.logger.error(l),{code:u.USAGE,error:l}}const o=e.format==="json",n=t==="images"?`images ${r[0]??""}`.trim():t;if(o&&!me.has(n)){const l=`containers ${n}: --format json is only available for the read subcommands (${[...me].toSorted((h,g)=>h.localeCompare(g)).join(" | ")}) — wrangler has no JSON rendering for the rest.`;return e.logger.error(l),{code:u.USAGE,error:l}}if(yr.has(t)&&!(e.dockerAvailable??qe)()){const l=`containers ${t} needs a running Docker-compatible engine (it builds/pushes images locally). Start Docker or Colima and retry. Note: container images must target linux/amd64.`;return e.logger.error(l),{code:u.MISSING_DEPENDENCY,error:l}}const a=["containers",t,...r];o&&a.push("--json"),e.tag!==void 0&&a.push("--tag",e.tag),e.push===!0&&a.push("--push"),e.env!==void 0&&a.push("--env",e.env);const s=e.cwd??process.cwd(),i=$e(Ae(s),"wrangler",a),c={args:i.args,command:i.command,cwd:s};return e.logger.info(`running ${c.command} ${c.args.join(" ")}`),{code:(await(e.spawner??be)(c)).code,delegated:o,descriptor:c}},k=(e,t)=>(t!==void 0&&e.push("--env",t),e),vr=e=>{const t=k(["deployments","list"],e.env);return e.format==="json"&&t.push("--json"),t},br=e=>{switch(e.subcommand){case"inspect":return e.versionId===void 0?{error:"deployments inspect requires a version id. Usage: lunora cloudflare deployments inspect <version-id>"}:{args:k(["versions","view",e.versionId],e.env)};case"list":return{args:vr(e)};case"promote":{if(e.versionId===void 0)return{error:"deployments promote requires a version id. Usage: lunora cloudflare deployments promote <version-id> --yes"};if(!e.yes)return{error:"deployments promote shifts 100% of live traffic. Re-run with --yes to confirm."};const t=k(["versions","deploy",`${e.versionId}@100%`,"--yes"],e.env);return e.message!==void 0&&t.push("--message",e.message),{args:t}}case"rollback":{if(!e.yes)return{error:"deployments rollback changes the live version. Re-run with --yes to confirm."};const t=k(["rollback"],e.env);return e.versionId!==void 0&&t.push(e.versionId),t.push("--yes"),e.message!==void 0&&t.push("--message",e.message),{args:t}}default:return{error:`deployments: unknown subcommand "${e.subcommand}"`}}},$r=async e=>{if(e.format==="json"&&e.subcommand!=="list"){const d=`deployments ${e.subcommand}: --format json is only available for \`deployments list\` — wrangler has no JSON rendering for the others.`;return e.logger.error(d),{code:u.USAGE,descriptor:void 0,error:d}}const{args:t,error:r}=br(e);if(r!==void 0||t===void 0)return e.logger.error(r??"deployments: nothing to run"),{code:u.USAGE,descriptor:void 0,error:r};const{logger:o}=e,n=e.cwd??process.cwd(),a=$e(Ae(n),"wrangler",t),s={args:a.args,command:a.command,cwd:n};return o.info(`${s.command} ${s.args.join(" ")}`),{code:(await(e.spawner??be)(s)).code,delegated:e.format==="json",descriptor:s}},Ar=e=>e==="list"||e==="inspect"||e==="rollback"||e==="promote",Sr="https://api.cloudflare.com/client/v4/accounts",Ne="Workers Scripts Read",he=1e3,ge=5e4,_r=1e4,Er=3e4,pe=[31,139],ye=["cpu","heap"],Tr=/^[\da-f]{64}$/iu,v=(e,t,r)=>(e.error(r),{code:t,error:r}),we=e=>{try{const t=JSON.parse(e);if(Array.isArray(t.errors)){const r=t.errors.map(o=>typeof o=="object"&&o!==null&&"message"in o?String(o.message):"").filter(o=>o.length>0);if(r.length>0)return L(r.join("; "))}}catch{}return L(e.trim())},Ir=e=>{switch(e){case 401:case 403:return` — check that CLOUDFLARE_API_TOKEN is valid and has the "${Ne}" permission on this account`;case 404:return" — check the worker name, version, and (for a Durable Object) the namespace and actor ids";case 429:return" — rate limited; wait and retry";default:return""}},kr=e=>{const t=e.type??"cpu";if(!ye.includes(t))return{error:`profile: invalid --type "${e.type??""}" — expected ${ye.join(" | ")}`};const r=e.durationMs===void 0?_r:Number(e.durationMs);return!Number.isInteger(r)||r<he||r>ge?{error:`profile: invalid --duration-ms "${e.durationMs??""}" — expected an integer from ${String(he)} to ${String(ge)}`}:e.namespaceId===void 0!=(e.actorId===void 0)?{error:"profile: --namespace-id and --actor-id must be passed together to profile a Durable Object"}:e.actorId!==void 0&&!Tr.test(e.actorId)?{error:"profile: --actor-id must be a 64-character hexadecimal Durable Object id"}:{durationMs:r,profileType:t}},Or=e=>{const{logger:t}=e,r=J(e.cwd),o=r===void 0?void 0:W(r).parsed,{accountId:n,token:a}=H(e.environment??process.env,o?.account_id);if(a===void 0)return v(t,u.AUTH,`CLOUDFLARE_API_TOKEN is not set. \`lunora cloudflare profile\` calls the Cloudflare API with an API token (a wrangler login session is not reused); create one with the "${Ne}" permission.`);if(n===void 0)return v(t,u.USAGE,"No Cloudflare account: set CLOUDFLARE_ACCOUNT_ID or `account_id` in wrangler.jsonc.");const s=typeof o?.name=="string"&&o.name.length>0?o.name:void 0,i=e.worker??s;if(i===void 0)return v(t,u.USAGE,"profile: no Worker name — pass one, or set `name` in wrangler.jsonc.");const c=e.env===void 0?void 0:o?.env?.[e.env]?.name;let d=i;return e.worker===void 0&&e.env!==void 0&&(d=typeof c=="string"&&c.length>0?c:`${i}-${e.env}`),{accountId:n,sourceMapsMissing:o!==void 0&&o.upload_source_maps!==!0,token:a,worker:d}},Cr=(e,t)=>{const r=`${e}.${String(process.pid)}.tmp`;try{Re(Je(e),{recursive:!0}),ve(r,t),Me(r,e);return}catch(o){try{je(r,{force:!0})}catch{}return o instanceof Error?o.message:String(o)}},Nr=async(e,{accountId:t,token:r,worker:o},{durationMs:n,profileType:a,version:s})=>{const{logger:i}=e,c={duration_ms:n,profile_type:a,...e.namespaceId===void 0||e.actorId===void 0?{}:{actor_id:e.actorId,namespace_id:e.namespaceId}},d=`${Sr}/${encodeURIComponent(t)}/workers/workers/${encodeURIComponent(o)}/versions/${encodeURIComponent(s)}/profile`,f=e.fetch??globalThis.fetch.bind(globalThis);let l,h;try{l=await f(d,{body:JSON.stringify(c),headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},method:"POST",signal:AbortSignal.timeout(n+Er)}),h=new Uint8Array(await l.arrayBuffer())}catch(g){return v(i,u.UNAVAILABLE,`profile failed: ${g instanceof Error?g.message:String(g)}.`)}if(!l.ok){const g=we(new TextDecoder().decode(h));return v(i,j(l.status),`profile failed (HTTP ${String(l.status)})${g.length>0?`: ${g}`:""}${Ir(l.status)}.`)}if(h[0]!==pe[0]||h[1]!==pe[1]){const g=we(new TextDecoder().decode(h));return v(i,u.FAILURE,`profile failed: the API answered 200 with something that is not a gzip profile${g.length>0?`: ${g}`:""}.`)}return{bytes:h}},Ur=async e=>{const{logger:t}=e,r=kr(e);if("error"in r)return v(t,u.USAGE,r.error);const o=Or(e);if("code"in o)return o;const{durationMs:n,profileType:a}=r,{worker:s}=o,i=e.version??"latest";t.info(`Profiling ${s}@${i} (${a}) for ${String(n)} ms — keep traffic flowing to it…`);const c=await Nr(e,o,{durationMs:n,profileType:a,version:i});if("code"in c)return c;const{bytes:d}=c,f=(e.now??new Date).toISOString().replaceAll(/[:.]/gu,"-"),l=Be(e.cwd,e.out??`${s}-${a}-${f}.pprof.gz`),h=Cr(l,d);return h!==void 0?v(t,u.FAILURE,`profile failed: could not write ${l}: ${h}.`):(t.success(`Wrote ${String(d.byteLength)} bytes to ${l}`),t.info("Open it with `go tool pprof -http=: <file>` (pprof reads gzip directly) or upload it to a flamegraph viewer."),o.sourceMapsMissing&&t.warn("wrangler config has no `upload_source_maps: true` — TypeScript function names in the profile may be mangled."),{code:u.SUCCESS,data:{bytes:d.byteLength,durationMs:n,file:l,profileType:a,version:i,worker:s}})},Dr={ai:{absorbs:"gateway",tool:"ai-gateway"},alert:{tool:"alerts"}},Rr=(e,t)=>{const r=Dr[e];if(r!==void 0)return{rest:r.absorbs!==void 0&&t[0]===r.absorbs?t.slice(1):t,tool:r.tool};const o=O.find(n=>n.name===e)?.name;return o===void 0?void 0:{rest:t,tool:o}},Mr={celld:"celld",node:"Node"},jr=e=>e==="status"||e==="setup"||e==="test",T=(e,t,r)=>(e.error(r),{code:t,error:r}),Lr=e=>{const t=Math.max(...O.map(r=>r.name.length));e.info("lunora cloudflare <tool> — tools for an app deployed to your own Cloudflare account:");for(const r of O)e.info(` ${r.name.padEnd(t)} ${r.summary}`),r.usage!==""&&e.info(` ${" ".repeat(t)} lunora cloudflare ${r.name} ${r.usage}`);return e.info("Run `lunora cloudflare --help` for every tool's arguments and options."),{code:0}},Pr=(e,t,r)=>{const{error:o,target:n}=De(e,r);return n===void 0?o??"no deploy target":n==="cloudflare"?void 0:`${r===void 0?`this project deploys to ${Mr[n]??`"${n}"`}`:`\`--target ${n}\``} — \`lunora cloudflare ${t}\` only applies to Cloudflare.`},Gr=(e,{logger:t,rest:r})=>r.length===0?void 0:T(t,u.USAGE,`cloudflare ${e}: takes no arguments, got "${r.join(" ")}"`),Fr=async e=>{const{cwd:t,logger:r,options:o}=e;return Gr("ai-gateway",e)??Xe({cwd:t,dryRun:o.dryRun===!0,id:o.id,logger:r,logs:o.logs})},xr=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const a=n[0]??"status";return jr(a)?pr({allowFloor:o.allowFloor===!0,cwd:e,dryRun:o.dryRun===!0,format:t,logger:r,replaceRecipients:o.replaceRecipients===!0,subcommand:a,yes:o.yes===!0,...o.email===void 0?{}:{emails:o.email},...o.webhook===void 0?{}:{webhooks:o.webhook},...o.multiplier===void 0?{}:{multiplier:o.multiplier},...o.threshold===void 0?{}:{thresholds:o.threshold}}):T(r,u.USAGE,`cloudflare alerts: unknown subcommand "${a}" — expected status | setup | test`)},qr=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const a=await wr({argument:n,cwd:e,env:o.env,format:t,logger:r,push:o.push===!0,tag:o.tag});return{code:a.code,delegated:a.delegated,error:a.error}},Br=async({cwd:e,format:t,logger:r,options:o,rest:n})=>{const[a,s]=n;if(!Ar(a))return T(r,u.USAGE,`cloudflare deployments: unknown subcommand "${a??""}" — expected list | inspect | rollback | promote`);const i=await $r({cwd:e,env:o.env,format:t,logger:r,message:o.message,subcommand:a,versionId:s,yes:o.yes===!0});return{code:i.code,delegated:i.delegated,error:i.error}},Jr=async({cwd:e,logger:t,options:r,rest:o})=>o.length>1?T(t,u.USAGE,`cloudflare profile: takes at most one argument (the worker), got "${o.join(" ")}"`):Ur({actorId:r.actorId,cwd:e,durationMs:r.durationMs,env:r.env,logger:t,namespaceId:r.namespaceId,out:r.out,type:r.type,version:r.versionId,worker:o[0]}),Wr={"ai-gateway":Fr,alerts:xr,containers:qr,deployments:Br,profile:Jr},so=Ue(async({argument:e,cwd:t,format:r,logger:o,options:n})=>{const[a,...s]=e;if(a===void 0||a==="")return Lr(o);const i=Rr(a,s);if(i===void 0)return T(o,u.USAGE,`cloudflare: unknown tool "${a}" — expected ${O.map(f=>f.name).join(" | ")}`);const{tool:c}=i,d=Pr(t,c,n.target);return d!==void 0?T(o,u.USAGE,d):Wr[c]({cwd:t,format:r,logger:o,options:n,rest:i.rest})});export{so as execute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as m}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as n}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{I as u,r as i}from"../packem_shared/import-
|
|
1
|
+
import{d}from"../packem_shared/command-CB4JM-Jl.mjs";import{EXIT_CODE as m}from"../packem_shared/EXIT_CODE-D_XCixv8.mjs";import{a as n}from"../packem_shared/resolve-target-C_ZloTvd.mjs";import{I as u,r as i}from"../packem_shared/import-ZdIj7W66.mjs";const b=d(({argument:f,cwd:a,format:c,logger:t,options:r})=>{const o=f[0];if(!o){const e="import requires a path. Usage: lunora import <file.ndjson | convex-export-dir> [--table <name>]";return t.error(e),{code:m.USAGE,error:e}}if(r.from!==void 0&&!u.includes(r.from)){const e=`--from ${r.from} is not a known source. Expected one of: ${u.join(", ")}.`;return t.error(e),{code:m.USAGE,error:e}}return i({batchSize:r.batchSize,cwd:a,file:o,format:c,from:r.from,logger:t,prod:r.prod===!0,scan:r.scan===!0,storageDir:r.storageDir,table:r.table,token:r.token,url:n({cwd:a,prod:r.prod===!0,url:r.url}),verify:r.verify===!0,withStorage:r.withStorage===!0,yes:r.yes===!0})});export{b as execute};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as E,R as M,V as a,r as s}from"./cli-
|
|
1
|
+
import{C as E,R as M,V as a,r as s}from"./cli-BGHRPHBJ.mjs";import"./cli-manifest-DoQ_3bzs.mjs";import"./EXIT_CODE-D_XCixv8.mjs";import"./createLogger-4TujevyB.mjs";import"./render-lunora-error-B6893vbU.mjs";export{E as COMMANDS,M as REGISTERED_COMMAND_NAMES,a as VERSION,s as runCli};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./admin-token-VdUnvnKW.mjs";import"./admin-url-DJB8P6Jh.mjs";import"./EXIT_CODE-D_XCixv8.mjs";import{D as a,r as I}from"./import-
|
|
1
|
+
import"./admin-token-VdUnvnKW.mjs";import"./admin-url-DJB8P6Jh.mjs";import"./EXIT_CODE-D_XCixv8.mjs";import{D as a,r as I}from"./import-ZdIj7W66.mjs";import"./shared-D-zCOmgY.mjs";export{a as DEFAULT_IMPORT_BATCH_SIZE,I as runImportCommand};
|