@emuanalytics/flow-cli 2.2.1 → 2.2.2

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.
Files changed (2) hide show
  1. package/dist/index.mjs +1 -1
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -24,4 +24,4 @@ ${Co(e)}
24
24
  `)}function Co(e){const i=e.docs;return i?`
25
25
  ${b.dim("Further reading:")}
26
26
 
27
- ${b.dim("-")} ${b.cyan(i)}`:""}function Uo(e){let i=me.get(e);return i||(i=Jo(e),i.catch(a=>{c(a)}),me.set(e,i)),i}async function Jo(e){const i=process.cwd(),a=I(i,".tmp-flow-starter"),t=await Ao(e);return le(a),await No(t,a),await Eo("install",a),async n=>{const o=I(i,n);await Mo(a,o),await Le({files:[I(o,"*"),I(o,"config/**"),I(o,"src/**")],from:/flow-starter-project-name/g,to:n,allowEmptyPaths:!0}),le(null)}}function Ro(e){return!/[^a-zA-Z0-9-]/.test(e)}function Fo(e,i){if(e.includes("/"))return{name:e,repo:e};const a=i.find(t=>t.name===e);if(!a)throw new Error(`Starter "${e}" does not exist.`);return a}const Ko=["new [name]"],zo="Create a new Flo.w app",Zo=e=>e.option("s",{alias:"starter",describe:"Starter template",type:"string"}).positional("name",{describe:"Project name",type:"string"});async function Go(e){try{const i=await fetch(`${re}/index.json`).then(t=>t.json());if(!e.starter){const t=await f.prompt([{type:"select",name:"starter",message:"Starter template",choices:i.map(n=>n.name),default:i[0].name}]);e.starter=t.starter}const a=Fo(e.starter,i);await Lo(a,e.name,!1)}catch(i){c(i.message)}}const Vo={__proto__:null,builder:Zo,command:Ko,desc:zo,handler:Go},Ho=["cache [id]"],Bo="Show pipeline cache",Wo=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function Qo({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="cache").sort((s,d)=>s.index-d.index),o=n.map(s=>({id:s.id,type:s.type,description:s.description,related_components:s.metadata.components.join()}));if(a==="json"){r({id:t.id,environment:t.environment,caches:n},"json");return}o.length?r(o,"table",null,{noWrap:!0}):v("No Caches Found")}catch(t){c(t)}}const Xo={__proto__:null,builder:Wo,command:Ho,desc:Bo,handler:Qo},Yo=["components <id>","comps <id>"],es="Show pipeline components",ts=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function is({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=t.components??[];if(a==="json"){r(n,"json");return}const o=as(n);if(!o.length){v("No Components Found");return}r(o,"table",["id","class","type","description","note"])}catch(t){c(t)}}function as(e){if(!e.length)return[];const i={};e.forEach(s=>i[s.id]={...s,next:[],previous:[],indent:0}),Object.values(i).forEach(s=>{const d=s.metadata||{},l=d.previousComponents??[],m=d.nextComponents??[];s.previous=l.map(u=>i[u]),s.next=m.map(u=>i[u])});const a={};Object.values(i).forEach(s=>{const d=(s.metadata||{}).parentComponent;d&&(a[d]||(a[d]=[]),a[d].push(s))}),Object.values(a).forEach(s=>{s.sort((d,l)=>d.index-l.index)});const t=Object.values(i).filter(s=>s.class!=="cache"&&(s.previous??[]).length===0),n=new Set;function o(s,d){if(!s||n.has(s.id))return;n.add(s.id),s.indent=d,s.class==="pipeline"&&(a[s.id]??[]).forEach(u=>o(u,d+1));const l=(s.next??[]).filter(u=>u.class!=="cache"),m=s.type==="fp:fork"||l.length>1;l.forEach(u=>o(u,m?d+1:d))}return t.forEach(s=>o(s,0)),Object.values(i).filter(s=>s.class!=="cache").sort((s,d)=>s.index-d.index).map(s=>{const d=[],l=(s.metadata||{}).caches??[];l.length>0&&d.push(`uses cache: ${l.join(", ")}`);const m=s.indent>0?" ".repeat(s.indent)+"> ":"";function u(y){return y.class==="pipeline"?`(${y.id})`:y.type==="fp:fork"?`[${y.id}]`:y.id}return{id:m+u(s),class:s.class,type:s.type,description:s.description,note:d.join()}})}const ns={__proto__:null,builder:ts,command:Yo,desc:es,handler:is},os=["config [id]"],ss="Show pipeline config",rs=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function ds({client:e,id:i,format:a}){try{const t=(await p(i,e.pipelines,"id"))?.config;if(!t||Object.keys(t).length===0){r({message:`No configuration found for pipeline ${i}`},a??"json");return}const n=X(t);if(a==="json"){r(t,"json");return}const o=Object.entries(n).map(([s,d])=>({key:s,value:d}));r(o,"table",["key","value"],{noWrap:!0})}catch(t){c(t)}}function ue(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function X(e,i="",a={}){for(const[t,n]of Object.entries(e)){const o=i?`${i}.${t}`:t;Array.isArray(n)?n.forEach((s,d)=>{const l=`${o}[${d}]`;ue(s)?X(s,l,a):a[l]=s}):ue(n)?X(n,o,a):a[o]=n}return a}const cs={__proto__:null,builder:rs,command:os,desc:ss,handler:ds},ls=["list [id]","ls","$0"],ps="List pipelines",ms=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).option("filter-cache",{type:"string",describe:"Filter pipelines using a specific cache ID (partial match)"}).option("filter-dataset",{type:"string",describe:"Filter pipelines using a specific dataset ID (partial match)"}).option("components",{type:"boolean",describe:"Include pipeline components in the JSON output"}).positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"}).conflicts("id",["limit","sort","filter-cache","filter-dataset"]);async function us({client:e,id:i,format:a,limit:t,sort:n,filterCache:o,filterDataset:s,components:d}){try{if(n??="id",i){const y=await p(i,e.pipelines,"id",void 0,n),x=fe(y);a==="table"?r(ye(x),"table",null,{noWrap:!0}):r(x,a);return}const l=!!o||!!s,m=d||l,u=(await e.pipelines.list({sort:n,limit:l?void 0:t,join:m?"components":void 0})).filter(y=>fs(y,o)).filter(y=>ys(y,s)).slice(0,l?t:void 0).map(y=>d?y:bs(y));gs(u,a)}catch(l){c(l)}}function ys(e,i){return!i||ie(e.components).some(a=>a.class==="sink"&&a.type==="fp:flow-ingest-stream"&&a.config?.datasetId?.includes(i))}function fs(e,i){return!i||ie(e.components).some(a=>a.class==="cache"&&a.config?.id?.includes(i))}function bs({components:e,...i}){return i}function gs(e,i){const a=e.map(fe);i==="table"?r(a.map(ye),"table",["id","environment","version","description","startedAt","heartbeatAt"]):r(a,i??"json")}function ye(e){const i=e.heartbeatAt&&(Date.now()-new Date(e.heartbeatAt).getTime())/1e3<30;return{...e,heartbeatAt:{color:i?"06a453":"e11529",value:e.heartbeatAt}}}function fe(e){const i=e.metadata.version?.tag;return{...e,version:i&&i.replace(/^release-/,"")}}const hs={__proto__:null,builder:ms,command:ls,desc:ps,handler:us},ws=["sink [id]"],$s="Show pipeline sink",_s=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function xs({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="sink").sort((s,d)=>s.index-d.index),o=n.reduce((s,d)=>(s[d.type]??=[],s[d.type].push(d),s),{});if(a==="json"){r({id:t.id,environment:t.environment,sinks:n},"json");return}if(Object.keys(o).length)for(const[,s]of Object.entries(o)){const d=s.map(l=>({id:l.id,description:l.description,...vs(l)}));r(d,"table")}else v("No Sinks Found")}catch(t){c(t)}}function vs(e){const i=e.config||{};switch(e.type){case"fp:amqp-writable-stream":return{vhost:i.vhost??"N/A",exchange:typeof i.exchange=="string"?i.exchange:i.exchange?.name??"N/A",enable:i.enable};case"fp:flow-ingest-stream":return{datasetId:i.datasetId??"N/A",enable:i.enable};case"fp:sqs-writable-stream":return{queueUrl:i.queueUrl??"N/A",region:i.region??"N/A",enable:i.enable};case"fp:sns-writable-stream":return{topicArn:i.topicArn??"N/A",region:i.region??"N/A",enable:i.enable};case"fp:null-sink":return{logging:i.logging};default:return{}}}const Ss={__proto__:null,builder:_s,command:ws,desc:$s,handler:xs},js=["source [id]"],Ds="Show pipeline source",ks=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function As({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="source").sort((s,d)=>s.index-d.index),o=n.map(s=>({id:s.id,type:s.type,description:s.description,...Ns(s)}));if(a==="json"){r({id:t.id,environment:t.environment,sources:n},"json");return}o.length?r(o,"table"):v("No Sources Found")}catch(t){c(t)}}function Ns(e){const i=e.config||{};switch(e.type){case"fp:amqp-readable-stream":return{vhost:i.vhost??"N/A",exchange:i.queue?.bindTo?.exchange??i.exchange??"N/A",queue:typeof i.queue=="string"?i.queue:i.queue?.name??"N/A"};case"fp:mqtt-subscriber-stream":return{serverUrl:i.serverUrl??"N/A",topic:i.topic??"N/A"};case"fp:flow-dataset-stream":return{datasetId:i.datasetId??"N/A"};case"fp:db-timeseries-stream":return{database:i.database??"N/A",table:i.table??"N/A"};case"fp:sqs-readable-stream":return{queueUrl:i.queueUrl??"N/A",region:i.region??"N/A"};case"fp:s3-file-list-stream":return{bucket:i.bucket??"N/A"};case"fp:s3-file-loader-stream":return{bucket:i.bucket??"N/A"};case"fp:planefinder-firehose-stream":return{host:i.host??"N/A"};case"fp:tcp-tls-readable-stream":return{hostname:i.hostname??"N/A"};case"fp:timer-stream":return{interval:i.interval??"N/A"};default:return{}}}const Is={__proto__:null,builder:ks,command:js,desc:Ds,handler:As},Ps=["pipelines <command>","pipes"],Os="Pipeline commands",Es=e=>e.command(hs).command(cs).command(ns).command(Is).command(Ss).command(Xo).demandCommand(1),qs={__proto__:null,builder:Es,command:Ps,desc:Os},Ts="create <id>",Ms="Create a new map style",Ls=e=>e.positional("id",{describe:"Map style id",type:"string"}).option("name",{alias:"n",describe:"Name for new style",type:"string"}).option("description",{alias:"d",describe:"Description for new style",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("sourceId",{alias:"sid",describe:"ID of source style",type:"string",default:"darkmatter"}).option("sourceKey",{alias:"skey",describe:"API key to access source style",type:"string",default:"6f19c5c7-1a9b-425b-84ab-09ba56e905b2"}),Cs=async e=>{try{const i=[].concat(await be(e.host,e.apiKey),await be(e.host,e.sourceKey)),a=i.map(o=>({name:o.name,value:o,short:o.name}));let t=i.find(o=>o.id===e.sourceId);if(e.interactive){const o=await f.prompt([{type:"input",name:"name",message:"Name for new style",default:e.name},{type:"input",name:"description",message:"Description for new style",default:e.description},{type:"select",name:"sourceStyle",message:"Select the source style to copy",choices:a,default:t}]);e.name=o.name,e.description=o.description,e.sourceId=o.sourceStyle.id,t=i.find(s=>s.id===e.sourceId)}t.style.id=e.id,t.style.name=e.name,t.style.metadata.description=e.description;let n={id:e.id,name:e.name,description:e.description,type:"plain",style:t.style};n=await e.client.styles.create(n),e.format==="table"?r(n,"table",["id","name","description","type"]):r(n,e.format??"json")}catch(i){c(i)}};async function be(e,i){return new Z(e,i).styles.list()}const Us={__proto__:null,builder:Ls,command:Ts,desc:Ms,handler:Cs},Js=["delete <id>","rm","del"],Rs="Delete a map style",Fs=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Map style id (or unique prefix)",type:"string"});async function Ks({client:e,id:i,yes:a}){try{const t=await p(i,e.styles);let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.id}'`,default:!1})).confirmed),n&&(await e.styles.delete(t.id),w("Deleted"))}catch(t){c(t)}}const zs={__proto__:null,builder:Fs,command:Js,desc:Rs,handler:Ks},Zs=D.dirname(Ee(import.meta.url)),$=G();$.use(G.json({limit:"1mb"})),$.use(G.static(D.join(Zs,"../maputnik"))),$.get("/styles",(e,i)=>{v(":thumbsup: Maputnik connected");const a=$.get("style");i.json([a.id])}),$.get("/styles/:id",async(e,i)=>{try{const a=$.get("style");v(`Loading style '${a.id}'`),i.json(a)}catch(a){c(a.message)}}),$.put("/styles/:id",async(e,i)=>{try{const a=$.get("client");if($.set("style",e.body),!$.get("writeable"))return i.status(204).send();const t={...e.body};t.sources=Gs(t.sources,`${a.baseUrl}`),t.glyphs=M(t.glyphs,a.baseUrl),t.sprite=M(t.sprite,a.baseUrl),await Hs(a,t.id,t),i.status(204).send()}catch(a){c(a.message)}});function Gs(e,i){return Object.keys(e).reduce((a,t)=>{let n=e[t];return Object.prototype.hasOwnProperty.call(n,"url")&&(n={...n,url:M(n.url,i)}),Object.prototype.hasOwnProperty.call(n,"data")&&(n={...n,data:M(n.data,i)}),Object.prototype.hasOwnProperty.call(n,"tiles")&&(n={...n,tiles:n.tiles.map(o=>M(o,i))}),a[t]=n,a},{})}function M(e,i){const a=new URL(e,i);return a.searchParams.delete("apikey"),a.toString().replace(i,"").replace(/%7B/g,"{").replace(/%7D/g,"}")}function Vs(e,i,a){return e.styles.update(i,{style:a})}const Hs=Ue(Vs,2e3,{leading:!0});class Y{server;port;constructor(i,a,t){this.port=this.normalizePort(process.env.PORT||"8000"),$.set("port",this.port),$.set("style",a),$.set("writeable",t),$.set("client",i),this.server=Ce.createServer($),this.server.listen(this.port),this.server.on("error",this.onError.bind(this)),this.server.on("listening",this.onListening.bind(this))}normalizePort(i){const a=parseInt(i,10);return isNaN(a)?i:a>=0?a:!1}onError(i){if(i.syscall!=="listen")throw i;const a=typeof this.port=="string"?`Pipe ${this.port}`:`Port ${this.port}`;switch(i.code){case"EACCES":c(`${a} requires elevated privileges`),process.exit(1);break;case"EADDRINUSE":c(`${a} is already in use`),process.exit(1);break;default:throw i}}onListening(){w(`Style server listening on port ${this.port}`),v(`:rocket: Point your browser at http://localhost:${this.port}`),this.postListen()}postListen(){return Promise.resolve(null)}}const Bs="edit <id>",Ws="Edit a map style in maputnik",Qs=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),Xs=async({client:e,id:i})=>{try{const a=await p(i,e.styles),t=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,t,!0),V("http://localhost:8000")}catch(a){c(a)}},Ys={__proto__:null,builder:Qs,command:Bs,desc:Ws,handler:Xs},er=["list [id]","ls","$0"],tr="List map styles",ir=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("id",["limit","sort"]).positional("id",{describe:"Map style id (or unique prefix)",type:"string"});async function ar({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.styles);r(o,a)}else{const o=await e.styles.list({limit:t,sort:n});a==="table"?r(o,"table",["id","name","description","type"]):r(o,a??"json")}}catch(o){c(o)}}const nr={__proto__:null,builder:ir,command:er,desc:tr,handler:ar},or=["metadata <id>","meta"],sr="Get/set resource metadata",rr=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function dr({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.styles);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.styles.deleteMetadata(t.id,o):await e.styles.setMetadata(t.id,o,s)}t=await p(i,e.styles),r(t.metadata,"json")}else{const t=await p(i,e.styles);r(t.metadata,"json")}}catch(t){c(t)}}const cr={__proto__:null,builder:rr,command:or,desc:sr,handler:dr},lr="view <id>",pr="View a map style in maputnik",mr=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),ur=async({client:e,id:i})=>{try{const a=await p(i,e.styles),t=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,t,!1),V("http://localhost:8000")}catch(a){c(a)}},yr={__proto__:null,builder:mr,command:lr,desc:pr,handler:ur},fr="styles <command>",br="Style commands",gr=e=>e.command(nr).command(Us).command(zs).command(Ys).command(yr).command(cr).demandCommand(1),hr={__proto__:null,builder:gr,command:fr,desc:br},wr=["create <id>"],$r="Create a new tile source",_r=e=>e.option("type",{alias:"t",description:"Tilesource type",type:"string",choices:["tilelive","dataset"]}).option("source",{alias:"s",description:"Tilesource source (tilelive URL or dataset ID)",type:"string"}).option("attribution",{alias:"a",description:"Tilesource attribution",type:"string"}).option("minZoom",{alias:"m",description:"Minimum zoom",type:"number"}).option("maxZoom",{alias:"M",description:"Maximum zoom",type:"number"}).option("extent",{alias:"e",description:"Extent x1,y1,x2,y2 (lng/lat)",type:"array"}).option("headers",{alias:"H",description:"Tile HTTP headers (space-separated <name>=<value> pairs)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Tilesource id",type:"string"});async function xr(e){const i=e.client,a=await i.tileSources.drivers();try{if(e.interactive){const o=await f.prompt([{type:"select",name:"type",message:"Data source",choices:a,default:e.type||"dataset"},{type:"input",name:"source",message:"TileLive URL",default:e.source,when:({type:s})=>s==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:Sr.bind(null,e.client),default:e.source,when:({type:s})=>s==="dataset"},{type:"input",name:"attribution",message:"Attribution",default:e.attribution},{type:"input",name:"minZoom",message:"Minimum zoom",default:e.minZoom||0,validate:s=>ee(s)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||18,validate:s=>ee(s)||"Enter a number"},{type:"confirm",name:"overrideExtent",message:"Override auto-calculated map extent",default:e.extent?!!e.extent.length:!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (blank for auto-detect)",validate:s=>ge(s)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:s})=>s}]);e.type=o.type,e.source=o.source,e.attribution=o.attribution,e.minZoom=o.minZoom,e.maxZoom=o.maxZoom,e.extent=o.overrideExtent?o.extent.split(" "):[]}let t;if(e.extent&&e.extent.length===0)t=null;else if(e.extent){if(!ge(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");t=vr(e.extent)}let n={id:e.id,type:e.type,source:e.source,attribution:e.attribution,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:t};n=await i.tileSources.create(n),e.format==="table"?r(n,"table",["id","type","source","minZoom","maxZoom"]):r(n,e.format??"json")}catch(t){c(t)}}function ee(e){return!isNaN(parseFloat(e))&&isFinite(e)}function ge(e){return!e||e.length===0||(typeof e=="string"&&(e=e.split(" ")),e.length!==4)?!1:!e.find(i=>!ee(i))}function vr(e){return e==null?null:(typeof e=="string"&&(e=e.split(" ")),e.map(i=>parseFloat(i)))}async function Sr(e){return(await e.datasets.list({sort:"id"})).map(i=>i.id)}const jr={__proto__:null,builder:_r,command:wr,desc:$r,handler:xr},Dr=["delete <id>","rm","del"],kr="Delete a tile source",Ar=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Tile source id (or unique prefix)",type:"string"});async function Nr({client:e,id:i,yes:a}){try{const t=await p(i,e.tileSources);let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.id}'`,default:!1})).confirmed),n&&(await e.tileSources.delete(t.id),w("Deleted"))}catch(t){c(t)}}const Ir={__proto__:null,builder:Ar,command:Dr,desc:kr,handler:Nr},Pr=["list [id]","ls","$0"],Or="List tile sources",Er=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("id",["limit","sort"]).positional("id",{describe:"Tile source id",type:"string"});async function qr({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.tileSources);r(o,a)}else{const o=await e.tileSources.list({limit:t,sort:n});a==="table"?r(o,"table",["id","type","source","minZoom","maxZoom"]):r(o,a??"json")}}catch(o){c(o)}}const Tr={__proto__:null,builder:Er,command:Pr,desc:Or,handler:qr},Mr=["metadata <id>","meta"],Lr="Get/set resource metadata",Cr=e=>e.positional("id",{describe:"Tile Source id (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function Ur({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.tileSources);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.tileSources.deleteMetadata(t.id,o):await e.tileSources.setMetadata(t.id,o,s)}t=await p(i,e.tileSources),r(t.metadata,"json")}else{const t=await p(i,e.tileSources);r(t.metadata,"json")}}catch(t){c(t)}}const Jr={__proto__:null,builder:Cr,command:Mr,desc:Lr,handler:Ur},Rr="preview <tilesourceId>",Fr="Preview a tile source in maputnik",Kr=async({client:e,tilesourceId:i})=>{try{const a=await p(i,e.tileSources),t=await e.tiles.getPreviewStyle(a.id,{apikey:e.apiKey});new Y(e,t,!1),V("http://localhost:8000")}catch(a){c(a)}},zr={__proto__:null,command:Rr,desc:Fr,handler:Kr},Zr=["tilespec <tilesourceId>"],Gr="Get tileJson for tile source",Vr=e=>e.positional("tilesourceId",{describe:"Tile source id",type:"string"}),Hr=async({client:e,format:i,tilesourceId:a})=>{try{const t=await p(a,e.tileSources),n=await e.tiles.getTileSpec(t.id);i==="table"&&(delete n.vector_layers,delete n.attribution,delete n.tiles,n.center=JSON.stringify(n.center),n.bounds=JSON.stringify(n.bounds)),r(n,i)}catch(t){c(t)}},Br={__proto__:null,builder:Vr,command:Zr,desc:Gr,handler:Hr},Wr=["update <id>"],Qr="Update a tile source",Xr=e=>e.option("type",{alias:"t",description:"Tilesource type",type:"string"}).option("source",{alias:"s",description:"Tilesource source (tilelive URL or dataset ID)",type:"string"}).option("attribution",{alias:"a",description:"Tilesource attribution",type:"string"}).option("minZoom",{alias:"m",description:"Minimum zoom",type:"number"}).option("maxZoom",{alias:"M",description:"Maximum zoom",type:"number"}).option("extent",{alias:"e",description:"Extent x1,y1,x2,y2 (lng/lat)",type:"array"}).option("headers",{alias:"H",description:"Tile HTTP headers (space-separated <name>=<value> pairs)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Tilesource id (or unique prefix)",type:"string"});async function Yr(e){const i=e.client,a=e.id,t=await i.tileSources.drivers();try{let n=await p(a,i.tileSources);if(e.interactive){const d=await f.prompt([{type:"select",name:"type",message:"Tilesource Type",choices:t,default:e.type||n.type},{type:"input",name:"source",message:"TileLive URL",default:e.source||n.source,when:({type:l})=>l==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:ed.bind(null,e.client),default:e.source||n.source,when:({type:l})=>l!=="tilelive"},{type:"input",name:"attribution",message:"Attribution",default:e.attribution||n.attribution},{type:"input",name:"minZoom",message:"Minimum zoom",default:e.minZoom||n.minZoom,validate:l=>E(l)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||n.maxZoom,validate:l=>E(l)||"Enter a number"},{type:"confirm",name:"overrideExtent",message:"Override map extent specfied by dataset",default:e.extent?!!e.extent.length:!!n.extentLngLat},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (blank to use extent specified by dataset)",default:R(e.extent||n.extentLngLat),validate:l=>J(l)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:l})=>l}]);e.type=d.type,e.source=d.source,e.attribution=d.attribution,e.minZoom=d.minZoom,e.maxZoom=d.maxZoom,e.extent=d.overrideExtent?d.extent.split(" "):[]}let o;e.headers&&(o=e.headers.reduce((d,l)=>{const[m,u]=l.split("=");if(!m||!u)throw new Error("Bad format for headers: specify key/value pairs as <header>=<value>");return d[m]=u,d},{}));let s;if(e.extent&&e.extent.length===0)s=null;else if(e.extent){if(!J(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");s=H(e.extent)}n=await i.tileSources.update(n.id,{id:e.id,type:e.type,source:e.source,attribution:e.attribution,headers:o,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:s}),r(n,"table")}catch(n){c(n)}}async function ed(e){return(await e.datasets.list({sort:"id"})).map(i=>i.id)}const td={__proto__:null,builder:Xr,command:Wr,desc:Qr,handler:Yr},id="tiles <command>",ad="Tile commands",nd=e=>e.command(Tr).command(jr).command(Ir).command(td).command(Jr).command(zr).command(Br).demandCommand(1),od={__proto__:null,builder:nd,command:id,desc:ad},sd=["list [id]","ls"],rd="List uploads",dd=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).positional("id",{describe:"Upload id",type:"string"}).conflicts("id",["limit","sort"]);async function cd({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.uploads);r(o,a)}else{const o=await e.uploads.list({limit:t,sort:n});a==="table"?r(o,"table",["id","fileName","status"]):r(o,a??"json")}}catch(o){c(o)}}const ld={__proto__:null,builder:dd,command:sd,desc:rd,handler:cd},{fileSize:pd}=Je,md=1e3,ud=["upload <filePath>","$0"],yd="Upload a geospatial data file",fd=e=>e.option("i",{alias:"id",describe:"Dataset id",type:"string"}).option("n",{alias:"name",describe:"Dataset name",type:"string"}).option("d",{alias:"description",describe:"Dataset description",type:"string"}).option("a",{alias:"attribution",describe:"Tiles source attribution",type:"string"}).option("source-srid",{describe:"Source SRID (default auto-detected from source)",type:"number"}).option("target-srid",{describe:"Target SRID (default auto-detected from source)",number:!0,type:"number"}).option("schema",{describe:"Database schema (defaults to 'public' if not specified)",type:"string"}).positional("filePath",{describe:"Path to file to upload",type:"string"}),bd=async e=>{Object.prototype.hasOwnProperty.call(e,"source-srid")&&isNaN(e["source-srid"])&&(c("--source-srid must be a number"),process.exit(1)),Object.prototype.hasOwnProperty.call(e,"target-srid")&&isNaN(e["target-srid"])&&(c("--target-srid must be a number"),process.exit(1));const i=D.basename(e.filePath);v(`Uploading file ${i} ...`);const a=N("Uploading").start(),t=N("Ingesting"),n=N("Processing");try{const o=g.createReadStream(e.filePath),s=await e.client.uploads.upload({data:o,filename:i,datasetId:e.id,datasetName:e.name,datasetDescription:e.description,attribution:e.attribution,sourceSrid:e["source-srid"],targetSrid:e["target-srid"],schema:e.schema}),d=s.id,l=x=>new Promise(A=>{setTimeout(A,x)});let m,u=s.status,y=a;for(;;)if(await l(md),m=await e.client.uploads.get(d),m.status!==u)if(u=m.status,u==="ingesting")a.succeed(),y=t,y.start();else if(u==="processing")a.succeed(),t.start(),t.succeed(),y=n,y.start();else if(u==="done"){a.succeed(),t.succeed(),n.succeed(),v(`Dataset uploaded with id '${m.options.datasetId}' (${pd(m.size)})`);break}else{y.fail(),c(m.error.message),w(m.error.stderr||m.error.stdout);break}}catch(o){a.stop(),t.stop(),n.stop(),c(o)}},gd={__proto__:null,builder:fd,command:ud,desc:yd,handler:bd},hd="upload <command>",wd="Upload commands",$d=e=>e.command(ld).command(gd).demandCommand(1),_d={__proto__:null,builder:$d,command:hd,desc:wd},xd=["create <email>"],vd="Create a user",Sd=e=>e.option("firstName",{description:"First name",type:"string"}).option("lastName",{description:"Last name",type:"string"}).option("roles",{alias:"r",description:"Roles (comma-separated)",type:"array"}).option("emailVerified",{description:"Email verified",type:"boolean"}).option("enabled",{alias:"e",description:"Login enabled",type:"boolean"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function jd(e){const i=e.client;try{if(e.interactive){const n=await f.prompt([{type:"input",name:"firstName",message:"First name",default:e.firstName},{type:"input",name:"lastName",message:"Last name",default:e.lastName},{type:"input",name:"roles",message:"Roles (comma separated)",default:(e.roles??[]).join(", ")},{type:"confirm",name:"emailVerified",message:"Verified?",default:Object.prototype.hasOwnProperty.call(e,"emailVerified")?e.emailVerified:!0},{type:"confirm",name:"enabled",message:"Enabled?",default:Object.prototype.hasOwnProperty.call(e,"enabled")?e.enabled:!0},{type:"password",name:"password",message:"Password"},{type:"password",name:"confirmPassword",message:"Confirm password"}]);if(n.password!==n.confirmPassword)throw new Error("Passwords do not match");e.firstName=n.firstName,e.lastName=n.lastName,e.roles=n.roles.split(",").map(o=>o.trim()),e.emailVerified=n.emailVerified,e.enabled=n.enabled,e.password=n.password}const a=await i.auth.users.create({email:e.email,firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled,password:e.password}),t=te();await i.auth.users.update(a.id,{passwordResetToken:t}),await i.auth.resetPassword(a.email,e.password,a.applicationId,t),e.format==="table"?r(a,"table"):r(a,"json")}catch(a){c(a)}}const Dd={__proto__:null,builder:Sd,command:xd,desc:vd,handler:jd},kd=["delete <email>","rm","del"],Ad="Delete a user",Nd=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function Id({client:e,email:i,yes:a}){try{const t=await p(i,e.auth.users,"email");let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.email}'`,default:!1})).confirmed),n&&(await e.auth.users.delete(t.id),w("Deleted"))}catch(t){c(t)}}const Pd={__proto__:null,builder:Nd,command:kd,desc:Ad,handler:Id},Od=["list [email]","ls","$0"],Ed="List users",qd=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("email",["limit","sort"]).positional("email",{describe:"User email (or unique prefix)",type:"string"});async function Td({client:e,email:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.auth.users,"email");r(o,a)}else{let o=await e.auth.users.list({limit:t,sort:n});a==="table"?(o=o.map(s=>({...s,name:`${s.firstName} ${s.lastName}`})),r(o,"table",["id","email","name","emailVerified","enabled"])):r(o,a??"json")}}catch(o){c(o)}}const Md={__proto__:null,builder:qd,command:Od,desc:Ed,handler:Td},Ld=["metadata <email>","meta"],Cd="Get/set resource metadata",Ud=e=>e.positional("email",{describe:"User email (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function Jd({client:e,email:i,set:a}){try{if(a){let t=await p(i,e.auth.users,"email");const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.auth.users.deleteMetadata(t.id,o):await e.auth.users.setMetadata(t.id,o,s)}t=await p(i,e.auth.users,"email"),r(t.metadata,"json")}else{const t=await p(i,e.auth.users,"email");r(t.metadata,"json")}}catch(t){c(t)}}const Rd={__proto__:null,builder:Ud,command:Ld,desc:Cd,handler:Jd},Fd=["update <email>"],Kd="Update a user",zd=e=>e.option("firstName",{description:"First name",type:"string"}).option("lastName",{description:"Last name",type:"string"}).option("roles",{alias:"r",description:"Roles (comma-separated)",type:"array"}).option("emailVerified",{description:"Email verified",type:"boolean"}).option("enabled",{alias:"e",description:"Login enabled",type:"boolean"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function Zd(e){const i=e.client,a=e.email;try{const t=await p(a,i.auth.users,"email");if(e.interactive){const o=await f.prompt([{type:"input",name:"firstName",message:"First name",default:e.firstName||t.firstName},{type:"input",name:"lastName",message:"Last name",default:e.lastName||t.lastName},{type:"input",name:"roles",message:"Roles (comma separated)",default:(e.roles||t.roles).join(", ")},{type:"confirm",name:"emailVerified",message:"Verified?",default:Object.prototype.hasOwnProperty.call(e,"emailVerified")?e.emailVerified:t.emailVerified},{type:"confirm",name:"enabled",message:"Enabled?",default:Object.prototype.hasOwnProperty.call(e,"enabled")?e.enabled:t.enabled},{type:"confirm",name:"changePassword",message:"Change password?",default:!1},{type:"password",name:"password",message:"New password",when:({changePassword:s})=>s},{type:"password",name:"confirmPassword",message:"Confirm password",when:({changePassword:s})=>s}]);if(o.changePassword){if(o.password!==o.confirmPassword)throw new Error("Passwords do not match");e.password=o.password}e.firstName=o.firstName,e.lastName=o.lastName,e.roles=o.roles.split(",").map(s=>s.trim()),e.emailVerified=o.emailVerified,e.enabled=o.enabled}if(e.password){const o=te();await i.auth.users.update(t.id,{passwordResetToken:o}),await i.auth.resetPassword(t.email,e.password,t.applicationId,o)}const n=await i.auth.users.update(t.id,{firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled});e.format==="table"?r(n,"table"):r(n,"json")}catch(t){c(t)}}const Gd={__proto__:null,builder:zd,command:Fd,desc:Kd,handler:Zd},Vd="users <command>",Hd="User commands",Bd=e=>e.command(Md).command(Dd).command(Pd).command(Rd).command(Gd).demandCommand(1),Wd={__proto__:null,builder:Bd,command:Vd,desc:Hd},Qd="@emuanalytics/flow-cli",Xd="2.2.1",Yd="module",ec="dist/index.mjs",tc="Robin Summerhill <robin.summerhill@emu-analytics.com>",ic="Copyright 2018 Emu Analytics Limited",ac={flow:"dist/index.mjs"},nc=["dist","maputnik"],oc={start:"node dist/index.mjs",typecheck:"tsc --noEmit",build:"unbuild",clean:"rimraf dist",lint:"eslint src",format:'prettier "src/**/*.ts" --write',prepublishOnly:"npm run build",prebuild:"npm run clean",precommit:"lint-staged"},sc={"@eslint/js":"^10.0.1","@types/cli-spinner":"^0.2.0","@types/express":"^4.17.25","@types/humanize-plus":"^1.8.0","@types/node":"^20.0.0","@types/table":"^4.0.5","@types/uuid":"^8.3.4","@types/yauzl":"^2.9.1",eslint:"^10.3.0","lint-staged":"^16.4.0",prettier:"^3.0.0",typescript:"^5.9.2","typescript-eslint":"^8.59.2",unbuild:"^3.6.1"},rc={"@emuanalytics/flow-engine-client":"^2.2.1","@modelcontextprotocol/sdk":"^1.12.0",JSONStream:"^1.3.5",chalk:"^5.6.2","cli-spinner":"^0.2.10",colorette:"^2.0.19","csv-stringify":"^5.3.3","debounce-promise":"^3.1.0",execa:"^5.1.1",express:"^4.16.3","find-up":"^8.0.0","humanize-plus":"^1.8.2",inquirer:"^13.4.3","loose-json":"^1.1.2","node-emoji":"^2.2.0",open:"^11.0.0",ora:"^9.4.0","replace-in-file":"^5.0.2","string-width":"^8.2.1",table:"^6.9.0",tslib:"^2.8.1",uuid:"^8.3.2",yargs:"^18.0.0",yauzl:"^2.10.0",zod:"^3.23.0"},dc={"@types/serve-static":"^1.15.0"},cc="5b5244b47512c723261fdd56742f8c6b71105d1e",lc={name:Qd,version:Xd,type:Yd,main:ec,author:tc,license:ic,bin:ac,files:nc,scripts:oc,devDependencies:sc,dependencies:rc,overrides:dc,"lint-staged":{"./src/**/*.ts":["eslint"]},gitHead:cc},pc="version",mc="Display Flo.w Engine version information",uc=async({host:e,apiKey:i,client:a,format:t})=>{try{const n=await a.config(),o={"CLI version":lc.version,"Flo.w version":n.version.tag,"Flo.w host":e,"API key":i};r(o,t)}catch(n){c(n.message)}},yc={__proto__:null,command:pc,desc:mc,handler:uc},fc="mcp",bc="Start the Flow MCP server for AI agent access via the Model Context Protocol";function gc(e){return e}const P={limit:h.number().int().min(1).max(1e3).optional().describe("Maximum number of results to return"),offset:h.number().int().min(0).optional().describe("Number of results to skip for pagination"),filter:h.string().optional().describe('JSON object filter expression, e.g. {"name":"foo"} or {"active":true}'),sort:h.string().optional().describe("Field name to sort by, prefix with - for descending (e.g. -createdAt)"),attributes:h.string().optional().describe("Comma-separated list of attributes to include in the response")};function O(e){return{limit:e.limit,offset:e.offset,filter:e.filter?JSON.parse(e.filter):void 0,sort:e.sort,attributes:e.attributes}}async function hc(e){const{client:i}=e,a=new Re({name:"flow-mcp",version:"1.0.0"});a.registerTool("get_current_time",{description:"Get the current UTC date and time as an ISO 8601 string. Use this when you need to compare timestamps such as pipeline heartbeatAt or startedAt to determine if something is currently active.",inputSchema:{},outputSchema:{datetime:h.string().datetime().describe("Current UTC date and time as ISO 8601")}},async()=>{const n=new Date().toISOString();return{content:[{type:"text",text:n}],structuredContent:{datetime:n}}}),a.registerTool("list_datasets",{description:"List datasets available in flow-engine with minimal fields (id, name, description, type). Use this to discover what datasets exist, then call get_dataset for full schema and attribute details on a specific dataset. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results.",inputSchema:{...P,limit:h.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:h.string().optional().default("id,name,description,type").describe("Comma-separated fields to return (default: id, name, description, type)")}},async n=>{console.error("Tool: list_datasets called",n);const o=await i.datasets.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("get_dataset",{description:"Get details of a specific dataset by ID, including its schema, attributes, and metadata",inputSchema:{id:h.string().describe("Dataset ID")}},async({id:n})=>{const o=await i.datasets.get(n);return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("query_dataset",{description:"Query the data inside a dataset. Supports filtering, aggregation, and spatial queries. Returns features as GeoJSON or plain JSON depending on the dataset type.",inputSchema:{id:h.string().describe("Dataset ID to query"),query:h.string().optional().describe("JSON query object \u2014 see flow-engine dataset query documentation for filter/aggregate syntax"),format:h.enum(["json","geojson","csv"]).optional().default("json").describe("Output format"),limit:h.number().int().min(1).max(1e4).optional().describe("Maximum number of features to return"),offset:h.number().int().min(0).optional().describe("Number of features to skip")}},async({id:n,query:o,format:s,limit:d,offset:l})=>{const m=o?JSON.parse(o):{};d!==void 0&&(m.limit=d),l!==void 0&&(m.offset=l);const u=await i.datasets.query(n,m,{format:s??"json"});return{content:[{type:"text",text:typeof u=="string"?u:JSON.stringify(u,null,2)}]}}),a.registerTool("list_styles",{description:"List all Mapbox GL styles (named map configurations) available in flow-engine",inputSchema:P},async n=>{const o=await i.styles.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_tile_sources",{description:"List all tile sources in flow-engine. Tile sources are raster or vector tile sets used as base layers or overlays.",inputSchema:P},async n=>{const o=await i.tileSources.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_pipelines",{description:"List data pipelines in flow-engine with minimal fields (id, environment, description, startedAt, heartbeatAt). Use this to discover what pipelines exist, then call get_pipeline for full details on a specific pipeline. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results.",inputSchema:{...P,limit:h.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:h.string().optional().default("id,environment,description,startedAt,heartbeatAt").describe("Comma-separated fields to return (default: id, environment, description, startedAt, heartbeatAt)")}},async n=>{const o=await i.pipelines.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("get_pipeline",{description:"Get details of a specific pipeline by ID, including its components and configuration",inputSchema:{id:h.string().describe("Pipeline ID")}},async({id:n})=>{const o=await i.pipelines.get(n,{join:"components"});return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_databases",{description:"List external database connections registered in flow-engine",inputSchema:P},async n=>{const o=await i.databases.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_applications",{description:"List applications registered in flow-engine",inputSchema:P},async n=>{const o=await i.applications.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}});const t=new Fe;await a.connect(t)}const wc={__proto__:null,builder:gc,command:fc,describe:bc,handler:hc},he=$e([".flowrc",".flowrc.json"]),$c=he?JSON.parse(g.readFileSync(he).toString("utf8")):{},_c=e=>(e.client=new Z(e.host,e.apiKey),e),we=_e(xe(process.argv));we.config($c).env("FLOW").option("k",{alias:"apiKey",demandOption:!0,describe:"Flo.w Engine API key",type:"string",global:!0}).option("h",{alias:"host",default:"https://flow.emu-analytics.net",describe:"Flo.w Engine host URL",type:"string"}).option("f",{alias:"format",default:"table",describe:"Output format",type:"string",choices:["table","csv","json","geojson"]}).command(ft).command($t).command(si).command(Ji).command(oa).command(an).command(xn).command(Qn).command(ko).command(Vo).command(qs).command(hr).command(od).command(_d).command(Wd).command(yc).command(wc).demandCommand(1).wrap(Math.min(100,we.terminalWidth())).help().version().middleware([_c]).parse();
27
+ ${b.dim("-")} ${b.cyan(i)}`:""}function Uo(e){let i=me.get(e);return i||(i=Jo(e),i.catch(a=>{c(a)}),me.set(e,i)),i}async function Jo(e){const i=process.cwd(),a=I(i,".tmp-flow-starter"),t=await Ao(e);return le(a),await No(t,a),await Eo("install",a),async n=>{const o=I(i,n);await Mo(a,o),await Le({files:[I(o,"*"),I(o,"config/**"),I(o,"src/**")],from:/flow-starter-project-name/g,to:n,allowEmptyPaths:!0}),le(null)}}function Ro(e){return!/[^a-zA-Z0-9-]/.test(e)}function Fo(e,i){if(e.includes("/"))return{name:e,repo:e};const a=i.find(t=>t.name===e);if(!a)throw new Error(`Starter "${e}" does not exist.`);return a}const Ko=["new [name]"],zo="Create a new Flo.w app",Zo=e=>e.option("s",{alias:"starter",describe:"Starter template",type:"string"}).positional("name",{describe:"Project name",type:"string"});async function Go(e){try{const i=await fetch(`${re}/index.json`).then(t=>t.json());if(!e.starter){const t=await f.prompt([{type:"select",name:"starter",message:"Starter template",choices:i.map(n=>n.name),default:i[0].name}]);e.starter=t.starter}const a=Fo(e.starter,i);await Lo(a,e.name,!1)}catch(i){c(i.message)}}const Vo={__proto__:null,builder:Zo,command:Ko,desc:zo,handler:Go},Ho=["cache [id]"],Bo="Show pipeline cache",Wo=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function Qo({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="cache").sort((s,d)=>s.index-d.index),o=n.map(s=>({id:s.id,type:s.type,description:s.description,related_components:s.metadata.components.join()}));if(a==="json"){r({id:t.id,environment:t.environment,caches:n},"json");return}o.length?r(o,"table",null,{noWrap:!0}):v("No Caches Found")}catch(t){c(t)}}const Xo={__proto__:null,builder:Wo,command:Ho,desc:Bo,handler:Qo},Yo=["components <id>","comps <id>"],es="Show pipeline components",ts=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function is({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=t.components??[];if(a==="json"){r(n,"json");return}const o=as(n);if(!o.length){v("No Components Found");return}r(o,"table",["id","class","type","description","note"])}catch(t){c(t)}}function as(e){if(!e.length)return[];const i={};e.forEach(s=>i[s.id]={...s,next:[],previous:[],indent:0}),Object.values(i).forEach(s=>{const d=s.metadata||{},l=d.previousComponents??[],m=d.nextComponents??[];s.previous=l.map(u=>i[u]),s.next=m.map(u=>i[u])});const a={};Object.values(i).forEach(s=>{const d=(s.metadata||{}).parentComponent;d&&(a[d]||(a[d]=[]),a[d].push(s))}),Object.values(a).forEach(s=>{s.sort((d,l)=>d.index-l.index)});const t=Object.values(i).filter(s=>s.class!=="cache"&&(s.previous??[]).length===0),n=new Set;function o(s,d){if(!s||n.has(s.id))return;n.add(s.id),s.indent=d,s.class==="pipeline"&&(a[s.id]??[]).forEach(u=>o(u,d+1));const l=(s.next??[]).filter(u=>u.class!=="cache"),m=s.type==="fp:fork"||l.length>1;l.forEach(u=>o(u,m?d+1:d))}return t.forEach(s=>o(s,0)),Object.values(i).filter(s=>s.class!=="cache").sort((s,d)=>s.index-d.index).map(s=>{const d=[],l=(s.metadata||{}).caches??[];l.length>0&&d.push(`uses cache: ${l.join(", ")}`);const m=s.indent>0?" ".repeat(s.indent)+"> ":"";function u(y){return y.class==="pipeline"?`(${y.id})`:y.type==="fp:fork"?`[${y.id}]`:y.id}return{id:m+u(s),class:s.class,type:s.type,description:s.description,note:d.join()}})}const ns={__proto__:null,builder:ts,command:Yo,desc:es,handler:is},os=["config [id]"],ss="Show pipeline config",rs=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function ds({client:e,id:i,format:a}){try{const t=(await p(i,e.pipelines,"id"))?.config;if(!t||Object.keys(t).length===0){r({message:`No configuration found for pipeline ${i}`},a??"json");return}const n=X(t);if(a==="json"){r(t,"json");return}const o=Object.entries(n).map(([s,d])=>({key:s,value:d}));r(o,"table",["key","value"],{noWrap:!0})}catch(t){c(t)}}function ue(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function X(e,i="",a={}){for(const[t,n]of Object.entries(e)){const o=i?`${i}.${t}`:t;Array.isArray(n)?n.forEach((s,d)=>{const l=`${o}[${d}]`;ue(s)?X(s,l,a):a[l]=s}):ue(n)?X(n,o,a):a[o]=n}return a}const cs={__proto__:null,builder:rs,command:os,desc:ss,handler:ds},ls=["list [id]","ls","$0"],ps="List pipelines",ms=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).option("filter-cache",{type:"string",describe:"Filter pipelines using a specific cache ID (partial match)"}).option("filter-dataset",{type:"string",describe:"Filter pipelines using a specific dataset ID (partial match)"}).option("components",{type:"boolean",describe:"Include pipeline components in the JSON output"}).positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"}).conflicts("id",["limit","sort","filter-cache","filter-dataset"]);async function us({client:e,id:i,format:a,limit:t,sort:n,filterCache:o,filterDataset:s,components:d}){try{if(n??="id",i){const y=await p(i,e.pipelines,"id",void 0,n),x=fe(y);a==="table"?r(ye(x),"table",null,{noWrap:!0}):r(x,a);return}const l=!!o||!!s,m=d||l,u=(await e.pipelines.list({sort:n,limit:l?void 0:t,join:m?"components":void 0})).filter(y=>fs(y,o)).filter(y=>ys(y,s)).slice(0,l?t:void 0).map(y=>d?y:bs(y));gs(u,a)}catch(l){c(l)}}function ys(e,i){return!i||ie(e.components).some(a=>a.class==="sink"&&a.type==="fp:flow-ingest-stream"&&a.config?.datasetId?.includes(i))}function fs(e,i){return!i||ie(e.components).some(a=>a.class==="cache"&&a.config?.id?.includes(i))}function bs({components:e,...i}){return i}function gs(e,i){const a=e.map(fe);i==="table"?r(a.map(ye),"table",["id","environment","version","description","startedAt","heartbeatAt"]):r(a,i??"json")}function ye(e){const i=e.heartbeatAt&&(Date.now()-new Date(e.heartbeatAt).getTime())/1e3<30;return{...e,heartbeatAt:{color:i?"06a453":"e11529",value:e.heartbeatAt}}}function fe(e){const i=e.metadata.version?.tag;return{...e,version:i&&i.replace(/^release-/,"")}}const hs={__proto__:null,builder:ms,command:ls,desc:ps,handler:us},ws=["sink [id]"],$s="Show pipeline sink",_s=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function xs({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="sink").sort((s,d)=>s.index-d.index),o=n.reduce((s,d)=>(s[d.type]??=[],s[d.type].push(d),s),{});if(a==="json"){r({id:t.id,environment:t.environment,sinks:n},"json");return}if(Object.keys(o).length)for(const[,s]of Object.entries(o)){const d=s.map(l=>({id:l.id,description:l.description,...vs(l)}));r(d,"table")}else v("No Sinks Found")}catch(t){c(t)}}function vs(e){const i=e.config||{};switch(e.type){case"fp:amqp-writable-stream":return{vhost:i.vhost??"N/A",exchange:typeof i.exchange=="string"?i.exchange:i.exchange?.name??"N/A",enable:i.enable};case"fp:flow-ingest-stream":return{datasetId:i.datasetId??"N/A",enable:i.enable};case"fp:sqs-writable-stream":return{queueUrl:i.queueUrl??"N/A",region:i.region??"N/A",enable:i.enable};case"fp:sns-writable-stream":return{topicArn:i.topicArn??"N/A",region:i.region??"N/A",enable:i.enable};case"fp:null-sink":return{logging:i.logging};default:return{}}}const Ss={__proto__:null,builder:_s,command:ws,desc:$s,handler:xs},js=["source [id]"],Ds="Show pipeline source",ks=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function As({client:e,id:i,format:a}){try{const t=await p(i,e.pipelines,"id",["components"],["id","components.index"]);if(!t){r({message:`Pipeline not found: ${i}`},a??"json");return}const n=(t.components??[]).filter(s=>s.class==="source").sort((s,d)=>s.index-d.index),o=n.map(s=>({id:s.id,type:s.type,description:s.description,...Ns(s)}));if(a==="json"){r({id:t.id,environment:t.environment,sources:n},"json");return}o.length?r(o,"table"):v("No Sources Found")}catch(t){c(t)}}function Ns(e){const i=e.config||{};switch(e.type){case"fp:amqp-readable-stream":return{vhost:i.vhost??"N/A",exchange:i.queue?.bindTo?.exchange??i.exchange??"N/A",queue:typeof i.queue=="string"?i.queue:i.queue?.name??"N/A"};case"fp:mqtt-subscriber-stream":return{serverUrl:i.serverUrl??"N/A",topic:i.topic??"N/A"};case"fp:flow-dataset-stream":return{datasetId:i.datasetId??"N/A"};case"fp:db-timeseries-stream":return{database:i.database??"N/A",table:i.table??"N/A"};case"fp:sqs-readable-stream":return{queueUrl:i.queueUrl??"N/A",region:i.region??"N/A"};case"fp:s3-file-list-stream":return{bucket:i.bucket??"N/A"};case"fp:s3-file-loader-stream":return{bucket:i.bucket??"N/A"};case"fp:planefinder-firehose-stream":return{host:i.host??"N/A"};case"fp:tcp-tls-readable-stream":return{hostname:i.hostname??"N/A"};case"fp:timer-stream":return{interval:i.interval??"N/A"};default:return{}}}const Is={__proto__:null,builder:ks,command:js,desc:Ds,handler:As},Ps=["pipelines <command>","pipes"],Os="Pipeline commands",Es=e=>e.command(hs).command(cs).command(ns).command(Is).command(Ss).command(Xo).demandCommand(1),qs={__proto__:null,builder:Es,command:Ps,desc:Os},Ts="create <id>",Ms="Create a new map style",Ls=e=>e.positional("id",{describe:"Map style id",type:"string"}).option("name",{alias:"n",describe:"Name for new style",type:"string"}).option("description",{alias:"d",describe:"Description for new style",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("sourceId",{alias:"sid",describe:"ID of source style",type:"string",default:"darkmatter"}).option("sourceKey",{alias:"skey",describe:"API key to access source style",type:"string",default:"6f19c5c7-1a9b-425b-84ab-09ba56e905b2"}),Cs=async e=>{try{const i=[].concat(await be(e.host,e.apiKey),await be(e.host,e.sourceKey)),a=i.map(o=>({name:o.name,value:o,short:o.name}));let t=i.find(o=>o.id===e.sourceId);if(e.interactive){const o=await f.prompt([{type:"input",name:"name",message:"Name for new style",default:e.name},{type:"input",name:"description",message:"Description for new style",default:e.description},{type:"select",name:"sourceStyle",message:"Select the source style to copy",choices:a,default:t}]);e.name=o.name,e.description=o.description,e.sourceId=o.sourceStyle.id,t=i.find(s=>s.id===e.sourceId)}t.style.id=e.id,t.style.name=e.name,t.style.metadata.description=e.description;let n={id:e.id,name:e.name,description:e.description,type:"plain",style:t.style};n=await e.client.styles.create(n),e.format==="table"?r(n,"table",["id","name","description","type"]):r(n,e.format??"json")}catch(i){c(i)}};async function be(e,i){return new Z(e,i).styles.list()}const Us={__proto__:null,builder:Ls,command:Ts,desc:Ms,handler:Cs},Js=["delete <id>","rm","del"],Rs="Delete a map style",Fs=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Map style id (or unique prefix)",type:"string"});async function Ks({client:e,id:i,yes:a}){try{const t=await p(i,e.styles);let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.id}'`,default:!1})).confirmed),n&&(await e.styles.delete(t.id),w("Deleted"))}catch(t){c(t)}}const zs={__proto__:null,builder:Fs,command:Js,desc:Rs,handler:Ks},Zs=D.dirname(Ee(import.meta.url)),$=G();$.use(G.json({limit:"1mb"})),$.use(G.static(D.join(Zs,"../maputnik"))),$.get("/styles",(e,i)=>{v(":thumbsup: Maputnik connected");const a=$.get("style");i.json([a.id])}),$.get("/styles/:id",async(e,i)=>{try{const a=$.get("style");v(`Loading style '${a.id}'`),i.json(a)}catch(a){c(a.message)}}),$.put("/styles/:id",async(e,i)=>{try{const a=$.get("client");if($.set("style",e.body),!$.get("writeable"))return i.status(204).send();const t={...e.body};t.sources=Gs(t.sources,`${a.baseUrl}`),t.glyphs=M(t.glyphs,a.baseUrl),t.sprite=M(t.sprite,a.baseUrl),await Hs(a,t.id,t),i.status(204).send()}catch(a){c(a.message)}});function Gs(e,i){return Object.keys(e).reduce((a,t)=>{let n=e[t];return Object.prototype.hasOwnProperty.call(n,"url")&&(n={...n,url:M(n.url,i)}),Object.prototype.hasOwnProperty.call(n,"data")&&(n={...n,data:M(n.data,i)}),Object.prototype.hasOwnProperty.call(n,"tiles")&&(n={...n,tiles:n.tiles.map(o=>M(o,i))}),a[t]=n,a},{})}function M(e,i){const a=new URL(e,i);return a.searchParams.delete("apikey"),a.toString().replace(i,"").replace(/%7B/g,"{").replace(/%7D/g,"}")}function Vs(e,i,a){return e.styles.update(i,{style:a})}const Hs=Ue(Vs,2e3,{leading:!0});class Y{server;port;constructor(i,a,t){this.port=this.normalizePort(process.env.PORT||"8000"),$.set("port",this.port),$.set("style",a),$.set("writeable",t),$.set("client",i),this.server=Ce.createServer($),this.server.listen(this.port),this.server.on("error",this.onError.bind(this)),this.server.on("listening",this.onListening.bind(this))}normalizePort(i){const a=parseInt(i,10);return isNaN(a)?i:a>=0?a:!1}onError(i){if(i.syscall!=="listen")throw i;const a=typeof this.port=="string"?`Pipe ${this.port}`:`Port ${this.port}`;switch(i.code){case"EACCES":c(`${a} requires elevated privileges`),process.exit(1);break;case"EADDRINUSE":c(`${a} is already in use`),process.exit(1);break;default:throw i}}onListening(){w(`Style server listening on port ${this.port}`),v(`:rocket: Point your browser at http://localhost:${this.port}`),this.postListen()}postListen(){return Promise.resolve(null)}}const Bs="edit <id>",Ws="Edit a map style in maputnik",Qs=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),Xs=async({client:e,id:i})=>{try{const a=await p(i,e.styles),t=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,t,!0),V("http://localhost:8000")}catch(a){c(a)}},Ys={__proto__:null,builder:Qs,command:Bs,desc:Ws,handler:Xs},er=["list [id]","ls","$0"],tr="List map styles",ir=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("id",["limit","sort"]).positional("id",{describe:"Map style id (or unique prefix)",type:"string"});async function ar({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.styles);r(o,a)}else{const o=await e.styles.list({limit:t,sort:n});a==="table"?r(o,"table",["id","name","description","type"]):r(o,a??"json")}}catch(o){c(o)}}const nr={__proto__:null,builder:ir,command:er,desc:tr,handler:ar},or=["metadata <id>","meta"],sr="Get/set resource metadata",rr=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function dr({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.styles);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.styles.deleteMetadata(t.id,o):await e.styles.setMetadata(t.id,o,s)}t=await p(i,e.styles),r(t.metadata,"json")}else{const t=await p(i,e.styles);r(t.metadata,"json")}}catch(t){c(t)}}const cr={__proto__:null,builder:rr,command:or,desc:sr,handler:dr},lr="view <id>",pr="View a map style in maputnik",mr=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),ur=async({client:e,id:i})=>{try{const a=await p(i,e.styles),t=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,t,!1),V("http://localhost:8000")}catch(a){c(a)}},yr={__proto__:null,builder:mr,command:lr,desc:pr,handler:ur},fr="styles <command>",br="Style commands",gr=e=>e.command(nr).command(Us).command(zs).command(Ys).command(yr).command(cr).demandCommand(1),hr={__proto__:null,builder:gr,command:fr,desc:br},wr=["create <id>"],$r="Create a new tile source",_r=e=>e.option("type",{alias:"t",description:"Tilesource type",type:"string",choices:["tilelive","dataset"]}).option("source",{alias:"s",description:"Tilesource source (tilelive URL or dataset ID)",type:"string"}).option("attribution",{alias:"a",description:"Tilesource attribution",type:"string"}).option("minZoom",{alias:"m",description:"Minimum zoom",type:"number"}).option("maxZoom",{alias:"M",description:"Maximum zoom",type:"number"}).option("extent",{alias:"e",description:"Extent x1,y1,x2,y2 (lng/lat)",type:"array"}).option("headers",{alias:"H",description:"Tile HTTP headers (space-separated <name>=<value> pairs)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Tilesource id",type:"string"});async function xr(e){const i=e.client,a=await i.tileSources.drivers();try{if(e.interactive){const o=await f.prompt([{type:"select",name:"type",message:"Data source",choices:a,default:e.type||"dataset"},{type:"input",name:"source",message:"TileLive URL",default:e.source,when:({type:s})=>s==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:Sr.bind(null,e.client),default:e.source,when:({type:s})=>s==="dataset"},{type:"input",name:"attribution",message:"Attribution",default:e.attribution},{type:"input",name:"minZoom",message:"Minimum zoom",default:e.minZoom||0,validate:s=>ee(s)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||18,validate:s=>ee(s)||"Enter a number"},{type:"confirm",name:"overrideExtent",message:"Override auto-calculated map extent",default:e.extent?!!e.extent.length:!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (blank for auto-detect)",validate:s=>ge(s)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:s})=>s}]);e.type=o.type,e.source=o.source,e.attribution=o.attribution,e.minZoom=o.minZoom,e.maxZoom=o.maxZoom,e.extent=o.overrideExtent?o.extent.split(" "):[]}let t;if(e.extent&&e.extent.length===0)t=null;else if(e.extent){if(!ge(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");t=vr(e.extent)}let n={id:e.id,type:e.type,source:e.source,attribution:e.attribution,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:t};n=await i.tileSources.create(n),e.format==="table"?r(n,"table",["id","type","source","minZoom","maxZoom"]):r(n,e.format??"json")}catch(t){c(t)}}function ee(e){return!isNaN(parseFloat(e))&&isFinite(e)}function ge(e){return!e||e.length===0||(typeof e=="string"&&(e=e.split(" ")),e.length!==4)?!1:!e.find(i=>!ee(i))}function vr(e){return e==null?null:(typeof e=="string"&&(e=e.split(" ")),e.map(i=>parseFloat(i)))}async function Sr(e){return(await e.datasets.list({sort:"id"})).map(i=>i.id)}const jr={__proto__:null,builder:_r,command:wr,desc:$r,handler:xr},Dr=["delete <id>","rm","del"],kr="Delete a tile source",Ar=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Tile source id (or unique prefix)",type:"string"});async function Nr({client:e,id:i,yes:a}){try{const t=await p(i,e.tileSources);let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.id}'`,default:!1})).confirmed),n&&(await e.tileSources.delete(t.id),w("Deleted"))}catch(t){c(t)}}const Ir={__proto__:null,builder:Ar,command:Dr,desc:kr,handler:Nr},Pr=["list [id]","ls","$0"],Or="List tile sources",Er=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("id",["limit","sort"]).positional("id",{describe:"Tile source id",type:"string"});async function qr({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.tileSources);r(o,a)}else{const o=await e.tileSources.list({limit:t,sort:n});a==="table"?r(o,"table",["id","type","source","minZoom","maxZoom"]):r(o,a??"json")}}catch(o){c(o)}}const Tr={__proto__:null,builder:Er,command:Pr,desc:Or,handler:qr},Mr=["metadata <id>","meta"],Lr="Get/set resource metadata",Cr=e=>e.positional("id",{describe:"Tile Source id (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function Ur({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.tileSources);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.tileSources.deleteMetadata(t.id,o):await e.tileSources.setMetadata(t.id,o,s)}t=await p(i,e.tileSources),r(t.metadata,"json")}else{const t=await p(i,e.tileSources);r(t.metadata,"json")}}catch(t){c(t)}}const Jr={__proto__:null,builder:Cr,command:Mr,desc:Lr,handler:Ur},Rr="preview <tilesourceId>",Fr="Preview a tile source in maputnik",Kr=async({client:e,tilesourceId:i})=>{try{const a=await p(i,e.tileSources),t=await e.tiles.getPreviewStyle(a.id,{apikey:e.apiKey});new Y(e,t,!1),V("http://localhost:8000")}catch(a){c(a)}},zr={__proto__:null,command:Rr,desc:Fr,handler:Kr},Zr=["tilespec <tilesourceId>"],Gr="Get tileJson for tile source",Vr=e=>e.positional("tilesourceId",{describe:"Tile source id",type:"string"}),Hr=async({client:e,format:i,tilesourceId:a})=>{try{const t=await p(a,e.tileSources),n=await e.tiles.getTileSpec(t.id);i==="table"&&(delete n.vector_layers,delete n.attribution,delete n.tiles,n.center=JSON.stringify(n.center),n.bounds=JSON.stringify(n.bounds)),r(n,i)}catch(t){c(t)}},Br={__proto__:null,builder:Vr,command:Zr,desc:Gr,handler:Hr},Wr=["update <id>"],Qr="Update a tile source",Xr=e=>e.option("type",{alias:"t",description:"Tilesource type",type:"string"}).option("source",{alias:"s",description:"Tilesource source (tilelive URL or dataset ID)",type:"string"}).option("attribution",{alias:"a",description:"Tilesource attribution",type:"string"}).option("minZoom",{alias:"m",description:"Minimum zoom",type:"number"}).option("maxZoom",{alias:"M",description:"Maximum zoom",type:"number"}).option("extent",{alias:"e",description:"Extent x1,y1,x2,y2 (lng/lat)",type:"array"}).option("headers",{alias:"H",description:"Tile HTTP headers (space-separated <name>=<value> pairs)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Tilesource id (or unique prefix)",type:"string"});async function Yr(e){const i=e.client,a=e.id,t=await i.tileSources.drivers();try{let n=await p(a,i.tileSources);if(e.interactive){const d=await f.prompt([{type:"select",name:"type",message:"Tilesource Type",choices:t,default:e.type||n.type},{type:"input",name:"source",message:"TileLive URL",default:e.source||n.source,when:({type:l})=>l==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:ed.bind(null,e.client),default:e.source||n.source,when:({type:l})=>l!=="tilelive"},{type:"input",name:"attribution",message:"Attribution",default:e.attribution||n.attribution},{type:"input",name:"minZoom",message:"Minimum zoom",default:e.minZoom||n.minZoom,validate:l=>E(l)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||n.maxZoom,validate:l=>E(l)||"Enter a number"},{type:"confirm",name:"overrideExtent",message:"Override map extent specfied by dataset",default:e.extent?!!e.extent.length:!!n.extentLngLat},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (blank to use extent specified by dataset)",default:R(e.extent||n.extentLngLat),validate:l=>J(l)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:l})=>l}]);e.type=d.type,e.source=d.source,e.attribution=d.attribution,e.minZoom=d.minZoom,e.maxZoom=d.maxZoom,e.extent=d.overrideExtent?d.extent.split(" "):[]}let o;e.headers&&(o=e.headers.reduce((d,l)=>{const[m,u]=l.split("=");if(!m||!u)throw new Error("Bad format for headers: specify key/value pairs as <header>=<value>");return d[m]=u,d},{}));let s;if(e.extent&&e.extent.length===0)s=null;else if(e.extent){if(!J(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");s=H(e.extent)}n=await i.tileSources.update(n.id,{id:e.id,type:e.type,source:e.source,attribution:e.attribution,headers:o,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:s}),r(n,"table")}catch(n){c(n)}}async function ed(e){return(await e.datasets.list({sort:"id"})).map(i=>i.id)}const td={__proto__:null,builder:Xr,command:Wr,desc:Qr,handler:Yr},id="tiles <command>",ad="Tile commands",nd=e=>e.command(Tr).command(jr).command(Ir).command(td).command(Jr).command(zr).command(Br).demandCommand(1),od={__proto__:null,builder:nd,command:id,desc:ad},sd=["list [id]","ls"],rd="List uploads",dd=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).positional("id",{describe:"Upload id",type:"string"}).conflicts("id",["limit","sort"]);async function cd({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.uploads);r(o,a)}else{const o=await e.uploads.list({limit:t,sort:n});a==="table"?r(o,"table",["id","fileName","status"]):r(o,a??"json")}}catch(o){c(o)}}const ld={__proto__:null,builder:dd,command:sd,desc:rd,handler:cd},{fileSize:pd}=Je,md=1e3,ud=["upload <filePath>","$0"],yd="Upload a geospatial data file",fd=e=>e.option("i",{alias:"id",describe:"Dataset id",type:"string"}).option("n",{alias:"name",describe:"Dataset name",type:"string"}).option("d",{alias:"description",describe:"Dataset description",type:"string"}).option("a",{alias:"attribution",describe:"Tiles source attribution",type:"string"}).option("source-srid",{describe:"Source SRID (default auto-detected from source)",type:"number"}).option("target-srid",{describe:"Target SRID (default auto-detected from source)",number:!0,type:"number"}).option("schema",{describe:"Database schema (defaults to 'public' if not specified)",type:"string"}).positional("filePath",{describe:"Path to file to upload",type:"string"}),bd=async e=>{Object.prototype.hasOwnProperty.call(e,"source-srid")&&isNaN(e["source-srid"])&&(c("--source-srid must be a number"),process.exit(1)),Object.prototype.hasOwnProperty.call(e,"target-srid")&&isNaN(e["target-srid"])&&(c("--target-srid must be a number"),process.exit(1));const i=D.basename(e.filePath);v(`Uploading file ${i} ...`);const a=N("Uploading").start(),t=N("Ingesting"),n=N("Processing");try{const o=g.createReadStream(e.filePath),s=await e.client.uploads.upload({data:o,filename:i,datasetId:e.id,datasetName:e.name,datasetDescription:e.description,attribution:e.attribution,sourceSrid:e["source-srid"],targetSrid:e["target-srid"],schema:e.schema}),d=s.id,l=x=>new Promise(A=>{setTimeout(A,x)});let m,u=s.status,y=a;for(;;)if(await l(md),m=await e.client.uploads.get(d),m.status!==u)if(u=m.status,u==="ingesting")a.succeed(),y=t,y.start();else if(u==="processing")a.succeed(),t.start(),t.succeed(),y=n,y.start();else if(u==="done"){a.succeed(),t.succeed(),n.succeed(),v(`Dataset uploaded with id '${m.options.datasetId}' (${pd(m.size)})`);break}else{y.fail(),c(m.error.message),w(m.error.stderr||m.error.stdout);break}}catch(o){a.stop(),t.stop(),n.stop(),c(o)}},gd={__proto__:null,builder:fd,command:ud,desc:yd,handler:bd},hd="upload <command>",wd="Upload commands",$d=e=>e.command(ld).command(gd).demandCommand(1),_d={__proto__:null,builder:$d,command:hd,desc:wd},xd=["create <email>"],vd="Create a user",Sd=e=>e.option("firstName",{description:"First name",type:"string"}).option("lastName",{description:"Last name",type:"string"}).option("roles",{alias:"r",description:"Roles (comma-separated)",type:"array"}).option("emailVerified",{description:"Email verified",type:"boolean"}).option("enabled",{alias:"e",description:"Login enabled",type:"boolean"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function jd(e){const i=e.client;try{if(e.interactive){const n=await f.prompt([{type:"input",name:"firstName",message:"First name",default:e.firstName},{type:"input",name:"lastName",message:"Last name",default:e.lastName},{type:"input",name:"roles",message:"Roles (comma separated)",default:(e.roles??[]).join(", ")},{type:"confirm",name:"emailVerified",message:"Verified?",default:Object.prototype.hasOwnProperty.call(e,"emailVerified")?e.emailVerified:!0},{type:"confirm",name:"enabled",message:"Enabled?",default:Object.prototype.hasOwnProperty.call(e,"enabled")?e.enabled:!0},{type:"password",name:"password",message:"Password"},{type:"password",name:"confirmPassword",message:"Confirm password"}]);if(n.password!==n.confirmPassword)throw new Error("Passwords do not match");e.firstName=n.firstName,e.lastName=n.lastName,e.roles=n.roles.split(",").map(o=>o.trim()),e.emailVerified=n.emailVerified,e.enabled=n.enabled,e.password=n.password}const a=await i.auth.users.create({email:e.email,firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled,password:e.password}),t=te();await i.auth.users.update(a.id,{passwordResetToken:t}),await i.auth.resetPassword(a.email,e.password,a.applicationId,t),e.format==="table"?r(a,"table"):r(a,"json")}catch(a){c(a)}}const Dd={__proto__:null,builder:Sd,command:xd,desc:vd,handler:jd},kd=["delete <email>","rm","del"],Ad="Delete a user",Nd=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function Id({client:e,email:i,yes:a}){try{const t=await p(i,e.auth.users,"email");let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${t.email}'`,default:!1})).confirmed),n&&(await e.auth.users.delete(t.id),w("Deleted"))}catch(t){c(t)}}const Pd={__proto__:null,builder:Nd,command:kd,desc:Ad,handler:Id},Od=["list [email]","ls","$0"],Ed="List users",qd=e=>e.option("limit",{alias:"n",type:"number",describe:"Limit to n results"}).option("sort",{alias:["s"],type:"string",describe:"Sort by attribute"}).conflicts("email",["limit","sort"]).positional("email",{describe:"User email (or unique prefix)",type:"string"});async function Td({client:e,email:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.auth.users,"email");r(o,a)}else{let o=await e.auth.users.list({limit:t,sort:n});a==="table"?(o=o.map(s=>({...s,name:`${s.firstName} ${s.lastName}`})),r(o,"table",["id","email","name","emailVerified","enabled"])):r(o,a??"json")}}catch(o){c(o)}}const Md={__proto__:null,builder:qd,command:Od,desc:Ed,handler:Td},Ld=["metadata <email>","meta"],Cd="Get/set resource metadata",Ud=e=>e.positional("email",{describe:"User email (or unique prefix)",type:"string"}).option("set",{type:"array",describe:"One or more key/values to set in form <key>=<value>. Use <key>= to delete a key."});async function Jd({client:e,email:i,set:a}){try{if(a){let t=await p(i,e.auth.users,"email");const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.auth.users.deleteMetadata(t.id,o):await e.auth.users.setMetadata(t.id,o,s)}t=await p(i,e.auth.users,"email"),r(t.metadata,"json")}else{const t=await p(i,e.auth.users,"email");r(t.metadata,"json")}}catch(t){c(t)}}const Rd={__proto__:null,builder:Ud,command:Ld,desc:Cd,handler:Jd},Fd=["update <email>"],Kd="Update a user",zd=e=>e.option("firstName",{description:"First name",type:"string"}).option("lastName",{description:"Last name",type:"string"}).option("roles",{alias:"r",description:"Roles (comma-separated)",type:"array"}).option("emailVerified",{description:"Email verified",type:"boolean"}).option("enabled",{alias:"e",description:"Login enabled",type:"boolean"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("email",{describe:"Email (or unique prefix)",type:"string"});async function Zd(e){const i=e.client,a=e.email;try{const t=await p(a,i.auth.users,"email");if(e.interactive){const o=await f.prompt([{type:"input",name:"firstName",message:"First name",default:e.firstName||t.firstName},{type:"input",name:"lastName",message:"Last name",default:e.lastName||t.lastName},{type:"input",name:"roles",message:"Roles (comma separated)",default:(e.roles||t.roles).join(", ")},{type:"confirm",name:"emailVerified",message:"Verified?",default:Object.prototype.hasOwnProperty.call(e,"emailVerified")?e.emailVerified:t.emailVerified},{type:"confirm",name:"enabled",message:"Enabled?",default:Object.prototype.hasOwnProperty.call(e,"enabled")?e.enabled:t.enabled},{type:"confirm",name:"changePassword",message:"Change password?",default:!1},{type:"password",name:"password",message:"New password",when:({changePassword:s})=>s},{type:"password",name:"confirmPassword",message:"Confirm password",when:({changePassword:s})=>s}]);if(o.changePassword){if(o.password!==o.confirmPassword)throw new Error("Passwords do not match");e.password=o.password}e.firstName=o.firstName,e.lastName=o.lastName,e.roles=o.roles.split(",").map(s=>s.trim()),e.emailVerified=o.emailVerified,e.enabled=o.enabled}if(e.password){const o=te();await i.auth.users.update(t.id,{passwordResetToken:o}),await i.auth.resetPassword(t.email,e.password,t.applicationId,o)}const n=await i.auth.users.update(t.id,{firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled});e.format==="table"?r(n,"table"):r(n,"json")}catch(t){c(t)}}const Gd={__proto__:null,builder:zd,command:Fd,desc:Kd,handler:Zd},Vd="users <command>",Hd="User commands",Bd=e=>e.command(Md).command(Dd).command(Pd).command(Rd).command(Gd).demandCommand(1),Wd={__proto__:null,builder:Bd,command:Vd,desc:Hd},Qd="@emuanalytics/flow-cli",Xd="2.2.2",Yd="module",ec="dist/index.mjs",tc="Robin Summerhill <robin.summerhill@emu-analytics.com>",ic="Copyright 2018 Emu Analytics Limited",ac={flow:"dist/index.mjs"},nc=["dist","maputnik"],oc={start:"node dist/index.mjs",typecheck:"tsc --noEmit",build:"unbuild",clean:"rimraf dist",lint:"eslint src",format:'prettier "src/**/*.ts" --write',prepublishOnly:"npm run build",prebuild:"npm run clean",precommit:"lint-staged"},sc={"@eslint/js":"^10.0.1","@types/cli-spinner":"^0.2.0","@types/express":"^4.17.25","@types/humanize-plus":"^1.8.0","@types/node":"^20.0.0","@types/table":"^4.0.5","@types/uuid":"^8.3.4","@types/yauzl":"^2.9.1",eslint:"^10.3.0","lint-staged":"^16.4.0",prettier:"^3.0.0",typescript:"^5.9.2","typescript-eslint":"^8.59.2",unbuild:"^3.6.1"},rc={"@emuanalytics/flow-engine-client":"^2.2.2","@modelcontextprotocol/sdk":"^1.12.0",JSONStream:"^1.3.5",chalk:"^5.6.2","cli-spinner":"^0.2.10",colorette:"^2.0.19","csv-stringify":"^5.3.3","debounce-promise":"^3.1.0",execa:"^5.1.1",express:"^4.16.3","find-up":"^8.0.0","humanize-plus":"^1.8.2",inquirer:"^13.4.3","loose-json":"^1.1.2","node-emoji":"^2.2.0",open:"^11.0.0",ora:"^9.4.0","replace-in-file":"^5.0.2","string-width":"^8.2.1",table:"^6.9.0",tslib:"^2.8.1",uuid:"^8.3.2",yargs:"^18.0.0",yauzl:"^2.10.0",zod:"^3.23.0"},dc={"@types/serve-static":"^1.15.0"},cc="2a944ed12ef9653860dc98d95595e15b89f750de",lc={name:Qd,version:Xd,type:Yd,main:ec,author:tc,license:ic,bin:ac,files:nc,scripts:oc,devDependencies:sc,dependencies:rc,overrides:dc,"lint-staged":{"./src/**/*.ts":["eslint"]},gitHead:cc},pc="version",mc="Display Flo.w Engine version information",uc=async({host:e,apiKey:i,client:a,format:t})=>{try{const n=await a.config(),o={"CLI version":lc.version,"Flo.w version":n.version.tag,"Flo.w host":e,"API key":i};r(o,t)}catch(n){c(n.message)}},yc={__proto__:null,command:pc,desc:mc,handler:uc},fc="mcp",bc="Start the Flow MCP server for AI agent access via the Model Context Protocol";function gc(e){return e}const P={limit:h.number().int().min(1).max(1e3).optional().describe("Maximum number of results to return"),offset:h.number().int().min(0).optional().describe("Number of results to skip for pagination"),filter:h.string().optional().describe('JSON object filter expression, e.g. {"name":"foo"} or {"active":true}'),sort:h.string().optional().describe("Field name to sort by, prefix with - for descending (e.g. -createdAt)"),attributes:h.string().optional().describe("Comma-separated list of attributes to include in the response")};function O(e){return{limit:e.limit,offset:e.offset,filter:e.filter?JSON.parse(e.filter):void 0,sort:e.sort,attributes:e.attributes}}async function hc(e){const{client:i}=e,a=new Re({name:"flow-mcp",version:"1.0.0"});a.registerTool("get_current_time",{description:"Get the current UTC date and time as an ISO 8601 string. Use this when you need to compare timestamps such as pipeline heartbeatAt or startedAt to determine if something is currently active.",inputSchema:{},outputSchema:{datetime:h.string().datetime().describe("Current UTC date and time as ISO 8601")}},async()=>{const n=new Date().toISOString();return{content:[{type:"text",text:n}],structuredContent:{datetime:n}}}),a.registerTool("list_datasets",{description:"List datasets available in flow-engine with minimal fields (id, name, description, type). Use this to discover what datasets exist, then call get_dataset for full schema and attribute details on a specific dataset. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results.",inputSchema:{...P,limit:h.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:h.string().optional().default("id,name,description,type").describe("Comma-separated fields to return (default: id, name, description, type)")}},async n=>{console.error("Tool: list_datasets called",n);const o=await i.datasets.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("get_dataset",{description:"Get details of a specific dataset by ID, including its schema, attributes, and metadata",inputSchema:{id:h.string().describe("Dataset ID")}},async({id:n})=>{const o=await i.datasets.get(n);return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("query_dataset",{description:"Query the data inside a dataset. Supports filtering, aggregation, and spatial queries. Returns features as GeoJSON or plain JSON depending on the dataset type.",inputSchema:{id:h.string().describe("Dataset ID to query"),query:h.string().optional().describe("JSON query object \u2014 see flow-engine dataset query documentation for filter/aggregate syntax"),format:h.enum(["json","geojson","csv"]).optional().default("json").describe("Output format"),limit:h.number().int().min(1).max(1e4).optional().describe("Maximum number of features to return"),offset:h.number().int().min(0).optional().describe("Number of features to skip")}},async({id:n,query:o,format:s,limit:d,offset:l})=>{const m=o?JSON.parse(o):{};d!==void 0&&(m.limit=d),l!==void 0&&(m.offset=l);const u=await i.datasets.query(n,m,{format:s??"json"});return{content:[{type:"text",text:typeof u=="string"?u:JSON.stringify(u,null,2)}]}}),a.registerTool("list_styles",{description:"List all Mapbox GL styles (named map configurations) available in flow-engine",inputSchema:P},async n=>{const o=await i.styles.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_tile_sources",{description:"List all tile sources in flow-engine. Tile sources are raster or vector tile sets used as base layers or overlays.",inputSchema:P},async n=>{const o=await i.tileSources.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_pipelines",{description:"List data pipelines in flow-engine with minimal fields (id, environment, description, startedAt, heartbeatAt). Use this to discover what pipelines exist, then call get_pipeline for full details on a specific pipeline. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results.",inputSchema:{...P,limit:h.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:h.string().optional().default("id,environment,description,startedAt,heartbeatAt").describe("Comma-separated fields to return (default: id, environment, description, startedAt, heartbeatAt)")}},async n=>{const o=await i.pipelines.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("get_pipeline",{description:"Get details of a specific pipeline by ID, including its components and configuration",inputSchema:{id:h.string().describe("Pipeline ID")}},async({id:n})=>{const o=await i.pipelines.get(n,{join:"components"});return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_databases",{description:"List external database connections registered in flow-engine",inputSchema:P},async n=>{const o=await i.databases.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}),a.registerTool("list_applications",{description:"List applications registered in flow-engine",inputSchema:P},async n=>{const o=await i.applications.list(O(n));return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}});const t=new Fe;await a.connect(t)}const wc={__proto__:null,builder:gc,command:fc,describe:bc,handler:hc},he=$e([".flowrc",".flowrc.json"]),$c=he?JSON.parse(g.readFileSync(he).toString("utf8")):{},_c=e=>(e.client=new Z(e.host,e.apiKey),e),we=_e(xe(process.argv));we.config($c).env("FLOW").option("k",{alias:"apiKey",demandOption:!0,describe:"Flo.w Engine API key",type:"string",global:!0}).option("h",{alias:"host",default:"https://flow.emu-analytics.net",describe:"Flo.w Engine host URL",type:"string"}).option("f",{alias:"format",default:"table",describe:"Output format",type:"string",choices:["table","csv","json","geojson"]}).command(ft).command($t).command(si).command(Ji).command(oa).command(an).command(xn).command(Qn).command(ko).command(Vo).command(qs).command(hr).command(od).command(_d).command(Wd).command(yc).command(wc).demandCommand(1).wrap(Math.min(100,we.terminalWidth())).help().version().middleware([_c]).parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emuanalytics/flow-cli",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "author": "Robin Summerhill <robin.summerhill@emu-analytics.com>",
@@ -40,7 +40,7 @@
40
40
  "unbuild": "^3.6.1"
41
41
  },
42
42
  "dependencies": {
43
- "@emuanalytics/flow-engine-client": "^2.2.1",
43
+ "@emuanalytics/flow-engine-client": "^2.2.2",
44
44
  "@modelcontextprotocol/sdk": "^1.12.0",
45
45
  "JSONStream": "^1.3.5",
46
46
  "chalk": "^5.6.2",
@@ -74,5 +74,5 @@
74
74
  "eslint"
75
75
  ]
76
76
  },
77
- "gitHead": "5b5244b47512c723261fdd56742f8c6b71105d1e"
77
+ "gitHead": "2a944ed12ef9653860dc98d95595e15b89f750de"
78
78
  }