@emuanalytics/flow-cli 2.2.2 → 2.2.3
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/index.mjs +14 -14
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Client as Z}from"@emuanalytics/flow-engine-client";import{findUpSync as $e}from"find-up";import*as g from"fs";import _e from"yargs";import{hideBin as xe}from"yargs/helpers";import S from"chalk";import ve from"csv-stringify/lib/sync.js";import Se from"loose-json";import{emojify as C}from"node-emoji";import{table as je}from"table";import De from"string-width";import f from"inquirer";import N from"ora";import*as D from"path";import{join as I}from"path";import ke from"JSONStream";import{Writable as Ae,pipeline as Ne}from"stream";import{Spinner as Ie}from"cli-spinner";import*as b from"colorette";import{get as Pe}from"https";import*as Oe from"url";import{fileURLToPath as Ee}from"url";import{fromBuffer as qe}from"yauzl";import{promisify as Te}from"util";import Me from"execa";import Le from"replace-in-file";import*as Ce from"http";import G from"express";import Ue from"debounce-promise";import V from"open";import Je from"humanize-plus";import{v4 as te}from"uuid";import{McpServer as Re}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Fe}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as h}from"zod";const j=console.log;function c(e){typeof e=="string"?j(C(S.bold.red(e))):(j(C(S.bold.red(e.message))),e.error?.message&&w(e.error.message))}function v(e){j(C(S.bold(e)))}function w(e){j(C(S.dim(e)))}function r(e,i="table",a=null,t={}){if(!e)return c("Null object");if(i==="table"){let n;if(Array.isArray(e)){if(e.length===0)return j("No Items");if(typeof e[0]=="object"){const s=a||Object.keys(e[0]);n=[["",...s.map(d=>S.bold.green(d))]],e.forEach((d,l)=>{const m=[l.toString(),...s.map(u=>U(d[u]))];n.push(m)})}else n=[["",S.bold.green("Value")]],e.forEach((s,d)=>{const l=[d.toString(),U(s)];n.push(l)})}else n=[[S.bold.green("Property"),S.bold.green("Value")],...Object.keys(e).map(s=>[s,U(e[s])])];const o=Ke(n).map(s=>({width:t.noWrap?s:Math.min(s,36),truncate:200}));console.log(je(n,{columns:o}))}else i==="csv"?(Array.isArray(e)||(e=[e]),j(ve(e,{header:!0}))):j(JSON.stringify(e,null,2))}function Ke(e){if(!e[0])throw new Error("Dataset must have at least one row.");const i=new Array(e[0].length).fill(0);return e.forEach(a=>{ze(a).forEach((t,n)=>{i[n]<t&&(i[n]=t)})}),i}function ze(e){return e.map(i=>De(i))}function U(e){return Array.isArray(e)?"<array>":e===null?S.grey("null"):typeof e>"u"?S.grey("undefined"):typeof e=="object"?"color"in e&&"value"in e?S.hex(e.color)(U(e.value)):"<object>":e.toString().replace(/[^ -~]/g," ")}async function p(e,i,a="id",t,n){const o=await i.list({[`filter[${a}]`]:`~${e}`,sort:n||a,join:t});if(o.length===0)throw new Error("Not found");if(o.length>1&&o[0].id!==e)throw new Error(`Multiple matches for '${e}': ${o.map(s=>s[a]).join(", ")}`);return o[0]}function E(e){return!isNaN(parseFloat(e))&&isFinite(e)}function J(e){return!e||e.length===0||(typeof e=="string"&&(e=e.split(" ")),e.length!==4)?!1:!e.find(i=>!E(i))}function R(e){return!e||e.length===0?"":e.map(i=>i.toString()).join(" ")}function H(e){return e==null?null:(typeof e=="string"&&(e=e.split(" ")),e.map(i=>parseFloat(i)))}function B(e){return!Array.isArray(e)||e.length===0?"":e.join(", ")}function Ze(e){return e&&(e=e.trim()),e?e.split(",").map(i=>i.trim()):[]}function k(e){try{return e.reduce((i,a)=>{const[t,n]=a.split("=");try{i[t||null]=Se(n)}catch{i[t]=n}return i},{})}catch(i){throw new Error("Bad format for key/value: specify key/value pairs as <key>=<value>",{cause:i})}}function W(e,i){const a={...e};for(const t of i)Array.isArray(a[t])&&(a[t]=a[t].join(", "));return a}function ie(...e){return e.length===0?[]:e.length===1?e[0]===void 0||e[0]===null?[]:Array.isArray(e[0])?e[0]:[e[0]]:e}const Ge="create",Ve="Create a new API key",He=e=>e.option("id",{describe:"ID for new API key",type:"string"}).option("applicationId",{describe:"Application ID for new API key",type:"string"}).option("description",{describe:"Description for new API key",type:"string"}).option("type",{alias:"t",describe:"Type of key",type:"string",choices:["app","master"]}).option("permissions",{describe:"Permissions for new API key",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),Be=async e=>{try{if(e.interactive){const a=await f.prompt([{type:"input",name:"id",message:"ID of new key (leave blank to auto-generate)",default:e.id,validate:ae},{type:"input",name:"description",message:"Name for new API key",default:e.description},{type:"select",name:"type",message:"Type of API key to create",choices:["app","master"],default:e.type},{type:"input",name:"permissions",message:"Permissions for new API key (JSON)",default:e.permissions,validate:t=>{try{return JSON.parse(t),!0}catch{return!1}}},{type:"confirm",name:"overrideApplicationId",message:"Override Application ID",default:!1,when:({type:t})=>t==="app"},{type:"input",name:"applicationId",message:"Application ID of new key",default:e.applicationId,validate:ae,when:({overrideApplicationId:t})=>t}]);e.id=a.id,e.description=a.description,e.type=a.type,e.permissions=a.permissions,e.applicationId=a.applicationId}let i={description:e.description,type:e.type,permissions:JSON.parse(e.permissions)};e.id&&(i.id=e.id),e.applicationId&&(i.applicationId=e.applicationId),i=await e.client.apiKeys.create(i),r(i,"table")}catch(i){c(i)}};function ae(e){return e?!!e.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i):!0}const We={__proto__:null,builder:He,command:Ge,desc:Ve,handler:Be},Qe=["delete <id>","rm","del"],Xe="Delete an API key",Ye=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"API key id (or unique prefix)",type:"string"});async function et({client:e,id:i,yes:a}){try{const t=await p(i,e.apiKeys);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.apiKeys.delete(t.id),w("Deleted"))}catch(t){c(t)}}const tt={__proto__:null,builder:Ye,command:Qe,desc:Xe,handler:et},it=["list [id]","ls","$0"],at="List API keys",nt=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:"API key id (or unique prefix",type:"string"}).conflicts("id",["limit","sort"]);async function ot({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.apiKeys);r(s,a)}else o=await e.apiKeys.list({limit:t,sort:n}),a==="table"?(o=o.map(s=>({...s,permissions:JSON.stringify(s.permissions)})),r(o,"table",["id","description","type","permissions"])):r(o,a??"json")}catch(o){c(o)}}const st={__proto__:null,builder:nt,command:it,desc:at,handler:ot},rt=["metadata <id>","meta"],dt="Get/set resource metadata",ct=e=>e.positional("id",{describe:"API key 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 lt({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.apiKeys);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.apiKeys.deleteMetadata(t.id,o):await e.apiKeys.setMetadata(t.id,o,s)}t=await p(i,e.apiKeys),r(t.metadata,"json")}else{const t=await p(i,e.apiKeys);r(t.metadata,"json")}}catch(t){c(t)}}const pt={__proto__:null,builder:ct,command:rt,desc:dt,handler:lt},mt="apikeys <command>",ut="API key commands",yt=e=>e.command(st).command(We).command(tt).command(pt).demandCommand(1),ft={__proto__:null,builder:yt,command:mt,desc:ut},bt=["appconfig"],gt="Get/set app config",ht=e=>e.option("get",{type:"string",describe:"One or more key/values to set in form <key>=<value>. Supports dotted notation for keys"}).option("set",{type:"array",describe:"Set one or more key/values (in form <key>=<value>.) Use <key>= to delete a key. Supports dotted notation for keys"});async function wt({client:e,get:i,set:a}){try{if(a){const t=k(a);for(const n of Object.keys(t)){const o=t[n];o===""?await e.appconfig.delete(n):await e.appconfig.set(n,o)}w("Done")}else{const t=await e.appconfig.get(i);r(t,"json")}}catch(t){c(t)}}const $t={__proto__:null,builder:ht,command:bt,desc:gt,handler:wt},_t="create <name>",xt="Create a new Flo.w application",vt=e=>e.positional("name",{describe:"Name for new application",type:"string"}).option("description",{alias:"d",describe:"Description for new application",type:"string"}).option("contact",{alias:"c",describe:"Contact email for new application",type:"string"}).option("developer-key",{describe:"Create default developer key",type:"boolean",default:"true"}).option("public-key",{describe:"Create default public key",type:"boolean",default:"false"}).option("authorized-domains",{describe:"Authorized email domains",type:"array",default:["emu-analytics.com"]}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),St=async e=>{try{if(e.interactive){const o=await f.prompt([{type:"input",name:"name",message:"Name for new application",default:e.name},{type:"input",name:"description",message:"Description for new application",default:e.description},{type:"input",name:"contact",message:"Contact email for new application",default:e.contact},{type:"confirm",name:"developerKey",message:"Create default developer key (all permissions)?",default:e.developerKey},{type:"confirm",name:"publicKey",message:"Create default public key (minimal permissions)?",default:e.publicKey},{type:"input",name:"authorizedDomains",message:"Authorized email domains (comma-separated)",default:e.authorizedDomains.join(", ")}]);e.name=o.name,e.description=o.description,e.contact=o.contact,e.developerKey=o.developerKey,e.publicKey=o.publicKey,e.authorizedDomains=o.authorizedDomains.split(",").map(s=>s.trim())}let i={name:e.name,description:e.description,contact:e.contact,metadata:{auth:{authorisedEmailDomains:e.authorizedDomains},datasetAccessOptions:{allowFn:!0,maxQueryLimit:1e4,defaultQueryLimit:1e3,statementTimeout:1e4}}};i=await e.client.applications.create(i);let a,t;e.developerKey&&(a={description:`${e.name} developer key`,applicationId:i.id,type:"app",permissions:{"*":{"*":"*"}}},a=await e.client.apiKeys.create(a),await e.client.appconfig.set("dummy","dummy",{apiKey:a.id}),await e.client.appconfig.delete("dummy",{apiKey:a.id})),e.publicKey&&(t={description:`${e.name} public key`,applicationId:i.id,type:"app",permissions:{"*":{execute:"*"},datasets:{read:"*"},tiles:{read:"*"},pubsub:{read:"*"},appconfig:{read:"*"}}},t=await e.client.apiKeys.create(t),i.metadata.auth.defaultApiKey=t.id,await e.client.applications.update(i.id,i));const n={applicationId:i.id,developerApiKey:a?a.id:"none",publicApiKey:t?t.id:"none"};j(`Application ${i.name} created`),r(n,"table")}catch(i){c(i)}},jt={__proto__:null,builder:vt,command:_t,desc:xt,handler:St},Dt=["delete <id>","rm","del"],kt="Delete an application",At=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function Nt({client:e,id:i,yes:a}){try{const t=await p(i,e.applications);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.applications.delete(t.id),w("Deleted"))}catch(t){c(t)}}const It={__proto__:null,builder:At,command:Dt,desc:kt,handler:Nt},Pt=["export <file> <id>"],Ot="Export an application to JSON",Et=e=>e.positional("file",{describe:"Path to output JSON file",type:"string"}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function qt({client:e,id:i,file:a}){try{const t=await p(i,e.applications);a=D.resolve(a);const n=N(`Exporting application: ${t.name}`).start(),o=g.createWriteStream(a,{encoding:"utf8"});o.on("error",d=>{n.fail(),c(d),process.exit(1)}),o.write(`{
|
|
3
|
-
`);const
|
|
4
|
-
`)}
|
|
5
|
-
`),o.end(),n.succeed()}catch(t){c(t)}}const Tt={__proto__:null,builder:Et,command:Pt,desc:Ot,handler:qt},Mt=["import <file> [<id>]"],Lt="Import an application from JSON",Ct=e=>e.positional("file",{describe:"Path to input JSON file",type:"string"}).positional("id",{describe:"Application id (omit to use id from file)",type:"string"});async function Ut({client:e,id:i,file:a}){try{a=D.resolve(a);const t=new Date().toISOString(),n=N(`Importing from: ${a}`).start(),o=g.createReadStream(a,{encoding:"utf8"}),s=ke.parse([!0,{emitPath:!0}]);let d=0;const l=new Ae({objectMode:!0,write(m,u,y){const x=m.path[0],A=m.path[1],_=m.value,L=x.split(".").reduce((K,z)=>K?.[z],e);(typeof _!="object"||!L)&&y(new Error(`Bad file format: ${x}[${A}]`)),_.createdAt&&(_.createdAt=t),_.updatedAt&&(_.updatedAt=t),i&&(x==="applications"?_.id=i:_.applicationId=i),L.create(_,{applicationId:_.applicationId}).then(()=>{n.text=`Imported ${x}[${A}]`,d++,y()}).catch(y)}});Ne(o,s,l,m=>{m&&(n.fail(),c(m),process.exit(1)),n.succeed(`Imported ${d} resource${d>1?"s":""}`)})}catch(t){c(t)}}const Jt={__proto__:null,builder:Ct,command:Mt,desc:Lt,handler:Ut},Rt="auth.authorisedEmailDomains",Ft=["list [id]","ls","$0"],Kt="List applications",zt=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:"Application id (or unique prefix)",type:"string"}).conflicts("id",["limit","sort"]);async function Zt({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.applications),d=await e.applications.getMetadata(s.id,Rt);r({...s,authorisedEmailDomains:B(d)},a)}else o=await e.applications.list({limit:t,sort:n}),a==="table"?r(o,"table",["id","name","description","contact"]):r(o,a??"json")}catch(o){c(o)}}const Gt={__proto__:null,builder:zt,command:Ft,desc:Kt,handler:Zt},Vt=["metadata <id>","meta"],Ht="Get/set resource metadata",Bt=e=>e.positional("id",{describe:"Application 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 Wt({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.applications);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.applications.deleteMetadata(t.id,o):await e.applications.setMetadata(t.id,o,s)}t=await p(i,e.applications),r(t.metadata,"json")}else{const t=await p(i,e.applications);r(t.metadata,"json")}}catch(t){c(t)}}const Qt={__proto__:null,builder:Bt,command:Vt,desc:Ht,handler:Wt},ne="auth.authorisedEmailDomains",Xt=["update <id>"],Yt="Update an application",ei=e=>e.option("name",{alias:"n",description:"Dataset name",type:"string"}).option("description",{alias:"d",description:"Dataset description",type:"string"}).option("domains",{description:"Authorized self-signup email domains",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function ti(e){const i=e.client,a=e.id;try{const t=await p(a,i.applications),n=await i.applications.getMetadata(t.id,ne);if(e.interactive){const s=await f.prompt([{type:"input",name:"name",message:"Name",default:e.name||t.name},{type:"input",name:"description",message:"Description",default:e.description||t.description},{type:"input",name:"contact",message:"Contact",default:e.contact||t.contact},{type:"input",name:"domains",message:"Authorized self-signup email domains (comma-separated)",default:B(e.domains||n)}]);e.name=s.name,e.description=s.description,e.contact=s.contact,e.domains=Ze(s.domains)}await i.applications.setMetadata(t.id,ne,e.domains);const o=await i.applications.update(t.id,{id:e.id,name:e.name,description:e.description,contact:e.contact});e.format==="table"?r({...o,authorisedEmailDomains:B(e.domains)},"table"):r(o,"json")}catch(t){c(t)}}const ii={__proto__:null,builder:ei,command:Xt,desc:Yt,handler:ti},ai=["applications <command>","apps"],ni="Application commands",oi=e=>e.command(Gt).command(jt).command(It).command(Tt).command(Jt).command(Qt).command(ii).demandCommand(1),si={__proto__:null,builder:oi,command:ai,desc:ni},ri="create <id>",di="Create a new Contact Point",ci=e=>e.positional("id",{describe:"Contact point id",type:"string"}).option("type",{describe:"Type of new contact point",type:"string"}).option("description",{describe:"Description for new contact point",type:"string"}).option("templates",{description:"Contact point templates (comma-separated)",type:"array"}).option("recipients",{description:"Recipients (comma-separated)",type:"array"}).option("cp-host",{describe:"Contact point host (where applicable)",type:"string"}).option("cp-port",{describe:"Contact point port (where applicable)",type:"number"}).option("cp-user",{describe:"Contact point user (where applicable)",type:"string"}).option("password",{describe:"Contact point password (where applicable)",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),li=async e=>{try{if(e.interactive){const a=await e.client.contactpoints.drivers(),t=await f.prompt([{type:"select",name:"type",message:"Contact point type",choices:a,default:e.type??"email"},{type:"input",name:"description",message:"Description for new contact point",default:e.description},{type:"input",name:"templates",message:"Templates (comma separated)",default:(e.templates??[]).join(", ")},{type:"input",name:"recipients",message:"Recipients (comma separated)",default:(e.recipients??[]).join(", ")},{type:"input",name:"host",message:"Contact point host (where applicable)",default:e.cpHost},{type:"number",name:"port",message:"Contact point port (where applicable)",default:e.cpPort??587},{type:"input",name:"user",message:"Contact point user (where applicable)",default:e.cpUser},{type:"password",name:"password",message:"Contact point password (where applicable)",default:e.cpPassword}]);e.type=t.type,e.description=t.description?t.description:null,e.cpHost=t.host?t.host:null,e.cpPort=t.port?t.port:null,e.cpUser=t.user?t.user:null,e.cpPassword=t.password?t.password:null,e.templates=t.templates.split(",").map(n=>n.trim()).filter(n=>n.length),e.recipients=t.recipients.split(",").map(n=>n.trim()).filter(n=>n.length)}let i={id:e.id,type:e.type,description:e.description,templates:e.templates,recipients:e.recipients,host:e.cpHost,port:e.cpPort,user:e.cpUser,password:e.cpPassword};i=await e.client.contactpoints.create(i),r(W(i,["templates","recipients"]),"table")}catch(i){c(i)}},pi={__proto__:null,builder:ci,command:ri,desc:di,handler:li},mi=["delete <id>","rm","del"],ui="Delete a contact point",yi=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Contact Point id (or unique prefix)",type:"string"});async function fi({client:e,id:i,yes:a}){try{const t=await p(i,e.contactpoints);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.contactpoints.delete(t.id),w("Deleted"))}catch(t){c(t)}}const bi={__proto__:null,builder:yi,command:mi,desc:ui,handler:fi},gi=["list [id]","ls","$0"],hi="List contact points",wi=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:"Contact point id (or unique prefix)",type:"string"});async function $i({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.contactpoints);r(W(s,["templates","recipients"]),a)}else o=await e.contactpoints.list({limit:t,sort:n}),a==="table"?r(o.map(s=>W(s,["templates","recipients"])),"table",["id","type","description","templates","recipients"]):r(o,a??"json")}catch(o){c(o)}}const _i={__proto__:null,builder:wi,command:gi,desc:hi,handler:$i},xi=["metadata <id>","meta"],vi="Get/set resource metadata",Si=e=>e.positional("id",{describe:"Resource 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 ji({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.contactpoints);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.contactpoints.deleteMetadata(t.id,o):await e.contactpoints.setMetadata(t.id,o,s)}t=await p(i,e.contactpoints),r(t.metadata,"json")}else{const t=await p(i,e.contactpoints);r(t.metadata,"json")}}catch(t){c(t)}}const Di={__proto__:null,builder:Si,command:xi,desc:vi,handler:ji},ki=["send <id>"],Ai="Send a notification to a contact point",Ni=e=>e.positional("id",{describe:"Contact point id",type:"string"}).option("template",{description:"Notification template",type:"string"}).option("subject",{description:"Notification subject",type:"string"}).option("recipients",{description:"Notification recipients (comma separated)",type:"array"}).option("data",{description:"Notification data (JSON-formatted)",type:"string",default:"{}"}),Ii=async({client:e,id:i,template:a,subject:t,recipients:n,data:o})=>{try{const s=await p(i,e.contactpoints);await e.contactpoints.send(s.id,{subject:t,template:a,recipients:n,data:JSON.parse(o)}),w("Notification sent")}catch(s){c(s)}},Pi={__proto__:null,builder:Ni,command:ki,desc:Ai,handler:Ii},Oi=["update <id>"],Ei="Update a contact point",qi=e=>e.option("description",{alias:"d",description:"Contact point description",type:"string"}).option("templates",{description:"Contact point templates (comma-separated)",type:"array"}).option("recipients",{description:"Recipients (comma-separated)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Contact point id (or unique prefix)",type:"string"});async function Ti(e){const i=e.client,a=e.id;try{const t=await p(a,i.contactpoints);if(e.interactive){const o=await f.prompt([{type:"input",name:"description",message:"Description",default:e.description||t.description},{type:"input",name:"templates",message:"Templates (comma separated)",default:(e.templates||t.templates).join(", ")},{type:"input",name:"recipients",message:"Recipients (comma separated)",default:(e.recipients||t.recipients).join(", ")}]);e.description=o.description,e.templates=o.templates.split(",").map(s=>s.trim()).filter(s=>s.length),e.recipients=o.recipients.split(",").map(s=>s.trim()).filter(s=>s.length)}const n=await i.contactpoints.update(t.id,{id:e.id,description:e.description,templates:e.templates,recipients:e.recipients});e.format==="table"?r(n,"table"):r(n,"json")}catch(t){c(t)}}const Mi={__proto__:null,builder:qi,command:Oi,desc:Ei,handler:Ti},Li=["contactpoints <command>","cps"],Ci="Contact Point commands",Ui=e=>e.command(_i).command(pi).command(bi).command(Di).command(Pi).command(Mi).demandCommand(1),Ji={__proto__:null,builder:Ui,command:Li,desc:Ci},Ri="create",Fi="Create a new Database connection",Ki=e=>e.option("id",{describe:"ID for new database",type:"string"}).option("applicationId",{describe:"Application ID for new database",type:"string"}).option("name",{describe:"Name for new database",type:"string"}).option("description",{describe:"Description for new database",type:"string"}).option("driver",{describe:"Database driver",type:"string"}).option("db-host",{describe:"Database host",type:"string"}).option("db-port",{describe:"Database port",type:"number",default:5432}).option("db-database",{describe:"Database name",type:"string"}).option("db-user",{describe:"Database user",type:"string"}).option("db-password",{describe:"Database password",type:"string"}).option("default",{describe:"Default database",type:"boolean",default:!0}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),zi=async e=>{try{if(e.interactive){const a=await e.client.databases.drivers(),t=await f.prompt([{type:"input",name:"id",message:"ID of new database (leave blank to auto-generate)",default:e.id,validate:oe},{type:"input",name:"name",message:"Name for new database",default:e.description},{type:"input",name:"description",message:"Description for new database",default:e.description},{type:"select",name:"driver",message:"Database driver",choices:a,default:e.driver??"postgis"},{type:"input",name:"dbHost",message:"Database host",default:e.dbHost},{type:"number",name:"dbPort",message:"Database port",default:e.dbPort},{type:"input",name:"dbDatabase",message:"Database",default:e.dbDatabase},{type:"input",name:"dbUser",message:"Database user",default:e.dbUser},{type:"password",name:"dbPassword",message:"Database password",default:e.dbPassword},{type:"confirm",name:"default",message:"Is this the default database?",default:e.default},{type:"confirm",name:"overrideApplicationId",message:"Override Application ID",default:!1},{type:"input",name:"applicationId",message:"Application ID of new key",default:e.applicationId,validate:oe,when:({overrideApplicationId:n})=>n}]);e.id=t.id,e.name=t.name,e.description=t.description,e.driver=t.driver,e.dbHost=t.dbHost,e.dbPort=t.dbPort,e.dbDatabase=t.dbDatabase,e.dbUser=t.dbUser,e.dbPassword=t.dbPassword,e.default=t.default,e.applicationId=t.applicationId}let i={name:e.name,description:e.description,type:e.driver,host:e.dbHost,port:e.dbPort,db:e.dbDatabase,user:e.dbUser,password:e.dbPassword,default:e.default};e.id&&(i.id=e.id),e.applicationId&&(i.applicationId=e.applicationId),i=await e.client.databases.create(i),i.password="*****",r(i,"table")}catch(i){c(i)}};function oe(e){return e?!!e.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i):!0}const Zi={__proto__:null,builder:Ki,command:Ri,desc:Fi,handler:zi},Gi=["delete <id>","rm","del"],Vi="Delete a database",Hi=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Database id (or unique prefix)",type:"string"});async function Bi({client:e,id:i,yes:a}){try{const t=await p(i,e.databases);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.databases.delete(t.id),w("Deleted"))}catch(t){c(t)}}const Wi={__proto__:null,builder:Hi,command:Gi,desc:Vi,handler:Bi},Qi=["list [id]","ls","$0"],Xi="List databases",Yi=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:"Datavase id (or unique prefix)",type:"string"});async function ea({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.databases);r(s,a)}else o=await e.databases.list({limit:t,sort:n}),a==="table"?r(o,"table",["id","name","description","type","default"]):r(o,a??"json")}catch(o){c(o)}}const ta={__proto__:null,builder:Yi,command:Qi,desc:Xi,handler:ea},ia=["databases <command>","dbs"],aa="Database commands",na=e=>e.command(ta).command(Zi).command(Wi).demandCommand(1),oa={__proto__:null,builder:na,command:ia,desc:aa},sa=["attributes <id>","attrs"],ra="List dataset attributes",da=e=>e.positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function ca({client:e,id:i,format:a}){try{const t=await p(i,e.datasets);a==="table"?r(t.attributes,"table",["attribute","description","type","dbType","default","nullable","primaryKey"]):r(t.attributes,a??"json")}catch(t){c(t)}}const la={__proto__:null,builder:da,command:sa,desc:ra,handler:ca},pa="create <id>",ma="Create a new dataset",ua=e=>e.positional("id",{describe:"Dataset id",type:"string"}).option("name",{alias:"n",describe:"Name for new dataset",type:"string"}).option("description",{alias:"d",describe:"Description for new dataset",type:"string"}).option("databaseId",{describe:"ID of associated database (for multi-database apps only)",type:"string"}).option("type",{alias:"t",describe:"Type of dataset",type:"string",choices:["table","sql"]}).option("source",{alias:"s",description:"Dataset source",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("autodetect",{describe:"Autodetect geometry",type:"boolean"}).option("geometryType",{describe:"Geometry type (ignored when autodetect=true)",type:"string"}).option("srid",{describe:"Geometry SRID (ignored when autodetect=true)",type:"number"}).option("extent",{description:"Geometry extent x1,y1,x2,y2 (lng/lat) (ignored when autodetect=true)",type:"array"}),ya=async e=>{try{const i=await fa(e.host,e.apiKey),a=i.map(o=>({name:o.name,value:o.id,short:o.name})),t=["Point","LineString","Polygon","MultiPoint","MultiLineString","MultiPolygon","Geometry"];if(e.interactive){const o=await f.prompt([{type:"input",name:"name",message:"Name for new dataset",default:e.name},{type:"input",name:"description",message:"Description for new dataset",default:e.description},{type:"select",name:"databaseId",message:"Select the associated database",choices:a,default:e.databaseId||i.find(s=>s.default).id,when:()=>a.length>1},{type:"confirm",name:"autodetect",message:"Autodetect geometry?",default:e.autodetect},{type:"select",name:"geometryType",message:"Geometry Type",choices:t,default:e.geometryType,when:({autodetect:s})=>s===!1},{type:"input",name:"srid",message:"Geometry SRID",default:e.srid,validate:s=>E(s)||"Enter a number",when:({autodetect:s})=>s===!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (in SRID units)",default:R(e.extent??[-180,-90,180,90]),validate:s=>J(s)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({autodetect:s})=>s===!1},{type:"select",name:"type",message:"Type of dataset to create",choices:["table","sql"],default:e.type},{type:"input",name:"source",message:"Table name",default:e.source,when:({type:s})=>s==="table"},{type:"editor",name:"source",message:"SQL query",default:e.source,when:({type:s})=>s==="sql"}]);e.name=o.name,e.description=o.description,e.databaseId=o.databaseId,e.autodetect=o.autodetect,e.geometryType=o.autodetect?void 0:o.geometryType,e.srid=o.autodetect?void 0:o.srid,e.extent=o.autodetect?void 0:H(o.extent),e.type=o.type,e.source=o.source.trim()}let n={id:e.id,name:e.name,description:e.description,databaseId:e.databaseId,type:e.type,source:e.source,options:{autodetect:e.autodetect,geometryType:e.geometryType,srid:e.srid,extent:e.extent}};n=await e.client.datasets.create(n),e.format==="table"?r(n,"table",["id","name","description","type"]):r(n,e.format??"json")}catch(i){c(i)}};async function fa(e,i){return new Z(e,i).databases.list()}const ba={__proto__:null,builder:ua,command:pa,desc:ma,handler:ya},ga=["delete <id>","rm","del"],ha="Delete a dataset",wa=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function $a({client:e,id:i,yes:a}){try{const t=await p(i,e.datasets);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.datasets.delete(t.id),w("Deleted"))}catch(t){c(t)}}const _a={__proto__:null,builder:wa,command:ga,desc:ha,handler:$a},xa=["export <id> [destPath]"],va="Export a dataset",Sa=e=>e.hide("f").option("o",{alias:"output",default:"geojson",describe:"Output format",type:"string",choices:["geojson","gpkg","shapefile"]}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"}).positional("destPath",{describe:"Destination path",type:"string"});async function ja({client:e,id:i,output:a,destPath:t}){try{const n=await p(i,e.datasets);if(n.type!=="table")throw new Error(`Export of dataset type '${n.type}' not supported`);if(n.type!=="table")throw new Error(`Export of dataset type '${n.type}' not supported`);const o=e.datasets.getExportUrl(n.id,a),s=await e._get(o,{responseType:"stream"});if(!t){let d;switch(a){case"geojson":d="geojson";break;case"gpkg":d="gpkg";break;case"shapefile":d="shp.zip";break;default:throw new Error(`Unsupported output format: ${a}`)}t=`${n.id}.${d}`}s.on("end",()=>j(`Dataset exported to '${t}'`)),s.pipe(g.createWriteStream(t))}catch(n){c(n)}}const Da={__proto__:null,builder:Sa,command:xa,desc:va,handler:ja},ka=["list [id]","ls","$0"],Aa="List datasets",Na=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-tag",{alias:"t",type:"array",describe:"Filter datasets using a specific tag"}).conflicts("id",["limit","sort","filter-tag"]).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Ia({client:e,id:i,format:a,limit:t,sort:n,filterTag:o}){try{if(n??="id",i){const l=await p(i,e.datasets);a==="table"?r(se(l),"table"):r(l,a??"json");return}const s=!!o,d=(await e.datasets.list({sort:n,limit:s?void 0:t})).filter(l=>Pa(l,o)).slice(0,s?t:void 0);a==="table"?r(d.map(se),"table",["id","name","description","type","tags"]):r(d,a??"json")}catch(s){c(s)}}function se(e){return{...e,tags:e.metadata.tags?e.metadata.tags.join(","):""}}function Pa(e,i){return!i||e.metadata.tags&&i.every(a=>e.metadata.tags.includes(a))}const Oa={__proto__:null,builder:Na,command:ka,desc:Aa,handler:Ia},Ea=["metadata <id>","meta"],qa="Get/set resource metadata",Ta=e=>e.positional("id",{describe:"Dataset 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 Ma({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.datasets);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.datasets.deleteMetadata(t.id,o):await e.datasets.setMetadata(t.id,o,s)}t=await p(i,e.datasets),r(t.metadata,"json")}else{const t=await p(i,e.datasets);r(t.metadata,"json")}}catch(t){c(t)}}const La={__proto__:null,builder:Ta,command:Ea,desc:qa,handler:Ma},Ca=["query <id>"],Ua="Query a dataset",Ja=e=>e.option("limit",{alias:"n",type:"number",default:100,describe:"Limit to n results"}).option("offset",{alias:["s","skip"],type:"number",default:0,describe:"Start list at offset n"}).option("query",{alias:["q"],type:"string",default:"{}",describe:"JSON-formatted query"}).option("explain",{alias:["x"],type:"boolean",default:!1,describe:"Explain the generated query"}).option("explain-analyze",{alias:["xx","explain-analyse"],type:"boolean",default:!1,describe:"Explain and analyze the generated query"}).option("params",{alias:["p"],type:"array",describe:"One or more query parameters in form <name>=<value>"}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Ra({client:e,id:i,format:a,offset:t,limit:n,query:o,explain:s,explainAnalyze:d,params:l}){try{const m=await p(i,e.datasets),u=JSON.parse(o);u.limit=n,u.offset=t;let y={};try{l&&(y=l.reduce((_,L)=>{const[K,z]=L.split("=");return _[K]=z,_},{}))}catch(_){throw new Error("Bad format for params: specify key/value pairs as <key>=<value>",{cause:_})}const x={datasetId:m.id,...u,format:a==="table"?"json":a??"json",params:{...y,explain:d?"analyze":!!s}},A=await e.datasets.query(x);a==="csv"?j(A):r(A,a)}catch(m){c(m)}}const Fa={__proto__:null,builder:Ja,command:Ca,desc:Ua,handler:Ra},Ka=["tags <id>"],za="Add or remove tags on a dataset.",Za=e=>e.positional("id",{describe:"Dataset id (or unique prefix)",type:"string"}).option("add",{alias:"a",type:"array",describe:"One or more tags to add."}).option("remove",{alias:"r",type:"array",describe:"One or more tags to remove."});async function Ga({client:e,id:i,add:a,remove:t,format:n}){try{const o=await p(i,e.datasets),s=o.metadata.tags,d=new Set(s);a&&a.forEach(u=>d.add(u)),t&&t.forEach(u=>d.delete(u));const l=Array.from(d),m=await e.datasets.setMetadata(o.id,"tags",l);r(n==="csv"?m.tags.map(u=>({tag:u})):m.tags,n||"table")}catch(o){c(o)}}const Va={__proto__:null,builder:Za,command:Ka,desc:za,handler:Ga},Ha=["update <id>"],Ba="Update a dataset",Wa=e=>e.option("type",{alias:"t",description:"Dataset type",type:"string",choices:["table","sql"]}).option("source",{alias:"s",description:"Dataset source",type:"string"}).option("name",{alias:"n",description:"Dataset name",type:"string"}).option("description",{alias:"d",description:"Dataset description",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("autodetect",{describe:"Autodetect geometry",type:"boolean"}).option("geometryType",{describe:"Geometry type (ignored when autodetect=true)",type:"string"}).option("srid",{describe:"Geometry SRID (ignored when autodetect=true)",type:"number"}).option("extent",{description:"Geometry extent x1,y1,x2,y2 (lng/lat) (ignored when autodetect=true)",type:"array"}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Qa(e){const i=e.client,a=e.id;try{const t=await p(a,i.datasets),n=["Point","LineString","Polygon","MultiPoint","MultiLineString","MultiPolygon","Geometry"];if(e.interactive){const d=await f.prompt([{type:"input",name:"name",message:"Name",default:e.name||t.name},{type:"input",name:"description",message:"Description",default:e.description||t.description},{type:"confirm",name:"autodetect",message:"Autodetect geometry?",default:Object.prototype.hasOwnProperty.call(e,"autodetect")?e.autodetect:t.options&&Object.prototype.hasOwnProperty.call(t.options,"autodetect")?t.options.autodetect:!0},{type:"select",name:"geometryType",message:"Geometry Type",choices:n,default:e.geometryType||t.options?.geometryType||"Geometry",when:({autodetect:l})=>l===!1},{type:"input",name:"srid",message:"Geometry SRID",default:e.srid??t.options?.srid??4326,validate:l=>E(l)||"Enter a number",when:({autodetect:l})=>l===!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (in SRID units)",default:R(e.extent??t.options?.extent??[-180,-90,180,90]),validate:l=>J(l)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({autodetect:l})=>l===!1},{type:"select",name:"type",message:"Source type",choices:["table","sql"],default:e.type||t.type},{type:"input",name:"source",message:"Table name",default:e.source||t.source,when:({type:l})=>l==="table"},{type:"editor",name:"source",message:"SQL query",default:e.source||t.source,when:({type:l})=>l==="sql"}]);e.name=d.name,e.description=d.description,e.autodetect=d.autodetect,e.geometryType=d.geometryType,e.srid=d.autodetect?void 0:d.srid,e.extent=d.autodetect?void 0:H(d.extent),e.type=d.type,e.source=d.source.trim()}const o=await i.datasets.update(t.id,{id:e.id,name:e.name,description:e.description,type:e.type,source:e.source,options:{autodetect:e.autodetect,geometryType:e.geometryType,srid:e.srid,extent:e.extent}}),s=o.attributes.find(d=>d.isGeometry);e.format==="table"?r(o,"table"):r(o,"json"),s&&s.type==="geometry"&&o.options.autodetect&&(c(`Warning: could not auto-detect SRID or extent of field '${s.attribute}' (query produced no features)`),v(`Using the following default values: SRID(${s.srid}), extent: ${R(s.extent)}`),v("Re-run with --auto-detection=false to manually specify values if this is not correct."))}catch(t){c(t)}}const Xa={__proto__:null,builder:Wa,command:Ha,desc:Ba,handler:Qa},Ya=["datasets <command>","ds"],en="Dataset commands",tn=e=>e.command(Oa).command(ba).command(_a).command(Da).command(Fa).command(Xa).command(la).command(La).command(Va).demandCommand(1),an={__proto__:null,builder:tn,command:Ya,desc:en},nn=["delete <id>","rm","del"],on="Delete a job",sn=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Job id (or unique prefix)",type:"string"});async function rn({client:e,id:i,yes:a}){try{const t=await p(i,e.jobs);let n=a;n||(n=(await f.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete job '${t.name}' (${t.id})`,default:!1})).confirmed),n&&(await e.jobs.delete(t.id),w("Deleted"))}catch(t){c(t)}}const dn={__proto__:null,builder:sn,command:nn,desc:on,handler:rn},cn=["list [id]","ls","$0"],ln="List jobs",pn=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:"Job id (or unique prefix)",type:"string"});async function mn({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.jobs);r(s,a)}else o=await e.jobs.list({limit:t,sort:n}),a==="table"?r(o,"table",["id","name","description","type","status"]):r(o,a??"json")}catch(o){c(o)}}const un={__proto__:null,builder:pn,command:cn,desc:ln,handler:mn},yn=["metadata <id>","meta"],fn="Get job metadata",bn=e=>e.positional("id",{describe:"Job id (or unique prefix)",type:"string"});async function gn({client:e,id:i}){try{const a=await p(i,e.jobs),t=await e.jobs.getMetadata(a.id);r(t,"json")}catch(a){c(a)}}const hn={__proto__:null,builder:bn,command:yn,desc:fn,handler:gn},wn=["jobs <command>"],$n="Job commands",_n=e=>e.command(un).command(dn).command(hn).demandCommand(1),xn={__proto__:null,builder:_n,command:wn,desc:$n},vn="create <id>",Sn="Create a new map layer",jn=e=>e.positional("id",{describe:"Map layer id",type:"string"}).option("name",{alias:"n",describe:"Name for new map layer",type:"string"}).option("description",{alias:"d",describe:"Description for new map layer",type:"string"}).option("layerspec",{type:"string",default:"{}",describe:"JSON-formatted layer specification"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),Dn=async e=>{try{if(e.interactive){const a=await f.prompt([{type:"input",name:"name",message:"Name for new map layer",default:e.name},{type:"input",name:"description",message:"Description for new map layer",default:e.description},{type:"editor",name:"layerspec",message:"Layer specification",default:e.layerspec}]);e.name=a.name,e.description=a.description,e.layerspec=a.layerspec}let i={id:e.id,name:e.name,description:e.description,layerSpec:JSON.parse(e.layerspec)};i=await e.client.maplayers.create(i),e.format==="table"?r(i,"table",["id","name","description"]):r(i,e.format??"json")}catch(i){c(i)}},kn={__proto__:null,builder:jn,command:vn,desc:Sn,handler:Dn},An=["delete <id>","rm","del"],Nn="Delete a map layer",In=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Map layer id (or unique prefix)",type:"string"});async function Pn({client:e,id:i,yes:a}){try{const t=await p(i,e.maplayers);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.maplayers.delete(t.id),w("Deleted"))}catch(t){c(t)}}const On={__proto__:null,builder:In,command:An,desc:Nn,handler:Pn},En=["list [id]","ls","$0"],qn="List map layers",Tn=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 Mn({client:e,id:i,format:a,limit:t,sort:n}){try{if(i){const o=await p(i,e.maplayers);r(o,a)}else{const o=await e.maplayers.list({limit:t,sort:n});a==="table"?r(o,"table",["id","name","description"]):r(o,a??"json")}}catch(o){c(o)}}const Ln={__proto__:null,builder:Tn,command:En,desc:qn,handler:Mn},Cn=["metadata <id>","meta"],Un="Get/set resource metadata",Jn=e=>e.positional("id",{describe:"Map layer 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 Rn({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.maplayers);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.maplayers.deleteMetadata(t.id,o):await e.maplayers.setMetadata(t.id,o,s)}t=await p(i,e.maplayers),r(t.metadata,"json")}else{const t=await p(i,e.maplayers);r(t.metadata,"json")}}catch(t){c(t)}}const Fn={__proto__:null,builder:Jn,command:Cn,desc:Un,handler:Rn},Kn=["update <id>"],zn="Update a map layer",Zn=e=>e.option("type",{alias:"t",description:"Map layer type",type:"string",choices:["table","sql"]}).option("name",{alias:"n",description:"Map layer name",type:"string"}).option("description",{alias:"d",description:"Map layer description",type:"string"}).option("layerspec",{type:"string",describe:"JSON-formatted layer specification"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Map layer id (or unique prefix)",type:"string"});async function Gn(e){const i=e.client,a=e.id;try{const t=await p(a,i.maplayers);if(e.interactive){const o=await f.prompt([{type:"input",name:"name",message:"Name",default:e.name||t.name},{type:"input",name:"description",message:"Description",default:e.description||t.description},{type:"editor",name:"layerspec",message:"Layer style specification",default:e.layerspec||JSON.stringify(t.layerSpec)}]);e.name=o.name,e.description=o.description,e.layerspec=o.layerspec}const n=await i.maplayers.update(t.id,{id:e.id,name:e.name,description:e.description,layerSpec:JSON.parse(e.layerspec)});e.format==="table"?r(n,"table"):r(n,"json")}catch(t){c(t)}}const Vn={__proto__:null,builder:Zn,command:Kn,desc:zn,handler:Gn},Hn=["maplayers <command>","layers"],Bn="Map layers commands",Wn=e=>e.command(Ln).command(kn).command(On).command(Fn).command(Vn).demandCommand(1),Qn={__proto__:null,builder:Wn,command:Hn,desc:Bn},Xn=["delete <id>","rm","del"],Yn="Delete media",eo=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Media id (or unique prefix)",type:"string"});async function to({client:e,id:i,yes:a}){try{const t=await p(i,e.media);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.media.delete(t.id),w("Deleted"))}catch(t){c(t)}}const io={__proto__:null,builder:eo,command:Xn,desc:Yn,handler:to},ao=["download <id>","get"],no="Download media",oo=e=>e.positional("id",{describe:"Media id (or unique prefix)",type:"string"}).option("out",{describe:"File path to save the content. Output to stdout if not specified",type:"string"});async function so({client:e,id:i,out:a}){try{const t=await p(i,e.media);let n;a&&(n=N("Downloading").start());const o=await e.media.content(t.id);o.on("end",()=>n?.succeed(`Downloaded media to ${a}`)),o.pipe(a?g.createWriteStream(a):process.stdout)}catch(t){c(t)}}const ro={__proto__:null,builder:oo,command:ao,desc:no,handler:so},co=["list [id]","ls","$0"],lo="List media",po=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:"Media id (or unique prefix)",type:"string"});async function mo({client:e,id:i,format:a,limit:t,sort:n}){try{let o;if(i){const s=await p(i,e.media);r(s,a)}else o=await e.media.list({limit:t,sort:n}),a==="table"?r(o,"table",["id","type","mimeType","fileName"]):r(o,a??"json")}catch(o){c(o)}}const uo={__proto__:null,builder:po,command:co,desc:lo,handler:mo},yo=["metadata <id>","meta"],fo="Get/set resource metadata",bo=e=>e.positional("id",{describe:"Resource 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 go({client:e,id:i,set:a}){try{if(a){let t=await p(i,e.media);const n=k(a);for(const o of Object.keys(n)){const s=n[o];s===""?await e.media.deleteMetadata(t.id,o):await e.media.setMetadata(t.id,o,s)}t=await p(i,e.media),r(t.metadata,"json")}else{const t=await p(i,e.media);r(t.metadata,"json")}}catch(t){c(t)}}const ho={__proto__:null,builder:bo,command:yo,desc:fo,handler:go},wo=["upload <id> <filePath>"],$o="Upload media from a file",_o=e=>e.positional("id",{describe:"Media id",type:"string"}).positional("filePath",{describe:"Path to media file to upload",type:"string"}).option("type",{describe:"Media type - application-specific",type:"string"}).option("mimeType",{describe:"Media MIME type. Auto-detected if not provided.",type:"string"}),xo=async({client:e,format:i,id:a,type:t,mimeType:n,filePath:o})=>{const s=D.basename(o);v(`Uploading file ${s} ...`);const d=N("Uploading").start();try{const l=g.createReadStream(o),m=await e.media.upload(a,{data:l,fileName:s,type:t,mimeType:n});d.succeed(),r(m,i??"json")}catch(l){d.stop(),c(l)}},vo={__proto__:null,builder:_o,command:wo,desc:$o,handler:xo},So=["media <command>"],jo="Media commands",Do=e=>e.command(uo).command(vo).command(ro).command(io).command(ho).demandCommand(1),ko={__proto__:null,builder:Do,command:So,desc:jo},re="https://s3.eu-west-2.amazonaws.com/emu-flo.w/starters";function Ao(e){return de(`${re}/${e.repo}`)}function de(e){const i=Oe.parse(e);return new Promise((a,t)=>{Pe(i,n=>{if(n.statusCode===302)de(n.headers.location).then(a,t);else{const o=[];n.on("data",s=>o.push(s)),n.on("end",()=>{a(Buffer.concat(o))}),n.on("error",t)}})})}function No(e,i){return new Promise((a,t)=>{qe(e,{lazyEntries:!0},Io(i,a,t))})}function Io(e,i,a){return(t,n)=>{if(t)throw t;n.readEntry(),n.on("entry",o=>{const s=o.fileName.split("/");s[0]=e;const d=s.join(D.sep);d[d.length-1]===D.sep?n.readEntry():ce(D.dirname(d),()=>{n.openReadStream(o,(l,m)=>{if(l)throw l;m.on("end",()=>{n.readEntry()}),m.pipe(g.createWriteStream(d))})})}),n.once("error",a),n.once("end",()=>{i()})}}function ce(e,i){if(e===".")return i();g.stat(e,a=>{if(a==null)return i();const t=D.dirname(e);ce(t,()=>{g.mkdir(e,i)})})}const Po=[];let q=null;function le(e){q=e,e&&(Q(e),process.once("uncaughtException",()=>F(!0)),process.once("exit",()=>F()),process.once("SIGINT",()=>F()),process.once("SIGTERM",()=>F()))}function F(e=!1){q&&Oo(),setTimeout(()=>{q&&(Q(q),q=null),process.exit(e?1:0)},200)}function Oo(){Po.forEach(e=>e.kill("SIGINT"))}async function Eo(e,i,a="ignore"){await Me("npm",[e],{cwd:i,stdio:a})}function Q(e){g.existsSync(e)&&(g.readdirSync(e).forEach(i=>{const a=I(e,i);g.lstatSync(a).isDirectory()?Q(a):g.unlinkSync(a)}),g.rmdirSync(e))}function qo(e){return pe()?e:""}function To(e){return e>1e3?`in ${(e/1e3).toFixed(2)} s`:`in ${parseFloat(e.toFixed(3))} ms`}function pe(){return process.platform==="win32"}function T(){return pe()?">":"$"}const Mo=Te(g.rename),me=new Map;async function Lo(e,i,a){if(g.existsSync(i))throw new Error(`Folder "./${i}" already exists, please choose a different project name.`);if(i=i.toLowerCase().trim(),!Ro(i))throw new Error(`Project name "${i}" is not valid. It must be a kebab-case name without spaces.`);const t=new Ie(b.bold("Preparing starter"));t.setSpinnerString(18),t.start();const n=Date.now();try{const s=await Uo(e);if(!s)throw new Error("starter install failed");await s(i)}finally{t.stop(!0)}const o=To(Date.now()-n);j(`${b.green("\u2714")} ${b.bold("All setup")} ${qo("\u{1F389}")} ${b.dim(o)}
|
|
2
|
+
import{Client as K}from"@emuanalytics/flow-engine-client";import{findUpSync as $e}from"find-up";import*as $ from"fs";import _e from"yargs";import{hideBin as xe}from"yargs/helpers";import A from"chalk";import ve from"csv-stringify/lib/sync.js";import Se from"loose-json";import{emojify as C}from"node-emoji";import{table as Ae}from"table";import De from"string-width";import b from"inquirer";import T from"ora";import*as j from"path";import{join as P}from"path";import Ne from"JSONStream";import{Writable as je,pipeline as ke}from"stream";import{McpServer as Ie}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as u}from"zod";import{Spinner as Te}from"cli-spinner";import*as w from"colorette";import{get as Pe}from"https";import*as Me from"url";import{fileURLToPath as Ee}from"url";import{fromBuffer as qe}from"yauzl";import{promisify as Le}from"util";import Ce from"execa";import Ue from"replace-in-file";import*as Je from"http";import z from"express";import Re from"debounce-promise";import V from"open";import Ge from"humanize-plus";import{v4 as te}from"uuid";const D=console.log;function l(e){typeof e=="string"?D(C(A.bold.red(e))):(D(C(A.bold.red(e.message))),e.error?.message&&_(e.error.message))}function v(e){D(C(A.bold(e)))}function _(e){D(C(A.dim(e)))}function d(e,t="table",a=null,i={}){if(!e)return l("Null object");if(t==="table"){let n;if(Array.isArray(e)){if(e.length===0)return D("No Items");if(typeof e[0]=="object"){const o=a||Object.keys(e[0]);n=[["",...o.map(r=>A.bold.green(r))]],e.forEach((r,c)=>{const m=[c.toString(),...o.map(y=>U(r[y]))];n.push(m)})}else n=[["",A.bold.green("Value")]],e.forEach((o,r)=>{const c=[r.toString(),U(o)];n.push(c)})}else n=[[A.bold.green("Property"),A.bold.green("Value")],...Object.keys(e).map(o=>[o,U(e[o])])];const s=Ze(n).map(o=>({width:i.noWrap?o:Math.min(o,36),truncate:200}));console.log(Ae(n,{columns:s}))}else t==="csv"?(Array.isArray(e)||(e=[e]),D(ve(e,{header:!0}))):D(JSON.stringify(e,null,2))}function Ze(e){if(!e[0])throw new Error("Dataset must have at least one row.");const t=new Array(e[0].length).fill(0);return e.forEach(a=>{Fe(a).forEach((i,n)=>{t[n]<i&&(t[n]=i)})}),t}function Fe(e){return e.map(t=>De(t))}function U(e){return Array.isArray(e)?"<array>":e===null?A.grey("null"):typeof e>"u"?A.grey("undefined"):typeof e=="object"?"color"in e&&"value"in e?A.hex(e.color)(U(e.value)):"<object>":e.toString().replace(/[^ -~]/g," ")}async function p(e,t,a="id",i,n){const s=await t.list({[`filter[${a}]`]:`~${e}`,sort:n||a,join:i});if(s.length===0)throw new Error("Not found");if(s.length>1&&s[0].id!==e)throw new Error(`Multiple matches for '${e}': ${s.map(o=>o[a]).join(", ")}`);return s[0]}function M(e){return!isNaN(parseFloat(e))&&isFinite(e)}function J(e){return!e||e.length===0||(typeof e=="string"&&(e=e.split(" ")),e.length!==4)?!1:!e.find(t=>!M(t))}function R(e){return!e||e.length===0?"":e.map(t=>t.toString()).join(" ")}function W(e){return e==null?null:(typeof e=="string"&&(e=e.split(" ")),e.map(t=>parseFloat(t)))}function B(e){return!Array.isArray(e)||e.length===0?"":e.join(", ")}function Ke(e){return e&&(e=e.trim()),e?e.split(",").map(t=>t.trim()):[]}function k(e){try{return e.reduce((t,a)=>{const[i,n]=a.split("=");try{t[i||null]=Se(n)}catch{t[i]=n}return t},{})}catch(t){throw new Error("Bad format for key/value: specify key/value pairs as <key>=<value>",{cause:t})}}function H(e,t){const a={...e};for(const i of t)Array.isArray(a[i])&&(a[i]=a[i].join(", "));return a}function ie(...e){return e.length===0?[]:e.length===1?e[0]===void 0||e[0]===null?[]:Array.isArray(e[0])?e[0]:[e[0]]:e}const ze="create",Ve="Create a new API key",We=e=>e.option("id",{describe:"ID for new API key",type:"string"}).option("applicationId",{describe:"Application ID for new API key",type:"string"}).option("description",{describe:"Description for new API key",type:"string"}).option("type",{alias:"t",describe:"Type of key",type:"string",choices:["app","master"]}).option("permissions",{describe:"Permissions for new API key",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),Be=async e=>{try{if(e.interactive){const a=await b.prompt([{type:"input",name:"id",message:"ID of new key (leave blank to auto-generate)",default:e.id,validate:ae},{type:"input",name:"description",message:"Name for new API key",default:e.description},{type:"select",name:"type",message:"Type of API key to create",choices:["app","master"],default:e.type},{type:"input",name:"permissions",message:"Permissions for new API key (JSON)",default:e.permissions,validate:i=>{try{return JSON.parse(i),!0}catch{return!1}}},{type:"confirm",name:"overrideApplicationId",message:"Override Application ID",default:!1,when:({type:i})=>i==="app"},{type:"input",name:"applicationId",message:"Application ID of new key",default:e.applicationId,validate:ae,when:({overrideApplicationId:i})=>i}]);e.id=a.id,e.description=a.description,e.type=a.type,e.permissions=a.permissions,e.applicationId=a.applicationId}let t={description:e.description,type:e.type,permissions:JSON.parse(e.permissions)};e.id&&(t.id=e.id),e.applicationId&&(t.applicationId=e.applicationId),t=await e.client.apiKeys.create(t),d(t,"table")}catch(t){l(t)}};function ae(e){return e?!!e.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i):!0}const He={__proto__:null,builder:We,command:ze,desc:Ve,handler:Be},Qe=["delete <id>","rm","del"],Xe="Delete an API key",Ye=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"API key id (or unique prefix)",type:"string"});async function et({client:e,id:t,yes:a}){try{const i=await p(t,e.apiKeys);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.apiKeys.delete(i.id),_("Deleted"))}catch(i){l(i)}}const tt={__proto__:null,builder:Ye,command:Qe,desc:Xe,handler:et},it=["list [id]","ls","$0"],at="List API keys",nt=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:"API key id (or unique prefix",type:"string"}).conflicts("id",["limit","sort"]);async function st({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.apiKeys);d(o,a)}else s=await e.apiKeys.list({limit:i,sort:n}),a==="table"?(s=s.map(o=>({...o,permissions:JSON.stringify(o.permissions)})),d(s,"table",["id","description","type","permissions"])):d(s,a??"json")}catch(s){l(s)}}const ot={__proto__:null,builder:nt,command:it,desc:at,handler:st},rt=["metadata <id>","meta"],dt="Get/set resource metadata",ct=e=>e.positional("id",{describe:"API key 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 lt({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.apiKeys);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.apiKeys.deleteMetadata(i.id,s):await e.apiKeys.setMetadata(i.id,s,o)}i=await p(t,e.apiKeys),d(i.metadata,"json")}else{const i=await p(t,e.apiKeys);d(i.metadata,"json")}}catch(i){l(i)}}const pt={__proto__:null,builder:ct,command:rt,desc:dt,handler:lt},mt="apikeys <command>",ut="API key commands",yt=e=>e.command(ot).command(He).command(tt).command(pt).demandCommand(1),ft={__proto__:null,builder:yt,command:mt,desc:ut},bt=["appconfig"],gt="Get/set app config",ht=e=>e.option("get",{type:"string",describe:"One or more key/values to set in form <key>=<value>. Supports dotted notation for keys"}).option("set",{type:"array",describe:"Set one or more key/values (in form <key>=<value>.) Use <key>= to delete a key. Supports dotted notation for keys"});async function wt({client:e,get:t,set:a}){try{if(a){const i=k(a);for(const n of Object.keys(i)){const s=i[n];s===""?await e.appconfig.delete(n):await e.appconfig.set(n,s)}_("Done")}else{const i=await e.appconfig.get(t);d(i,"json")}}catch(i){l(i)}}const $t={__proto__:null,builder:ht,command:bt,desc:gt,handler:wt},_t="create <name>",xt="Create a new Flo.w application",vt=e=>e.positional("name",{describe:"Name for new application",type:"string"}).option("description",{alias:"d",describe:"Description for new application",type:"string"}).option("contact",{alias:"c",describe:"Contact email for new application",type:"string"}).option("developer-key",{describe:"Create default developer key",type:"boolean",default:"true"}).option("public-key",{describe:"Create default public key",type:"boolean",default:"false"}).option("authorized-domains",{describe:"Authorized email domains",type:"array",default:["emu-analytics.com"]}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),St=async e=>{try{if(e.interactive){const s=await b.prompt([{type:"input",name:"name",message:"Name for new application",default:e.name},{type:"input",name:"description",message:"Description for new application",default:e.description},{type:"input",name:"contact",message:"Contact email for new application",default:e.contact},{type:"confirm",name:"developerKey",message:"Create default developer key (all permissions)?",default:e.developerKey},{type:"confirm",name:"publicKey",message:"Create default public key (minimal permissions)?",default:e.publicKey},{type:"input",name:"authorizedDomains",message:"Authorized email domains (comma-separated)",default:e.authorizedDomains.join(", ")}]);e.name=s.name,e.description=s.description,e.contact=s.contact,e.developerKey=s.developerKey,e.publicKey=s.publicKey,e.authorizedDomains=s.authorizedDomains.split(",").map(o=>o.trim())}let t={name:e.name,description:e.description,contact:e.contact,metadata:{auth:{authorisedEmailDomains:e.authorizedDomains},datasetAccessOptions:{allowFn:!0,maxQueryLimit:1e4,defaultQueryLimit:1e3,statementTimeout:1e4}}};t=await e.client.applications.create(t);let a,i;e.developerKey&&(a={description:`${e.name} developer key`,applicationId:t.id,type:"app",permissions:{"*":{"*":"*"}}},a=await e.client.apiKeys.create(a),await e.client.appconfig.set("dummy","dummy",{apiKey:a.id}),await e.client.appconfig.delete("dummy",{apiKey:a.id})),e.publicKey&&(i={description:`${e.name} public key`,applicationId:t.id,type:"app",permissions:{"*":{execute:"*"},datasets:{read:"*"},tiles:{read:"*"},pubsub:{read:"*"},appconfig:{read:"*"}}},i=await e.client.apiKeys.create(i),t.metadata.auth.defaultApiKey=i.id,await e.client.applications.update(t.id,t));const n={applicationId:t.id,developerApiKey:a?a.id:"none",publicApiKey:i?i.id:"none"};D(`Application ${t.name} created`),d(n,"table")}catch(t){l(t)}},At={__proto__:null,builder:vt,command:_t,desc:xt,handler:St},Dt=["delete <id>","rm","del"],Nt="Delete an application",jt=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function kt({client:e,id:t,yes:a}){try{const i=await p(t,e.applications);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.applications.delete(i.id),_("Deleted"))}catch(i){l(i)}}const It={__proto__:null,builder:jt,command:Dt,desc:Nt,handler:kt},Ot=["export <file> <id>"],Tt="Export an application to JSON",Pt=e=>e.positional("file",{describe:"Path to output JSON file",type:"string"}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function Mt({client:e,id:t,file:a}){try{const i=await p(t,e.applications);a=j.resolve(a);const n=T(`Exporting application: ${i.name}`).start(),s=$.createWriteStream(a,{encoding:"utf8"});s.on("error",r=>{n.fail(),l(r),process.exit(1)}),s.write(`{
|
|
3
|
+
`);const o=["applications","apiKeys","databases","datasets","styles","tileSources","auth.users"];for(const r of o){const c=r.split(".").reduce((f,g)=>f?.[g],e),m=r==="applications"?"id":"applicationId",y=await c.list({filter:{[m]:i.id}});s.write(`"${r}": ${JSON.stringify(y,null,2)},
|
|
4
|
+
`)}s.write(`}
|
|
5
|
+
`),s.end(),n.succeed()}catch(i){l(i)}}const Et={__proto__:null,builder:Pt,command:Ot,desc:Tt,handler:Mt},qt=["import <file> [<id>]"],Lt="Import an application from JSON",Ct=e=>e.positional("file",{describe:"Path to input JSON file",type:"string"}).positional("id",{describe:"Application id (omit to use id from file)",type:"string"});async function Ut({client:e,id:t,file:a}){try{a=j.resolve(a);const i=new Date().toISOString(),n=T(`Importing from: ${a}`).start(),s=$.createReadStream(a,{encoding:"utf8"}),o=Ne.parse([!0,{emitPath:!0}]);let r=0;const c=new je({objectMode:!0,write(m,y,f){const g=m.path[0],S=m.path[1],h=m.value,N=g.split(".").reduce((Z,F)=>Z?.[F],e);(typeof h!="object"||!N)&&f(new Error(`Bad file format: ${g}[${S}]`)),h.createdAt&&(h.createdAt=i),h.updatedAt&&(h.updatedAt=i),t&&(g==="applications"?h.id=t:h.applicationId=t),N.create(h,{applicationId:h.applicationId}).then(()=>{n.text=`Imported ${g}[${S}]`,r++,f()}).catch(f)}});ke(s,o,c,m=>{m&&(n.fail(),l(m),process.exit(1)),n.succeed(`Imported ${r} resource${r>1?"s":""}`)})}catch(i){l(i)}}const Jt={__proto__:null,builder:Ct,command:qt,desc:Lt,handler:Ut},Rt="auth.authorisedEmailDomains",Gt=["list [id]","ls","$0"],Zt="List applications",Ft=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:"Application id (or unique prefix)",type:"string"}).conflicts("id",["limit","sort"]);async function Kt({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.applications),r=await e.applications.getMetadata(o.id,Rt);d({...o,authorisedEmailDomains:B(r)},a)}else s=await e.applications.list({limit:i,sort:n}),a==="table"?d(s,"table",["id","name","description","contact"]):d(s,a??"json")}catch(s){l(s)}}const zt={__proto__:null,builder:Ft,command:Gt,desc:Zt,handler:Kt},Vt=["metadata <id>","meta"],Wt="Get/set resource metadata",Bt=e=>e.positional("id",{describe:"Application 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 Ht({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.applications);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.applications.deleteMetadata(i.id,s):await e.applications.setMetadata(i.id,s,o)}i=await p(t,e.applications),d(i.metadata,"json")}else{const i=await p(t,e.applications);d(i.metadata,"json")}}catch(i){l(i)}}const Qt={__proto__:null,builder:Bt,command:Vt,desc:Wt,handler:Ht},ne="auth.authorisedEmailDomains",Xt=["update <id>"],Yt="Update an application",ei=e=>e.option("name",{alias:"n",description:"Dataset name",type:"string"}).option("description",{alias:"d",description:"Dataset description",type:"string"}).option("domains",{description:"Authorized self-signup email domains",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Application id (or unique prefix)",type:"string"});async function ti(e){const t=e.client,a=e.id;try{const i=await p(a,t.applications),n=await t.applications.getMetadata(i.id,ne);if(e.interactive){const o=await b.prompt([{type:"input",name:"name",message:"Name",default:e.name||i.name},{type:"input",name:"description",message:"Description",default:e.description||i.description},{type:"input",name:"contact",message:"Contact",default:e.contact||i.contact},{type:"input",name:"domains",message:"Authorized self-signup email domains (comma-separated)",default:B(e.domains||n)}]);e.name=o.name,e.description=o.description,e.contact=o.contact,e.domains=Ke(o.domains)}await t.applications.setMetadata(i.id,ne,e.domains);const s=await t.applications.update(i.id,{id:e.id,name:e.name,description:e.description,contact:e.contact});e.format==="table"?d({...s,authorisedEmailDomains:B(e.domains)},"table"):d(s,"json")}catch(i){l(i)}}const ii={__proto__:null,builder:ei,command:Xt,desc:Yt,handler:ti},ai=["applications <command>","apps"],ni="Application commands",si=e=>e.command(zt).command(At).command(It).command(Et).command(Jt).command(Qt).command(ii).demandCommand(1),oi={__proto__:null,builder:si,command:ai,desc:ni},ri="create <id>",di="Create a new Contact Point",ci=e=>e.positional("id",{describe:"Contact point id",type:"string"}).option("type",{describe:"Type of new contact point",type:"string"}).option("description",{describe:"Description for new contact point",type:"string"}).option("templates",{description:"Contact point templates (comma-separated)",type:"array"}).option("recipients",{description:"Recipients (comma-separated)",type:"array"}).option("cp-host",{describe:"Contact point host (where applicable)",type:"string"}).option("cp-port",{describe:"Contact point port (where applicable)",type:"number"}).option("cp-user",{describe:"Contact point user (where applicable)",type:"string"}).option("password",{describe:"Contact point password (where applicable)",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),li=async e=>{try{if(e.interactive){const a=await e.client.contactpoints.drivers(),i=await b.prompt([{type:"select",name:"type",message:"Contact point type",choices:a,default:e.type??"email"},{type:"input",name:"description",message:"Description for new contact point",default:e.description},{type:"input",name:"templates",message:"Templates (comma separated)",default:(e.templates??[]).join(", ")},{type:"input",name:"recipients",message:"Recipients (comma separated)",default:(e.recipients??[]).join(", ")},{type:"input",name:"host",message:"Contact point host (where applicable)",default:e.cpHost},{type:"number",name:"port",message:"Contact point port (where applicable)",default:e.cpPort??587},{type:"input",name:"user",message:"Contact point user (where applicable)",default:e.cpUser},{type:"password",name:"password",message:"Contact point password (where applicable)",default:e.cpPassword}]);e.type=i.type,e.description=i.description?i.description:null,e.cpHost=i.host?i.host:null,e.cpPort=i.port?i.port:null,e.cpUser=i.user?i.user:null,e.cpPassword=i.password?i.password:null,e.templates=i.templates.split(",").map(n=>n.trim()).filter(n=>n.length),e.recipients=i.recipients.split(",").map(n=>n.trim()).filter(n=>n.length)}let t={id:e.id,type:e.type,description:e.description,templates:e.templates,recipients:e.recipients,host:e.cpHost,port:e.cpPort,user:e.cpUser,password:e.cpPassword};t=await e.client.contactpoints.create(t),d(H(t,["templates","recipients"]),"table")}catch(t){l(t)}},pi={__proto__:null,builder:ci,command:ri,desc:di,handler:li},mi=["delete <id>","rm","del"],ui="Delete a contact point",yi=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Contact Point id (or unique prefix)",type:"string"});async function fi({client:e,id:t,yes:a}){try{const i=await p(t,e.contactpoints);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.contactpoints.delete(i.id),_("Deleted"))}catch(i){l(i)}}const bi={__proto__:null,builder:yi,command:mi,desc:ui,handler:fi},gi=["list [id]","ls","$0"],hi="List contact points",wi=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:"Contact point id (or unique prefix)",type:"string"});async function $i({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.contactpoints);d(H(o,["templates","recipients"]),a)}else s=await e.contactpoints.list({limit:i,sort:n}),a==="table"?d(s.map(o=>H(o,["templates","recipients"])),"table",["id","type","description","templates","recipients"]):d(s,a??"json")}catch(s){l(s)}}const _i={__proto__:null,builder:wi,command:gi,desc:hi,handler:$i},xi=["metadata <id>","meta"],vi="Get/set resource metadata",Si=e=>e.positional("id",{describe:"Resource 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 Ai({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.contactpoints);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.contactpoints.deleteMetadata(i.id,s):await e.contactpoints.setMetadata(i.id,s,o)}i=await p(t,e.contactpoints),d(i.metadata,"json")}else{const i=await p(t,e.contactpoints);d(i.metadata,"json")}}catch(i){l(i)}}const Di={__proto__:null,builder:Si,command:xi,desc:vi,handler:Ai},Ni=["send <id>"],ji="Send a notification to a contact point",ki=e=>e.positional("id",{describe:"Contact point id",type:"string"}).option("template",{description:"Notification template",type:"string"}).option("subject",{description:"Notification subject",type:"string"}).option("recipients",{description:"Notification recipients (comma separated)",type:"array"}).option("data",{description:"Notification data (JSON-formatted)",type:"string",default:"{}"}),Ii=async({client:e,id:t,template:a,subject:i,recipients:n,data:s})=>{try{const o=await p(t,e.contactpoints);await e.contactpoints.send(o.id,{subject:i,template:a,recipients:n,data:JSON.parse(s)}),_("Notification sent")}catch(o){l(o)}},Oi={__proto__:null,builder:ki,command:Ni,desc:ji,handler:Ii},Ti=["update <id>"],Pi="Update a contact point",Mi=e=>e.option("description",{alias:"d",description:"Contact point description",type:"string"}).option("templates",{description:"Contact point templates (comma-separated)",type:"array"}).option("recipients",{description:"Recipients (comma-separated)",type:"array"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Contact point id (or unique prefix)",type:"string"});async function Ei(e){const t=e.client,a=e.id;try{const i=await p(a,t.contactpoints);if(e.interactive){const s=await b.prompt([{type:"input",name:"description",message:"Description",default:e.description||i.description},{type:"input",name:"templates",message:"Templates (comma separated)",default:(e.templates||i.templates).join(", ")},{type:"input",name:"recipients",message:"Recipients (comma separated)",default:(e.recipients||i.recipients).join(", ")}]);e.description=s.description,e.templates=s.templates.split(",").map(o=>o.trim()).filter(o=>o.length),e.recipients=s.recipients.split(",").map(o=>o.trim()).filter(o=>o.length)}const n=await t.contactpoints.update(i.id,{id:e.id,description:e.description,templates:e.templates,recipients:e.recipients});e.format==="table"?d(n,"table"):d(n,"json")}catch(i){l(i)}}const qi={__proto__:null,builder:Mi,command:Ti,desc:Pi,handler:Ei},Li=["contactpoints <command>","cps"],Ci="Contact Point commands",Ui=e=>e.command(_i).command(pi).command(bi).command(Di).command(Oi).command(qi).demandCommand(1),Ji={__proto__:null,builder:Ui,command:Li,desc:Ci},Ri="create",Gi="Create a new Database connection",Zi=e=>e.option("id",{describe:"ID for new database",type:"string"}).option("applicationId",{describe:"Application ID for new database",type:"string"}).option("name",{describe:"Name for new database",type:"string"}).option("description",{describe:"Description for new database",type:"string"}).option("driver",{describe:"Database driver",type:"string"}).option("db-host",{describe:"Database host",type:"string"}).option("db-port",{describe:"Database port",type:"number",default:5432}).option("db-database",{describe:"Database name",type:"string"}).option("db-user",{describe:"Database user",type:"string"}).option("db-password",{describe:"Database password",type:"string"}).option("default",{describe:"Default database",type:"boolean",default:!0}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),Fi=async e=>{try{if(e.interactive){const a=await e.client.databases.drivers(),i=await b.prompt([{type:"input",name:"id",message:"ID of new database (leave blank to auto-generate)",default:e.id,validate:se},{type:"input",name:"name",message:"Name for new database",default:e.description},{type:"input",name:"description",message:"Description for new database",default:e.description},{type:"select",name:"driver",message:"Database driver",choices:a,default:e.driver??"postgis"},{type:"input",name:"dbHost",message:"Database host",default:e.dbHost},{type:"number",name:"dbPort",message:"Database port",default:e.dbPort},{type:"input",name:"dbDatabase",message:"Database",default:e.dbDatabase},{type:"input",name:"dbUser",message:"Database user",default:e.dbUser},{type:"password",name:"dbPassword",message:"Database password",default:e.dbPassword},{type:"confirm",name:"default",message:"Is this the default database?",default:e.default},{type:"confirm",name:"overrideApplicationId",message:"Override Application ID",default:!1},{type:"input",name:"applicationId",message:"Application ID of new key",default:e.applicationId,validate:se,when:({overrideApplicationId:n})=>n}]);e.id=i.id,e.name=i.name,e.description=i.description,e.driver=i.driver,e.dbHost=i.dbHost,e.dbPort=i.dbPort,e.dbDatabase=i.dbDatabase,e.dbUser=i.dbUser,e.dbPassword=i.dbPassword,e.default=i.default,e.applicationId=i.applicationId}let t={name:e.name,description:e.description,type:e.driver,host:e.dbHost,port:e.dbPort,db:e.dbDatabase,user:e.dbUser,password:e.dbPassword,default:e.default};e.id&&(t.id=e.id),e.applicationId&&(t.applicationId=e.applicationId),t=await e.client.databases.create(t),t.password="*****",d(t,"table")}catch(t){l(t)}};function se(e){return e?!!e.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i):!0}const Ki={__proto__:null,builder:Zi,command:Ri,desc:Gi,handler:Fi},zi=["delete <id>","rm","del"],Vi="Delete a database",Wi=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Database id (or unique prefix)",type:"string"});async function Bi({client:e,id:t,yes:a}){try{const i=await p(t,e.databases);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.databases.delete(i.id),_("Deleted"))}catch(i){l(i)}}const Hi={__proto__:null,builder:Wi,command:zi,desc:Vi,handler:Bi},Qi=["list [id]","ls","$0"],Xi="List databases",Yi=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:"Datavase id (or unique prefix)",type:"string"});async function ea({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.databases);d(o,a)}else s=await e.databases.list({limit:i,sort:n}),a==="table"?d(s,"table",["id","name","description","type","default"]):d(s,a??"json")}catch(s){l(s)}}const ta={__proto__:null,builder:Yi,command:Qi,desc:Xi,handler:ea},ia=["databases <command>","dbs"],aa="Database commands",na=e=>e.command(ta).command(Ki).command(Hi).demandCommand(1),sa={__proto__:null,builder:na,command:ia,desc:aa},oa=["attributes <id>","attrs"],ra="List dataset attributes",da=e=>e.positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function ca({client:e,id:t,format:a}){try{const i=await p(t,e.datasets);a==="table"?d(i.attributes,"table",["attribute","description","type","dbType","default","nullable","primaryKey"]):d(i.attributes,a??"json")}catch(i){l(i)}}const la={__proto__:null,builder:da,command:oa,desc:ra,handler:ca},pa="create <id>",ma="Create a new dataset",ua=e=>e.positional("id",{describe:"Dataset id",type:"string"}).option("name",{alias:"n",describe:"Name for new dataset",type:"string"}).option("description",{alias:"d",describe:"Description for new dataset",type:"string"}).option("databaseId",{describe:"ID of associated database (for multi-database apps only)",type:"string"}).option("type",{alias:"t",describe:"Type of dataset",type:"string",choices:["table","sql"]}).option("source",{alias:"s",description:"Dataset source",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("autodetect",{describe:"Autodetect geometry",type:"boolean"}).option("geometryType",{describe:"Geometry type (ignored when autodetect=true)",type:"string"}).option("srid",{describe:"Geometry SRID (ignored when autodetect=true)",type:"number"}).option("extent",{description:"Geometry extent x1,y1,x2,y2 (lng/lat) (ignored when autodetect=true)",type:"array"}),ya=async e=>{try{const t=await fa(e.host,e.apiKey),a=t.map(s=>({name:s.name,value:s.id,short:s.name})),i=["Point","LineString","Polygon","MultiPoint","MultiLineString","MultiPolygon","Geometry"];if(e.interactive){const s=await b.prompt([{type:"input",name:"name",message:"Name for new dataset",default:e.name},{type:"input",name:"description",message:"Description for new dataset",default:e.description},{type:"select",name:"databaseId",message:"Select the associated database",choices:a,default:e.databaseId||t.find(o=>o.default).id,when:()=>a.length>1},{type:"confirm",name:"autodetect",message:"Autodetect geometry?",default:e.autodetect},{type:"select",name:"geometryType",message:"Geometry Type",choices:i,default:e.geometryType,when:({autodetect:o})=>o===!1},{type:"input",name:"srid",message:"Geometry SRID",default:e.srid,validate:o=>M(o)||"Enter a number",when:({autodetect:o})=>o===!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (in SRID units)",default:R(e.extent??[-180,-90,180,90]),validate:o=>J(o)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({autodetect:o})=>o===!1},{type:"select",name:"type",message:"Type of dataset to create",choices:["table","sql"],default:e.type},{type:"input",name:"source",message:"Table name",default:e.source,when:({type:o})=>o==="table"},{type:"editor",name:"source",message:"SQL query",default:e.source,when:({type:o})=>o==="sql"}]);e.name=s.name,e.description=s.description,e.databaseId=s.databaseId,e.autodetect=s.autodetect,e.geometryType=s.autodetect?void 0:s.geometryType,e.srid=s.autodetect?void 0:s.srid,e.extent=s.autodetect?void 0:W(s.extent),e.type=s.type,e.source=s.source.trim()}let n={id:e.id,name:e.name,description:e.description,databaseId:e.databaseId,type:e.type,source:e.source,options:{autodetect:e.autodetect,geometryType:e.geometryType,srid:e.srid,extent:e.extent}};n=await e.client.datasets.create(n),e.format==="table"?d(n,"table",["id","name","description","type"]):d(n,e.format??"json")}catch(t){l(t)}};async function fa(e,t){return new K(e,t).databases.list()}const ba={__proto__:null,builder:ua,command:pa,desc:ma,handler:ya},ga=["delete <id>","rm","del"],ha="Delete a dataset",wa=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function $a({client:e,id:t,yes:a}){try{const i=await p(t,e.datasets);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.datasets.delete(i.id),_("Deleted"))}catch(i){l(i)}}const _a={__proto__:null,builder:wa,command:ga,desc:ha,handler:$a},xa=["export <id> [destPath]"],va="Export a dataset",Sa=e=>e.hide("f").option("o",{alias:"output",default:"geojson",describe:"Output format",type:"string",choices:["geojson","gpkg","shapefile"]}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"}).positional("destPath",{describe:"Destination path",type:"string"});async function Aa({client:e,id:t,output:a,destPath:i}){try{const n=await p(t,e.datasets);if(n.type!=="table")throw new Error(`Export of dataset type '${n.type}' not supported`);if(n.type!=="table")throw new Error(`Export of dataset type '${n.type}' not supported`);const s=e.datasets.getExportUrl(n.id,a),o=await e._get(s,{responseType:"stream"});if(!i){let r;switch(a){case"geojson":r="geojson";break;case"gpkg":r="gpkg";break;case"shapefile":r="shp.zip";break;default:throw new Error(`Unsupported output format: ${a}`)}i=`${n.id}.${r}`}o.on("end",()=>D(`Dataset exported to '${i}'`)),o.pipe($.createWriteStream(i))}catch(n){l(n)}}const Da={__proto__:null,builder:Sa,command:xa,desc:va,handler:Aa},Na=["list [id]","ls","$0"],ja="List datasets",ka=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-tag",{alias:"t",type:"array",describe:"Filter datasets using a specific tag"}).conflicts("id",["limit","sort","filter-tag"]).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Ia({client:e,id:t,format:a,limit:i,sort:n,filterTag:s}){try{if(n??="id",t){const c=await p(t,e.datasets);a==="table"?d(oe(c),"table"):d(c,a??"json");return}const o=!!s,r=(await e.datasets.list({sort:n,limit:o?void 0:i})).filter(c=>Oa(c,s)).slice(0,o?i:void 0);a==="table"?d(r.map(oe),"table",["id","name","description","type","tags"]):d(r,a??"json")}catch(o){l(o)}}function oe(e){return{...e,tags:e.metadata.tags?e.metadata.tags.join(","):""}}function Oa(e,t){return!t||e.metadata.tags&&t.every(a=>e.metadata.tags.includes(a))}const Ta={__proto__:null,builder:ka,command:Na,desc:ja,handler:Ia},Pa=["metadata <id>","meta"],Ma="Get/set resource metadata",Ea=e=>e.positional("id",{describe:"Dataset 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 qa({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.datasets);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.datasets.deleteMetadata(i.id,s):await e.datasets.setMetadata(i.id,s,o)}i=await p(t,e.datasets),d(i.metadata,"json")}else{const i=await p(t,e.datasets);d(i.metadata,"json")}}catch(i){l(i)}}const La={__proto__:null,builder:Ea,command:Pa,desc:Ma,handler:qa},Ca=["query <id>"],Ua="Query a dataset",Ja=e=>e.option("limit",{alias:"n",type:"number",default:100,describe:"Limit to n results"}).option("offset",{alias:["s","skip"],type:"number",default:0,describe:"Start list at offset n"}).option("query",{alias:["q"],type:"string",default:"{}",describe:"JSON-formatted query"}).option("explain",{alias:["x"],type:"boolean",default:!1,describe:"Explain the generated query"}).option("explain-analyze",{alias:["xx","explain-analyse"],type:"boolean",default:!1,describe:"Explain and analyze the generated query"}).option("params",{alias:["p"],type:"array",describe:"One or more query parameters in form <name>=<value>"}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Ra({client:e,id:t,format:a,offset:i,limit:n,query:s,explain:o,explainAnalyze:r,params:c}){try{const m=await p(t,e.datasets),y=JSON.parse(s);y.limit=n,y.offset=i;let f={};try{c&&(f=c.reduce((h,N)=>{const[Z,F]=N.split("=");return h[Z]=F,h},{}))}catch(h){throw new Error("Bad format for params: specify key/value pairs as <key>=<value>",{cause:h})}const g={datasetId:m.id,...y,format:a==="table"?"json":a??"json",params:{...f,explain:r?"analyze":!!o}},S=await e.datasets.query(g);a==="csv"?D(S):d(S,a)}catch(m){l(m)}}const Ga={__proto__:null,builder:Ja,command:Ca,desc:Ua,handler:Ra},Za=["tags <id>"],Fa="Add or remove tags on a dataset.",Ka=e=>e.positional("id",{describe:"Dataset id (or unique prefix)",type:"string"}).option("add",{alias:"a",type:"array",describe:"One or more tags to add."}).option("remove",{alias:"r",type:"array",describe:"One or more tags to remove."});async function za({client:e,id:t,add:a,remove:i,format:n}){try{const s=await p(t,e.datasets),o=s.metadata.tags,r=new Set(o);a&&a.forEach(y=>r.add(y)),i&&i.forEach(y=>r.delete(y));const c=Array.from(r),m=await e.datasets.setMetadata(s.id,"tags",c);d(n==="csv"?m.tags.map(y=>({tag:y})):m.tags,n||"table")}catch(s){l(s)}}const Va={__proto__:null,builder:Ka,command:Za,desc:Fa,handler:za},Wa=["update <id>"],Ba="Update a dataset",Ha=e=>e.option("type",{alias:"t",description:"Dataset type",type:"string",choices:["table","sql"]}).option("source",{alias:"s",description:"Dataset source",type:"string"}).option("name",{alias:"n",description:"Dataset name",type:"string"}).option("description",{alias:"d",description:"Dataset description",type:"string"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).option("autodetect",{describe:"Autodetect geometry",type:"boolean"}).option("geometryType",{describe:"Geometry type (ignored when autodetect=true)",type:"string"}).option("srid",{describe:"Geometry SRID (ignored when autodetect=true)",type:"number"}).option("extent",{description:"Geometry extent x1,y1,x2,y2 (lng/lat) (ignored when autodetect=true)",type:"array"}).positional("id",{describe:"Dataset id (or unique prefix)",type:"string"});async function Qa(e){const t=e.client,a=e.id;try{const i=await p(a,t.datasets),n=["Point","LineString","Polygon","MultiPoint","MultiLineString","MultiPolygon","Geometry"];if(e.interactive){const r=await b.prompt([{type:"input",name:"name",message:"Name",default:e.name||i.name},{type:"input",name:"description",message:"Description",default:e.description||i.description},{type:"confirm",name:"autodetect",message:"Autodetect geometry?",default:Object.prototype.hasOwnProperty.call(e,"autodetect")?e.autodetect:i.options&&Object.prototype.hasOwnProperty.call(i.options,"autodetect")?i.options.autodetect:!0},{type:"select",name:"geometryType",message:"Geometry Type",choices:n,default:e.geometryType||i.options?.geometryType||"Geometry",when:({autodetect:c})=>c===!1},{type:"input",name:"srid",message:"Geometry SRID",default:e.srid??i.options?.srid??4326,validate:c=>M(c)||"Enter a number",when:({autodetect:c})=>c===!1},{type:"input",name:"extent",message:"Map extent (x1 y1 x2 y2) (in SRID units)",default:R(e.extent??i.options?.extent??[-180,-90,180,90]),validate:c=>J(c)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({autodetect:c})=>c===!1},{type:"select",name:"type",message:"Source type",choices:["table","sql"],default:e.type||i.type},{type:"input",name:"source",message:"Table name",default:e.source||i.source,when:({type:c})=>c==="table"},{type:"editor",name:"source",message:"SQL query",default:e.source||i.source,when:({type:c})=>c==="sql"}]);e.name=r.name,e.description=r.description,e.autodetect=r.autodetect,e.geometryType=r.geometryType,e.srid=r.autodetect?void 0:r.srid,e.extent=r.autodetect?void 0:W(r.extent),e.type=r.type,e.source=r.source.trim()}const s=await t.datasets.update(i.id,{id:e.id,name:e.name,description:e.description,type:e.type,source:e.source,options:{autodetect:e.autodetect,geometryType:e.geometryType,srid:e.srid,extent:e.extent}}),o=s.attributes.find(r=>r.isGeometry);e.format==="table"?d(s,"table"):d(s,"json"),o&&o.type==="geometry"&&s.options.autodetect&&(l(`Warning: could not auto-detect SRID or extent of field '${o.attribute}' (query produced no features)`),v(`Using the following default values: SRID(${o.srid}), extent: ${R(o.extent)}`),v("Re-run with --auto-detection=false to manually specify values if this is not correct."))}catch(i){l(i)}}const Xa={__proto__:null,builder:Ha,command:Wa,desc:Ba,handler:Qa},Ya=["datasets <command>","ds"],en="Dataset commands",tn=e=>e.command(Ta).command(ba).command(_a).command(Da).command(Ga).command(Xa).command(la).command(La).command(Va).demandCommand(1),an={__proto__:null,builder:tn,command:Ya,desc:en},nn=["delete <id>","rm","del"],sn="Delete a job",on=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Job id (or unique prefix)",type:"string"});async function rn({client:e,id:t,yes:a}){try{const i=await p(t,e.jobs);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete job '${i.name}' (${i.id})`,default:!1})).confirmed),n&&(await e.jobs.delete(i.id),_("Deleted"))}catch(i){l(i)}}const dn={__proto__:null,builder:on,command:nn,desc:sn,handler:rn},cn=["list [id]","ls","$0"],ln="List jobs",pn=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:"Job id (or unique prefix)",type:"string"});async function mn({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.jobs);d(o,a)}else s=await e.jobs.list({limit:i,sort:n}),a==="table"?d(s,"table",["id","name","description","type","status"]):d(s,a??"json")}catch(s){l(s)}}const un={__proto__:null,builder:pn,command:cn,desc:ln,handler:mn},yn=["metadata <id>","meta"],fn="Get job metadata",bn=e=>e.positional("id",{describe:"Job id (or unique prefix)",type:"string"});async function gn({client:e,id:t}){try{const a=await p(t,e.jobs),i=await e.jobs.getMetadata(a.id);d(i,"json")}catch(a){l(a)}}const hn={__proto__:null,builder:bn,command:yn,desc:fn,handler:gn},wn=["jobs <command>"],$n="Job commands",_n=e=>e.command(un).command(dn).command(hn).demandCommand(1),xn={__proto__:null,builder:_n,command:wn,desc:$n},vn="create <id>",Sn="Create a new map layer",An=e=>e.positional("id",{describe:"Map layer id",type:"string"}).option("name",{alias:"n",describe:"Name for new map layer",type:"string"}).option("description",{alias:"d",describe:"Description for new map layer",type:"string"}).option("layerspec",{type:"string",default:"{}",describe:"JSON-formatted layer specification"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}),Dn=async e=>{try{if(e.interactive){const a=await b.prompt([{type:"input",name:"name",message:"Name for new map layer",default:e.name},{type:"input",name:"description",message:"Description for new map layer",default:e.description},{type:"editor",name:"layerspec",message:"Layer specification",default:e.layerspec}]);e.name=a.name,e.description=a.description,e.layerspec=a.layerspec}let t={id:e.id,name:e.name,description:e.description,layerSpec:JSON.parse(e.layerspec)};t=await e.client.maplayers.create(t),e.format==="table"?d(t,"table",["id","name","description"]):d(t,e.format??"json")}catch(t){l(t)}},Nn={__proto__:null,builder:An,command:vn,desc:Sn,handler:Dn},jn=["delete <id>","rm","del"],kn="Delete a map layer",In=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Map layer id (or unique prefix)",type:"string"});async function On({client:e,id:t,yes:a}){try{const i=await p(t,e.maplayers);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.maplayers.delete(i.id),_("Deleted"))}catch(i){l(i)}}const Tn={__proto__:null,builder:In,command:jn,desc:kn,handler:On},Pn=["list [id]","ls","$0"],Mn="List map layers",En=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 qn({client:e,id:t,format:a,limit:i,sort:n}){try{if(t){const s=await p(t,e.maplayers);d(s,a)}else{const s=await e.maplayers.list({limit:i,sort:n});a==="table"?d(s,"table",["id","name","description"]):d(s,a??"json")}}catch(s){l(s)}}const Ln={__proto__:null,builder:En,command:Pn,desc:Mn,handler:qn},Cn=["metadata <id>","meta"],Un="Get/set resource metadata",Jn=e=>e.positional("id",{describe:"Map layer 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 Rn({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.maplayers);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.maplayers.deleteMetadata(i.id,s):await e.maplayers.setMetadata(i.id,s,o)}i=await p(t,e.maplayers),d(i.metadata,"json")}else{const i=await p(t,e.maplayers);d(i.metadata,"json")}}catch(i){l(i)}}const Gn={__proto__:null,builder:Jn,command:Cn,desc:Un,handler:Rn},Zn=["update <id>"],Fn="Update a map layer",Kn=e=>e.option("type",{alias:"t",description:"Map layer type",type:"string",choices:["table","sql"]}).option("name",{alias:"n",description:"Map layer name",type:"string"}).option("description",{alias:"d",description:"Map layer description",type:"string"}).option("layerspec",{type:"string",describe:"JSON-formatted layer specification"}).option("interactive",{alias:"i",describe:"Show interactive prompts",type:"boolean",default:"true"}).positional("id",{describe:"Map layer id (or unique prefix)",type:"string"});async function zn(e){const t=e.client,a=e.id;try{const i=await p(a,t.maplayers);if(e.interactive){const s=await b.prompt([{type:"input",name:"name",message:"Name",default:e.name||i.name},{type:"input",name:"description",message:"Description",default:e.description||i.description},{type:"editor",name:"layerspec",message:"Layer style specification",default:e.layerspec||JSON.stringify(i.layerSpec)}]);e.name=s.name,e.description=s.description,e.layerspec=s.layerspec}const n=await t.maplayers.update(i.id,{id:e.id,name:e.name,description:e.description,layerSpec:JSON.parse(e.layerspec)});e.format==="table"?d(n,"table"):d(n,"json")}catch(i){l(i)}}const Vn={__proto__:null,builder:Kn,command:Zn,desc:Fn,handler:zn},Wn=["maplayers <command>","layers"],Bn="Map layers commands",Hn=e=>e.command(Ln).command(Nn).command(Tn).command(Gn).command(Vn).demandCommand(1),Qn={__proto__:null,builder:Hn,command:Wn,desc:Bn},Xn="mcp",Yn="Start the Flow MCP server for AI agent access via the Model Context Protocol";function es(e){return e}const I={limit:u.number().int().min(1).max(1e3).optional().describe("Maximum number of results to return"),offset:u.number().int().min(0).optional().describe("Number of results to skip for pagination"),search:u.string().optional().describe('Advanced search as a JSON object (nestjsx/crud syntax). Simple equality: {"field":"value"}. With operator: {"field":{"$cont":"partial"}}. Multiple conditions (implicit AND): {"name":{"$starts":"foo"},"active":true}. Explicit OR: {"$or":[{"name":"a"},{"name":"b"}]}. Nested AND/OR: {"$or":[{"$and":[{"a":1},{"b":2}]},{"c":3}]}. Comparison operators: $eq $ne $gt $gte $lt $lte. String operators: $starts $ends $cont $excl (case-sensitive); $startsL $endsL $contL $exclL (case-insensitive). Set operators: $in $notin (value is array). Null checks: $isnull $notnull (value ignored). Range: $between (value is [min,max] array). Example \u2014 datasets whose name contains "boundary": {"name":{"$contL":"boundary"}}. Note: search operates on top-level scalar fields only; JSON object fields such as metadata cannot be searched into.'),sort:u.string().optional().describe("Field name to sort by, prefix with - for descending (e.g. -createdAt)"),attributes:u.string().optional().describe("Comma-separated list of attributes to include in the response")};function O(e){return{limit:e.limit,offset:e.offset,search:e.search?JSON.parse(e.search):void 0,sort:e.sort,attributes:e.attributes}}async function ts(e){const{client:t}=e,a=new Ie({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:u.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. The fields listed in the attributes parameter are also the fields available for searching. The 'metadata' attribute is a JSON object containing arbitrary information about the dataset including a tags array. The 'attributes' attribute is a JSON object that describes the schema of the data held in the dataset. ",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,name,description,type").describe("Comma-separated fields to return (default: id, name, description, type). Available fields: id, name, description, type, source, bytes, createdAt, updatedAt, applicationId, ownerId, databaseId, options, attributes, metadata.")}},async n=>{console.error("Tool: list_datasets called",n);const s=await t.datasets.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_dataset",{description:"Get details of a specific dataset by ID, including its schema, attributes, and metadata",inputSchema:{id:u.string().describe("Dataset ID")}},async({id:n})=>{const s=await t.datasets.get(n);return{content:[{type:"text",text:JSON.stringify(s,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:u.string().describe("Dataset ID to query"),where:u.string().optional().describe('JSON filter clause applied to dataset rows. Simple equality: {"field":"value"}. With operator: {"field":{"$gt":1}}. Multiple conditions (implicit AND): {"a":1,"b":2}. Logical: $or [{clause1},{clause2}], $and [{clause1},{clause2}]. Comparison: $eq $ne $gt $gte $lt $lte. Null checks: $is $not (value null). String: $like $notLike (use % wildcard); $iLike $notILike (case-insensitive). Regex: $regexp $notRegexp; $iRegexp $notIRegexp (case-insensitive). Range: $between $notBetween (value is [min,max] array). Set: $in (value is array). Example \u2014 rows where no2 > 50: {"no2":{"$gt":50}}.'),format:u.enum(["json","geojson","csv"]).optional().default("json").describe("Output format"),limit:u.number().int().min(1).max(1e4).optional().describe("Maximum number of features to return"),offset:u.number().int().min(0).optional().describe("Number of features to skip"),order:u.string().optional().describe("Comma-separated list of fields to sort by, prefix with - for descending (e.g. -timestamp,name)"),params:u.string().optional().describe(`Additional query parameters as JSON string. Use to pass extra params to parameterized SQL-type datasets (e.g. {"start_timestamp":"2026-01-01T00:00:00Z"}). Available parameters are usually specified in the dataset description as '{{param_name}}'.`),explain:u.boolean().optional().default(!1).describe("Set to `true` to generate a query explanation rather than return data")}},async({id:n,where:s,order:o,format:r,params:c,limit:m,offset:y,explain:f})=>{const g={where:s?JSON.parse(s):{}};m!==void 0&&(g.limit=m),y!==void 0&&(g.offset=y),o!==void 0&&(g.order=o.split(",").map(N=>(N=N.trim(),N.startsWith("-")?[N.slice(1),"DESC"]:[N,"ASC"])));const S=c?JSON.parse(c):{};S.explain=f,S.format=r??"json";const h=await t.datasets.query(n,g,S);return{content:[{type:"text",text:typeof h=="string"?h:JSON.stringify(h,null,2)}]}}),a.registerTool("list_styles",{description:"List Mapbox GL styles (named map configurations) in flow-engine with minimal fields (id, name, description, type). Styles contain full Mapbox GL JSON and can be very large \u2014 use this to discover what styles exist, then call get_style for the complete style definition of a specific one. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,name,description,type").describe("Comma-separated fields to return (default: id, name, description, type). Available fields: id, name, description, type, style, metadata, createdAt, updatedAt, applicationId. Avoid including style in list results as it contains the full Mapbox GL JSON and can be very large.")}},async n=>{console.error("Tool: list_styles called",n);const s=await t.styles.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_style",{description:"Get the full definition of a specific Mapbox GL style by ID, including the complete style JSON with layers, sources, and map configuration",inputSchema:{id:u.string().describe("Style ID")}},async({id:n})=>{const s=await t.styles.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_tile_sources",{description:"List tile sources in flow-engine with minimal fields (id, type, source, attribution, minZoom, maxZoom). Tile sources are raster or vector tile sets used as base layers or overlays in map styles. Use this to discover what tile sources exist, then call get_tile_source for full details on a specific one. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,type,source,attribution,minZoom,maxZoom").describe("Comma-separated fields to return (default: id, type, source, attribution, minZoom, maxZoom). Available fields: id, type, source, attribution, minZoom, maxZoom, extentLngLat, headers, metadata, createdAt, updatedAt, applicationId.")}},async n=>{console.error("Tool: list_tile_sources called",n);const s=await t.tileSources.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_tile_source",{description:"Get full details of a specific tile source by ID, including extent, zoom range, headers, and metadata",inputSchema:{id:u.string().describe("Tile source ID")}},async({id:n})=>{const s=await t.tileSources.get(n);return{content:[{type:"text",text:JSON.stringify(s,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. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,environment,description,startedAt,heartbeatAt").describe("Comma-separated fields to return (default: id, environment, description, startedAt, heartbeatAt). Available fields: id, environment, description, startedAt, heartbeatAt, createdAt, updatedAt, applicationId, metadata, config, components.")}},async n=>{const s=await t.pipelines.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_pipeline",{description:"Get details of a specific pipeline by ID, including its components and configuration",inputSchema:{id:u.string().describe("Pipeline ID")}},async({id:n})=>{const s=await t.pipelines.get(n,{join:"components"});return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_databases",{description:"List external database connections registered in flow-engine with minimal fields (id, name, description, type, host, db). Use this to discover what databases exist, then call get_database for full details on a specific database. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,name,description,type,host,db").describe("Comma-separated fields to return (default: id, name, description, type, host, db). Available fields: id, name, description, type, host, port, db, user, default, createdAt, updatedAt, applicationId.")}},async n=>{console.error("Tool: list_databases called",n);const s=await t.databases.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_database",{description:"Get full details of a specific database connection by ID, including host, port, database name, user, and associated application",inputSchema:{id:u.string().describe("Database ID")}},async({id:n})=>{const s=await t.databases.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_applications",{description:"List applications registered in flow-engine with minimal fields (id, name, description). Use this to discover what applications exist, then call get_application for full details on a specific application. When authenticated with an application-specific API key, only one application will be returned \u2014 call get_application on it to retrieve general information about the current flow application (auth config, dataset access options, associated databases) that helps contextualize other resources such as datasets and pipelines. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,name,description").describe("Comma-separated fields to return (default: id, name, description). Available fields: id, name, description, contact, metadata, createdAt, updatedAt.")}},async n=>{console.error("Tool: list_applications called",n);const s=await t.applications.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_application",{description:"Get full details of a specific application by ID, including contact, metadata, and associated databases",inputSchema:{id:u.string().describe("Application ID")}},async({id:n})=>{const s=await t.applications.get(n,{join:"databases"});return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_contact_points",{description:"List contact points in flow-engine with minimal fields (id, type, description, recipients). Contact points are notification channels (email, Slack, Teams, SNS) used by pipelines to send alerts. Use this to discover what contact points exist, then call get_contact_point for full details on a specific one. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,type,description,recipients").describe("Comma-separated fields to return (default: id, type, description, recipients). Available fields: id, type, description, templates, recipients, host, port, user, metadata, createdAt, updatedAt, applicationId.")}},async n=>{console.error("Tool: list_contact_points called",n);const s=await t.contactpoints.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_contact_point",{description:"Get full details of a specific contact point by ID, including type, recipients, templates, and driver configuration",inputSchema:{id:u.string().describe("Contact point ID")}},async({id:n})=>{const s=await t.contactpoints.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_media",{description:"List media assets in flow-engine with minimal fields (id, type, fileName, mimeType). Media assets are files such as images or documents stored within the application. Use this to discover what media exists, then call get_media for full details on a specific asset. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,type,fileName,mimeType").describe("Comma-separated fields to return (default: id, type, fileName, mimeType). Available fields: id, type, fileName, mimeType, metadata, createdAt, updatedAt, applicationId.")}},async n=>{console.error("Tool: list_media called",n);const s=await t.media.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_media",{description:"Get full details of a specific media asset by ID, including file name, MIME type, and metadata",inputSchema:{id:u.string().describe("Media asset ID")}},async({id:n})=>{const s=await t.media.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_media_content",{description:"Download the content of a media asset by ID. Text-based assets (text/*, application/json, application/xml, etc.) are returned as plain text. Binary assets are returned as a base64-encoded string.",inputSchema:{id:u.string().describe("Media asset ID")}},async({id:n})=>{const[s,o]=await Promise.all([t.media.get(n),t.media.content(n)]),r=[];await new Promise((m,y)=>{o.on("data",f=>r.push(f)),o.on("end",m),o.on("error",y)});const c=Buffer.concat(r);return s.mimeType?.startsWith("text/")||s.mimeType==="application/json"||s.mimeType==="application/xml"?{content:[{type:"text",text:c.toString("utf-8")}]}:{content:[{type:"text",text:`base64:${c.toString("base64")}`}]}}),a.registerTool("list_maplayers",{description:"List map layers in flow-engine with minimal fields (id, name, description, type, group). Map layers define how data is visually rendered on a map, referencing tile sources and specifying style rules. Use this to discover what layers exist, then call get_maplayer for the full layer specification. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,name,description,type,group").describe("Comma-separated fields to return (default: id, name, description, type, group). Available fields: id, name, description, type, group, layerSpec, metadata, ownerId, createdAt, updatedAt, applicationId. Avoid including layerSpec in list results as it contains the full layer definition and can be large.")}},async n=>{console.error("Tool: list_maplayers called",n);const s=await t.maplayers.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_maplayer",{description:"Get full details of a specific map layer by ID, including the complete layer specification (layerSpec) that defines rendering rules and data source bindings",inputSchema:{id:u.string().describe("Map layer ID")}},async({id:n})=>{const s=await t.maplayers.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("list_users",{description:"List users registered in flow-engine with minimal fields (id, email, firstName, lastName, roles). Use this to discover who has access to the application, then call get_user for full details on a specific user. Defaults to 100 results \u2014 use limit/offset to paginate. Override the attributes parameter only if you specifically need additional fields on all results. The fields listed in the attributes parameter are also the fields available for searching.",inputSchema:{...I,limit:u.number().int().min(1).max(1e3).optional().default(100).describe("Maximum number of results to return (default 100)"),attributes:u.string().optional().default("id,email,firstName,lastName,roles").describe("Comma-separated fields to return (default: id, email, firstName, lastName, roles). Available fields: id, email, firstName, lastName, roles, emailVerified, enabled, metadata, applicationId.")}},async n=>{console.error("Tool: list_users called",n);const s=await t.auth.users.list(O(n));return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}),a.registerTool("get_user",{description:"Get full details of a specific user by ID, including roles and metadata",inputSchema:{id:u.string().describe("User ID")}},async({id:n})=>{const s=await t.auth.users.get(n);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}});const i=new Oe;await a.connect(i)}const is={__proto__:null,builder:es,command:Xn,describe:Yn,handler:ts},as=["delete <id>","rm","del"],ns="Delete media",ss=e=>e.option("yes",{alias:"y",description:"Answer 'yes' to confirmation messages",default:!1}).positional("id",{describe:"Media id (or unique prefix)",type:"string"});async function os({client:e,id:t,yes:a}){try{const i=await p(t,e.media);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.media.delete(i.id),_("Deleted"))}catch(i){l(i)}}const rs={__proto__:null,builder:ss,command:as,desc:ns,handler:os},ds=["download <id>","get"],cs="Download media",ls=e=>e.positional("id",{describe:"Media id (or unique prefix)",type:"string"}).option("out",{describe:"File path to save the content. Output to stdout if not specified",type:"string"});async function ps({client:e,id:t,out:a}){try{const i=await p(t,e.media);let n;a&&(n=T("Downloading").start());const s=await e.media.content(i.id);s.on("end",()=>n?.succeed(`Downloaded media to ${a}`)),s.pipe(a?$.createWriteStream(a):process.stdout)}catch(i){l(i)}}const ms={__proto__:null,builder:ls,command:ds,desc:cs,handler:ps},us=["list [id]","ls","$0"],ys="List media",fs=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:"Media id (or unique prefix)",type:"string"});async function bs({client:e,id:t,format:a,limit:i,sort:n}){try{let s;if(t){const o=await p(t,e.media);d(o,a)}else s=await e.media.list({limit:i,sort:n}),a==="table"?d(s,"table",["id","type","mimeType","fileName"]):d(s,a??"json")}catch(s){l(s)}}const gs={__proto__:null,builder:fs,command:us,desc:ys,handler:bs},hs=["metadata <id>","meta"],ws="Get/set resource metadata",$s=e=>e.positional("id",{describe:"Resource 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 _s({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.media);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.media.deleteMetadata(i.id,s):await e.media.setMetadata(i.id,s,o)}i=await p(t,e.media),d(i.metadata,"json")}else{const i=await p(t,e.media);d(i.metadata,"json")}}catch(i){l(i)}}const xs={__proto__:null,builder:$s,command:hs,desc:ws,handler:_s},vs=["upload <id> <filePath>"],Ss="Upload media from a file",As=e=>e.positional("id",{describe:"Media id",type:"string"}).positional("filePath",{describe:"Path to media file to upload",type:"string"}).option("type",{describe:"Media type - application-specific",type:"string"}).option("mimeType",{describe:"Media MIME type. Auto-detected if not provided.",type:"string"}),Ds=async({client:e,format:t,id:a,type:i,mimeType:n,filePath:s})=>{const o=j.basename(s);v(`Uploading file ${o} ...`);const r=T("Uploading").start();try{const c=$.createReadStream(s),m=await e.media.upload(a,{data:c,fileName:o,type:i,mimeType:n});r.succeed(),d(m,t??"json")}catch(c){r.stop(),l(c)}},Ns={__proto__:null,builder:As,command:vs,desc:Ss,handler:Ds},js=["media <command>"],ks="Media commands",Is=e=>e.command(gs).command(Ns).command(ms).command(rs).command(xs).demandCommand(1),Os={__proto__:null,builder:Is,command:js,desc:ks},re="https://s3.eu-west-2.amazonaws.com/emu-flo.w/starters";function Ts(e){return de(`${re}/${e.repo}`)}function de(e){const t=Me.parse(e);return new Promise((a,i)=>{Pe(t,n=>{if(n.statusCode===302)de(n.headers.location).then(a,i);else{const s=[];n.on("data",o=>s.push(o)),n.on("end",()=>{a(Buffer.concat(s))}),n.on("error",i)}})})}function Ps(e,t){return new Promise((a,i)=>{qe(e,{lazyEntries:!0},Ms(t,a,i))})}function Ms(e,t,a){return(i,n)=>{if(i)throw i;n.readEntry(),n.on("entry",s=>{const o=s.fileName.split("/");o[0]=e;const r=o.join(j.sep);r[r.length-1]===j.sep?n.readEntry():ce(j.dirname(r),()=>{n.openReadStream(s,(c,m)=>{if(c)throw c;m.on("end",()=>{n.readEntry()}),m.pipe($.createWriteStream(r))})})}),n.once("error",a),n.once("end",()=>{t()})}}function ce(e,t){if(e===".")return t();$.stat(e,a=>{if(a==null)return t();const i=j.dirname(e);ce(i,()=>{$.mkdir(e,t)})})}const Es=[];let E=null;function le(e){E=e,e&&(Q(e),process.once("uncaughtException",()=>G(!0)),process.once("exit",()=>G()),process.once("SIGINT",()=>G()),process.once("SIGTERM",()=>G()))}function G(e=!1){E&&qs(),setTimeout(()=>{E&&(Q(E),E=null),process.exit(e?1:0)},200)}function qs(){Es.forEach(e=>e.kill("SIGINT"))}async function Ls(e,t,a="ignore"){await Ce("npm",[e],{cwd:t,stdio:a})}function Q(e){$.existsSync(e)&&($.readdirSync(e).forEach(t=>{const a=P(e,t);$.lstatSync(a).isDirectory()?Q(a):$.unlinkSync(a)}),$.rmdirSync(e))}function Cs(e){return pe()?e:""}function Us(e){return e>1e3?`in ${(e/1e3).toFixed(2)} s`:`in ${parseFloat(e.toFixed(3))} ms`}function pe(){return process.platform==="win32"}function q(){return pe()?">":"$"}const Js=Le($.rename),me=new Map;async function Rs(e,t,a){if($.existsSync(t))throw new Error(`Folder "./${t}" already exists, please choose a different project name.`);if(t=t.toLowerCase().trim(),!Ks(t))throw new Error(`Project name "${t}" is not valid. It must be a kebab-case name without spaces.`);const i=new Te(w.bold("Preparing starter"));i.setSpinnerString(18),i.start();const n=Date.now();try{const o=await Zs(e);if(!o)throw new Error("starter install failed");await o(t)}finally{i.stop(!0)}const s=Us(Date.now()-n);D(`${w.green("\u2714")} ${w.bold("All setup")} ${Cs("\u{1F389}")} ${w.dim(s)}
|
|
6
6
|
|
|
7
|
-
${
|
|
7
|
+
${w.dim(q())} ${w.green("npm start")}
|
|
8
8
|
Starts the development server.
|
|
9
9
|
|
|
10
|
-
${
|
|
10
|
+
${w.dim(q())} ${w.green("npm run build")}
|
|
11
11
|
Builds your app in production mode.
|
|
12
12
|
|
|
13
|
-
${
|
|
13
|
+
${w.dim(q())} ${w.green("npm test")}
|
|
14
14
|
Starts the test runner.
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
${
|
|
17
|
+
${w.dim("We suggest that you begin by typing:")}
|
|
18
18
|
|
|
19
|
-
${
|
|
20
|
-
${
|
|
21
|
-
${
|
|
19
|
+
${w.dim(q())} ${w.green("cd")} ${t}
|
|
20
|
+
${w.dim(q())} ${w.green("npm start")}
|
|
21
|
+
${Gs(e)}
|
|
22
22
|
|
|
23
23
|
Happy coding! \u{1F388}
|
|
24
|
-
`)}function
|
|
25
|
-
${
|
|
24
|
+
`)}function Gs(e){const t=e.docs;return t?`
|
|
25
|
+
${w.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.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();
|
|
27
|
+
${w.dim("-")} ${w.cyan(t)}`:""}function Zs(e){let t=me.get(e);return t||(t=Fs(e),t.catch(a=>{l(a)}),me.set(e,t)),t}async function Fs(e){const t=process.cwd(),a=P(t,".tmp-flow-starter"),i=await Ts(e);return le(a),await Ps(i,a),await Ls("install",a),async n=>{const s=P(t,n);await Js(a,s),await Ue({files:[P(s,"*"),P(s,"config/**"),P(s,"src/**")],from:/flow-starter-project-name/g,to:n,allowEmptyPaths:!0}),le(null)}}function Ks(e){return!/[^a-zA-Z0-9-]/.test(e)}function zs(e,t){if(e.includes("/"))return{name:e,repo:e};const a=t.find(i=>i.name===e);if(!a)throw new Error(`Starter "${e}" does not exist.`);return a}const Vs=["new [name]"],Ws="Create a new Flo.w app",Bs=e=>e.option("s",{alias:"starter",describe:"Starter template",type:"string"}).positional("name",{describe:"Project name",type:"string"});async function Hs(e){try{const t=await fetch(`${re}/index.json`).then(i=>i.json());if(!e.starter){const i=await b.prompt([{type:"select",name:"starter",message:"Starter template",choices:t.map(n=>n.name),default:t[0].name}]);e.starter=i.starter}const a=zs(e.starter,t);await Rs(a,e.name,!1)}catch(t){l(t.message)}}const Qs={__proto__:null,builder:Bs,command:Vs,desc:Ws,handler:Hs},Xs=["cache [id]"],Ys="Show pipeline cache",eo=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function to({client:e,id:t,format:a}){try{const i=await p(t,e.pipelines,"id",["components"],["id","components.index"]);if(!i){d({message:`Pipeline not found: ${t}`},a??"json");return}const n=(i.components??[]).filter(o=>o.class==="cache").sort((o,r)=>o.index-r.index),s=n.map(o=>({id:o.id,type:o.type,description:o.description,related_components:o.metadata.components.join()}));if(a==="json"){d({id:i.id,environment:i.environment,caches:n},"json");return}s.length?d(s,"table",null,{noWrap:!0}):v("No Caches Found")}catch(i){l(i)}}const io={__proto__:null,builder:eo,command:Xs,desc:Ys,handler:to},ao=["components <id>","comps <id>"],no="Show pipeline components",so=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function oo({client:e,id:t,format:a}){try{const i=await p(t,e.pipelines,"id",["components"],["id","components.index"]);if(!i){d({message:`Pipeline not found: ${t}`},a??"json");return}const n=i.components??[];if(a==="json"){d(n,"json");return}const s=ro(n);if(!s.length){v("No Components Found");return}d(s,"table",["id","class","type","description","note"])}catch(i){l(i)}}function ro(e){if(!e.length)return[];const t={};e.forEach(o=>t[o.id]={...o,next:[],previous:[],indent:0}),Object.values(t).forEach(o=>{const r=o.metadata||{},c=r.previousComponents??[],m=r.nextComponents??[];o.previous=c.map(y=>t[y]),o.next=m.map(y=>t[y])});const a={};Object.values(t).forEach(o=>{const r=(o.metadata||{}).parentComponent;r&&(a[r]||(a[r]=[]),a[r].push(o))}),Object.values(a).forEach(o=>{o.sort((r,c)=>r.index-c.index)});const i=Object.values(t).filter(o=>o.class!=="cache"&&(o.previous??[]).length===0),n=new Set;function s(o,r){if(!o||n.has(o.id))return;n.add(o.id),o.indent=r,o.class==="pipeline"&&(a[o.id]??[]).forEach(y=>s(y,r+1));const c=(o.next??[]).filter(y=>y.class!=="cache"),m=o.type==="fp:fork"||c.length>1;c.forEach(y=>s(y,m?r+1:r))}return i.forEach(o=>s(o,0)),Object.values(t).filter(o=>o.class!=="cache").sort((o,r)=>o.index-r.index).map(o=>{const r=[],c=(o.metadata||{}).caches??[];c.length>0&&r.push(`uses cache: ${c.join(", ")}`);const m=o.indent>0?" ".repeat(o.indent)+"> ":"";function y(f){return f.class==="pipeline"?`(${f.id})`:f.type==="fp:fork"?`[${f.id}]`:f.id}return{id:m+y(o),class:o.class,type:o.type,description:o.description,note:r.join()}})}const co={__proto__:null,builder:so,command:ao,desc:no,handler:oo},lo=["config [id]"],po="Show pipeline config",mo=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function uo({client:e,id:t,format:a}){try{const i=(await p(t,e.pipelines,"id"))?.config;if(!i||Object.keys(i).length===0){d({message:`No configuration found for pipeline ${t}`},a??"json");return}const n=X(i);if(a==="json"){d(i,"json");return}const s=Object.entries(n).map(([o,r])=>({key:o,value:r}));d(s,"table",["key","value"],{noWrap:!0})}catch(i){l(i)}}function ue(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function X(e,t="",a={}){for(const[i,n]of Object.entries(e)){const s=t?`${t}.${i}`:i;Array.isArray(n)?n.forEach((o,r)=>{const c=`${s}[${r}]`;ue(o)?X(o,c,a):a[c]=o}):ue(n)?X(n,s,a):a[s]=n}return a}const yo={__proto__:null,builder:mo,command:lo,desc:po,handler:uo},fo=["list [id]","ls","$0"],bo="List pipelines",go=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 ho({client:e,id:t,format:a,limit:i,sort:n,filterCache:s,filterDataset:o,components:r}){try{if(n??="id",t){const f=await p(t,e.pipelines,"id",void 0,n),g=fe(f);a==="table"?d(ye(g),"table",null,{noWrap:!0}):d(g,a);return}const c=!!s||!!o,m=r||c,y=(await e.pipelines.list({sort:n,limit:c?void 0:i,join:m?"components":void 0})).filter(f=>$o(f,s)).filter(f=>wo(f,o)).slice(0,c?i:void 0).map(f=>r?f:_o(f));xo(y,a)}catch(c){l(c)}}function wo(e,t){return!t||ie(e.components).some(a=>a.class==="sink"&&a.type==="fp:flow-ingest-stream"&&a.config?.datasetId?.includes(t))}function $o(e,t){return!t||ie(e.components).some(a=>a.class==="cache"&&a.config?.id?.includes(t))}function _o({components:e,...t}){return t}function xo(e,t){const a=e.map(fe);t==="table"?d(a.map(ye),"table",["id","environment","version","description","startedAt","heartbeatAt"]):d(a,t??"json")}function ye(e){const t=e.heartbeatAt&&(Date.now()-new Date(e.heartbeatAt).getTime())/1e3<30;return{...e,heartbeatAt:{color:t?"06a453":"e11529",value:e.heartbeatAt}}}function fe(e){const t=e.metadata.version?.tag;return{...e,version:t&&t.replace(/^release-/,"")}}const vo={__proto__:null,builder:go,command:fo,desc:bo,handler:ho},So=["sink [id]"],Ao="Show pipeline sink",Do=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function No({client:e,id:t,format:a}){try{const i=await p(t,e.pipelines,"id",["components"],["id","components.index"]);if(!i){d({message:`Pipeline not found: ${t}`},a??"json");return}const n=(i.components??[]).filter(o=>o.class==="sink").sort((o,r)=>o.index-r.index),s=n.reduce((o,r)=>(o[r.type]??=[],o[r.type].push(r),o),{});if(a==="json"){d({id:i.id,environment:i.environment,sinks:n},"json");return}if(Object.keys(s).length)for(const[,o]of Object.entries(s)){const r=o.map(c=>({id:c.id,description:c.description,...jo(c)}));d(r,"table")}else v("No Sinks Found")}catch(i){l(i)}}function jo(e){const t=e.config||{};switch(e.type){case"fp:amqp-writable-stream":return{vhost:t.vhost??"N/A",exchange:typeof t.exchange=="string"?t.exchange:t.exchange?.name??"N/A",enable:t.enable};case"fp:flow-ingest-stream":return{datasetId:t.datasetId??"N/A",enable:t.enable};case"fp:sqs-writable-stream":return{queueUrl:t.queueUrl??"N/A",region:t.region??"N/A",enable:t.enable};case"fp:sns-writable-stream":return{topicArn:t.topicArn??"N/A",region:t.region??"N/A",enable:t.enable};case"fp:null-sink":return{logging:t.logging};default:return{}}}const ko={__proto__:null,builder:Do,command:So,desc:Ao,handler:No},Io=["source [id]"],Oo="Show pipeline source",To=e=>e.positional("id",{describe:"Pipeline id (or unique prefix)",type:"string"});async function Po({client:e,id:t,format:a}){try{const i=await p(t,e.pipelines,"id",["components"],["id","components.index"]);if(!i){d({message:`Pipeline not found: ${t}`},a??"json");return}const n=(i.components??[]).filter(o=>o.class==="source").sort((o,r)=>o.index-r.index),s=n.map(o=>({id:o.id,type:o.type,description:o.description,...Mo(o)}));if(a==="json"){d({id:i.id,environment:i.environment,sources:n},"json");return}s.length?d(s,"table"):v("No Sources Found")}catch(i){l(i)}}function Mo(e){const t=e.config||{};switch(e.type){case"fp:amqp-readable-stream":return{vhost:t.vhost??"N/A",exchange:t.queue?.bindTo?.exchange??t.exchange??"N/A",queue:typeof t.queue=="string"?t.queue:t.queue?.name??"N/A"};case"fp:mqtt-subscriber-stream":return{serverUrl:t.serverUrl??"N/A",topic:t.topic??"N/A"};case"fp:flow-dataset-stream":return{datasetId:t.datasetId??"N/A"};case"fp:db-timeseries-stream":return{database:t.database??"N/A",table:t.table??"N/A"};case"fp:sqs-readable-stream":return{queueUrl:t.queueUrl??"N/A",region:t.region??"N/A"};case"fp:s3-file-list-stream":return{bucket:t.bucket??"N/A"};case"fp:s3-file-loader-stream":return{bucket:t.bucket??"N/A"};case"fp:planefinder-firehose-stream":return{host:t.host??"N/A"};case"fp:tcp-tls-readable-stream":return{hostname:t.hostname??"N/A"};case"fp:timer-stream":return{interval:t.interval??"N/A"};default:return{}}}const Eo={__proto__:null,builder:To,command:Io,desc:Oo,handler:Po},qo=["pipelines <command>","pipes"],Lo="Pipeline commands",Co=e=>e.command(vo).command(yo).command(co).command(Eo).command(ko).command(io).demandCommand(1),Uo={__proto__:null,builder:Co,command:qo,desc:Lo},Jo="create <id>",Ro="Create a new map style",Go=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"}),Zo=async e=>{try{const t=[].concat(await be(e.host,e.apiKey),await be(e.host,e.sourceKey)),a=t.map(s=>({name:s.name,value:s,short:s.name}));let i=t.find(s=>s.id===e.sourceId);if(e.interactive){const s=await b.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:i}]);e.name=s.name,e.description=s.description,e.sourceId=s.sourceStyle.id,i=t.find(o=>o.id===e.sourceId)}i.style.id=e.id,i.style.name=e.name,i.style.metadata.description=e.description;let n={id:e.id,name:e.name,description:e.description,type:"plain",style:i.style};n=await e.client.styles.create(n),e.format==="table"?d(n,"table",["id","name","description","type"]):d(n,e.format??"json")}catch(t){l(t)}};async function be(e,t){return new K(e,t).styles.list()}const Fo={__proto__:null,builder:Go,command:Jo,desc:Ro,handler:Zo},Ko=["delete <id>","rm","del"],zo="Delete a map style",Vo=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 Wo({client:e,id:t,yes:a}){try{const i=await p(t,e.styles);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.styles.delete(i.id),_("Deleted"))}catch(i){l(i)}}const Bo={__proto__:null,builder:Vo,command:Ko,desc:zo,handler:Wo},Ho=j.dirname(Ee(import.meta.url)),x=z();x.use(z.json({limit:"1mb"})),x.use(z.static(j.join(Ho,"../maputnik"))),x.get("/styles",(e,t)=>{v(":thumbsup: Maputnik connected");const a=x.get("style");t.json([a.id])}),x.get("/styles/:id",async(e,t)=>{try{const a=x.get("style");v(`Loading style '${a.id}'`),t.json(a)}catch(a){l(a.message)}}),x.put("/styles/:id",async(e,t)=>{try{const a=x.get("client");if(x.set("style",e.body),!x.get("writeable"))return t.status(204).send();const i={...e.body};i.sources=Qo(i.sources,`${a.baseUrl}`),i.glyphs=L(i.glyphs,a.baseUrl),i.sprite=L(i.sprite,a.baseUrl),await Yo(a,i.id,i),t.status(204).send()}catch(a){l(a.message)}});function Qo(e,t){return Object.keys(e).reduce((a,i)=>{let n=e[i];return Object.prototype.hasOwnProperty.call(n,"url")&&(n={...n,url:L(n.url,t)}),Object.prototype.hasOwnProperty.call(n,"data")&&(n={...n,data:L(n.data,t)}),Object.prototype.hasOwnProperty.call(n,"tiles")&&(n={...n,tiles:n.tiles.map(s=>L(s,t))}),a[i]=n,a},{})}function L(e,t){const a=new URL(e,t);return a.searchParams.delete("apikey"),a.toString().replace(t,"").replace(/%7B/g,"{").replace(/%7D/g,"}")}function Xo(e,t,a){return e.styles.update(t,{style:a})}const Yo=Re(Xo,2e3,{leading:!0});class Y{server;port;constructor(t,a,i){this.port=this.normalizePort(process.env.PORT||"8000"),x.set("port",this.port),x.set("style",a),x.set("writeable",i),x.set("client",t),this.server=Je.createServer(x),this.server.listen(this.port),this.server.on("error",this.onError.bind(this)),this.server.on("listening",this.onListening.bind(this))}normalizePort(t){const a=parseInt(t,10);return isNaN(a)?t:a>=0?a:!1}onError(t){if(t.syscall!=="listen")throw t;const a=typeof this.port=="string"?`Pipe ${this.port}`:`Port ${this.port}`;switch(t.code){case"EACCES":l(`${a} requires elevated privileges`),process.exit(1);break;case"EADDRINUSE":l(`${a} is already in use`),process.exit(1);break;default:throw t}}onListening(){_(`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 er="edit <id>",tr="Edit a map style in maputnik",ir=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),ar=async({client:e,id:t})=>{try{const a=await p(t,e.styles),i=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,i,!0),V("http://localhost:8000")}catch(a){l(a)}},nr={__proto__:null,builder:ir,command:er,desc:tr,handler:ar},sr=["list [id]","ls","$0"],or="List map styles",rr=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 dr({client:e,id:t,format:a,limit:i,sort:n}){try{if(t){const s=await p(t,e.styles);d(s,a)}else{const s=await e.styles.list({limit:i,sort:n});a==="table"?d(s,"table",["id","name","description","type"]):d(s,a??"json")}}catch(s){l(s)}}const cr={__proto__:null,builder:rr,command:sr,desc:or,handler:dr},lr=["metadata <id>","meta"],pr="Get/set resource metadata",mr=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 ur({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.styles);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.styles.deleteMetadata(i.id,s):await e.styles.setMetadata(i.id,s,o)}i=await p(t,e.styles),d(i.metadata,"json")}else{const i=await p(t,e.styles);d(i.metadata,"json")}}catch(i){l(i)}}const yr={__proto__:null,builder:mr,command:lr,desc:pr,handler:ur},fr="view <id>",br="View a map style in maputnik",gr=e=>e.positional("id",{describe:"Map style id (or unique prefix)",type:"string"}),hr=async({client:e,id:t})=>{try{const a=await p(t,e.styles),i=await e.maps.get(a.id,{apikey:e.apiKey});new Y(e,i,!1),V("http://localhost:8000")}catch(a){l(a)}},wr={__proto__:null,builder:gr,command:fr,desc:br,handler:hr},$r="styles <command>",_r="Style commands",xr=e=>e.command(cr).command(Fo).command(Bo).command(nr).command(wr).command(yr).demandCommand(1),vr={__proto__:null,builder:xr,command:$r,desc:_r},Sr=["create <id>"],Ar="Create a new tile source",Dr=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 Nr(e){const t=e.client,a=await t.tileSources.drivers();try{if(e.interactive){const s=await b.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:o})=>o==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:kr.bind(null,e.client),default:e.source,when:({type:o})=>o==="dataset"},{type:"input",name:"attribution",message:"Attribution",default:e.attribution},{type:"input",name:"minZoom",message:"Minimum zoom",default:e.minZoom||0,validate:o=>ee(o)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||18,validate:o=>ee(o)||"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:o=>ge(o)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:o})=>o}]);e.type=s.type,e.source=s.source,e.attribution=s.attribution,e.minZoom=s.minZoom,e.maxZoom=s.maxZoom,e.extent=s.overrideExtent?s.extent.split(" "):[]}let i;if(e.extent&&e.extent.length===0)i=null;else if(e.extent){if(!ge(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");i=jr(e.extent)}let n={id:e.id,type:e.type,source:e.source,attribution:e.attribution,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:i};n=await t.tileSources.create(n),e.format==="table"?d(n,"table",["id","type","source","minZoom","maxZoom"]):d(n,e.format??"json")}catch(i){l(i)}}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(t=>!ee(t))}function jr(e){return e==null?null:(typeof e=="string"&&(e=e.split(" ")),e.map(t=>parseFloat(t)))}async function kr(e){return(await e.datasets.list({sort:"id"})).map(t=>t.id)}const Ir={__proto__:null,builder:Dr,command:Sr,desc:Ar,handler:Nr},Or=["delete <id>","rm","del"],Tr="Delete a tile source",Pr=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 Mr({client:e,id:t,yes:a}){try{const i=await p(t,e.tileSources);let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.id}'`,default:!1})).confirmed),n&&(await e.tileSources.delete(i.id),_("Deleted"))}catch(i){l(i)}}const Er={__proto__:null,builder:Pr,command:Or,desc:Tr,handler:Mr},qr=["list [id]","ls","$0"],Lr="List tile sources",Cr=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 Ur({client:e,id:t,format:a,limit:i,sort:n}){try{if(t){const s=await p(t,e.tileSources);d(s,a)}else{const s=await e.tileSources.list({limit:i,sort:n});a==="table"?d(s,"table",["id","type","source","minZoom","maxZoom"]):d(s,a??"json")}}catch(s){l(s)}}const Jr={__proto__:null,builder:Cr,command:qr,desc:Lr,handler:Ur},Rr=["metadata <id>","meta"],Gr="Get/set resource metadata",Zr=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 Fr({client:e,id:t,set:a}){try{if(a){let i=await p(t,e.tileSources);const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.tileSources.deleteMetadata(i.id,s):await e.tileSources.setMetadata(i.id,s,o)}i=await p(t,e.tileSources),d(i.metadata,"json")}else{const i=await p(t,e.tileSources);d(i.metadata,"json")}}catch(i){l(i)}}const Kr={__proto__:null,builder:Zr,command:Rr,desc:Gr,handler:Fr},zr="preview <tilesourceId>",Vr="Preview a tile source in maputnik",Wr=async({client:e,tilesourceId:t})=>{try{const a=await p(t,e.tileSources),i=await e.tiles.getPreviewStyle(a.id,{apikey:e.apiKey});new Y(e,i,!1),V("http://localhost:8000")}catch(a){l(a)}},Br={__proto__:null,command:zr,desc:Vr,handler:Wr},Hr=["tilespec <tilesourceId>"],Qr="Get tileJson for tile source",Xr=e=>e.positional("tilesourceId",{describe:"Tile source id",type:"string"}),Yr=async({client:e,format:t,tilesourceId:a})=>{try{const i=await p(a,e.tileSources),n=await e.tiles.getTileSpec(i.id);t==="table"&&(delete n.vector_layers,delete n.attribution,delete n.tiles,n.center=JSON.stringify(n.center),n.bounds=JSON.stringify(n.bounds)),d(n,t)}catch(i){l(i)}},ed={__proto__:null,builder:Xr,command:Hr,desc:Qr,handler:Yr},td=["update <id>"],id="Update a tile source",ad=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 nd(e){const t=e.client,a=e.id,i=await t.tileSources.drivers();try{let n=await p(a,t.tileSources);if(e.interactive){const r=await b.prompt([{type:"select",name:"type",message:"Tilesource Type",choices:i,default:e.type||n.type},{type:"input",name:"source",message:"TileLive URL",default:e.source||n.source,when:({type:c})=>c==="tilelive"},{type:"select",name:"source",message:"Dataset",choices:sd.bind(null,e.client),default:e.source||n.source,when:({type:c})=>c!=="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:c=>M(c)||"Enter a number"},{type:"input",name:"maxZoom",message:"Maximum zoom",default:e.maxZoom||n.maxZoom,validate:c=>M(c)||"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:c=>J(c)||"Enter lng/lat bounding box as x1 y1 x2 y2",when:({overrideExtent:c})=>c}]);e.type=r.type,e.source=r.source,e.attribution=r.attribution,e.minZoom=r.minZoom,e.maxZoom=r.maxZoom,e.extent=r.overrideExtent?r.extent.split(" "):[]}let s;e.headers&&(s=e.headers.reduce((r,c)=>{const[m,y]=c.split("=");if(!m||!y)throw new Error("Bad format for headers: specify key/value pairs as <header>=<value>");return r[m]=y,r},{}));let o;if(e.extent&&e.extent.length===0)o=null;else if(e.extent){if(!J(e.extent))throw new Error("Bad format for extent: specify 'x1 y1 x2 y2' (lng/lat)");o=W(e.extent)}n=await t.tileSources.update(n.id,{id:e.id,type:e.type,source:e.source,attribution:e.attribution,headers:s,minZoom:e.minZoom,maxZoom:e.maxZoom,extentLngLat:o}),d(n,"table")}catch(n){l(n)}}async function sd(e){return(await e.datasets.list({sort:"id"})).map(t=>t.id)}const od={__proto__:null,builder:ad,command:td,desc:id,handler:nd},rd="tiles <command>",dd="Tile commands",cd=e=>e.command(Jr).command(Ir).command(Er).command(od).command(Kr).command(Br).command(ed).demandCommand(1),ld={__proto__:null,builder:cd,command:rd,desc:dd},pd=["list [id]","ls"],md="List uploads",ud=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 yd({client:e,id:t,format:a,limit:i,sort:n}){try{if(t){const s=await p(t,e.uploads);d(s,a)}else{const s=await e.uploads.list({limit:i,sort:n});a==="table"?d(s,"table",["id","fileName","status"]):d(s,a??"json")}}catch(s){l(s)}}const fd={__proto__:null,builder:ud,command:pd,desc:md,handler:yd},{fileSize:bd}=Ge,gd=1e3,hd=["upload <filePath>","$0"],wd="Upload a geospatial data file",$d=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"}),_d=async e=>{Object.prototype.hasOwnProperty.call(e,"source-srid")&&isNaN(e["source-srid"])&&(l("--source-srid must be a number"),process.exit(1)),Object.prototype.hasOwnProperty.call(e,"target-srid")&&isNaN(e["target-srid"])&&(l("--target-srid must be a number"),process.exit(1));const t=j.basename(e.filePath);v(`Uploading file ${t} ...`);const a=T("Uploading").start(),i=T("Ingesting"),n=T("Processing");try{const s=$.createReadStream(e.filePath),o=await e.client.uploads.upload({data:s,filename:t,datasetId:e.id,datasetName:e.name,datasetDescription:e.description,attribution:e.attribution,sourceSrid:e["source-srid"],targetSrid:e["target-srid"],schema:e.schema}),r=o.id,c=g=>new Promise(S=>{setTimeout(S,g)});let m,y=o.status,f=a;for(;;)if(await c(gd),m=await e.client.uploads.get(r),m.status!==y)if(y=m.status,y==="ingesting")a.succeed(),f=i,f.start();else if(y==="processing")a.succeed(),i.start(),i.succeed(),f=n,f.start();else if(y==="done"){a.succeed(),i.succeed(),n.succeed(),v(`Dataset uploaded with id '${m.options.datasetId}' (${bd(m.size)})`);break}else{f.fail(),l(m.error.message),_(m.error.stderr||m.error.stdout);break}}catch(s){a.stop(),i.stop(),n.stop(),l(s)}},xd={__proto__:null,builder:$d,command:hd,desc:wd,handler:_d},vd="upload <command>",Sd="Upload commands",Ad=e=>e.command(fd).command(xd).demandCommand(1),Dd={__proto__:null,builder:Ad,command:vd,desc:Sd},Nd=["create <email>"],jd="Create a user",kd=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 Id(e){const t=e.client;try{if(e.interactive){const n=await b.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(s=>s.trim()),e.emailVerified=n.emailVerified,e.enabled=n.enabled,e.password=n.password}const a=await t.auth.users.create({email:e.email,firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled,password:e.password}),i=te();await t.auth.users.update(a.id,{passwordResetToken:i}),await t.auth.resetPassword(a.email,e.password,a.applicationId,i),e.format==="table"?d(a,"table"):d(a,"json")}catch(a){l(a)}}const Od={__proto__:null,builder:kd,command:Nd,desc:jd,handler:Id},Td=["delete <email>","rm","del"],Pd="Delete a user",Md=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 Ed({client:e,email:t,yes:a}){try{const i=await p(t,e.auth.users,"email");let n=a;n||(n=(await b.prompt({name:"confirmed",type:"confirm",message:`Are you sure you want to delete '${i.email}'`,default:!1})).confirmed),n&&(await e.auth.users.delete(i.id),_("Deleted"))}catch(i){l(i)}}const qd={__proto__:null,builder:Md,command:Td,desc:Pd,handler:Ed},Ld=["list [email]","ls","$0"],Cd="List users",Ud=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 Jd({client:e,email:t,format:a,limit:i,sort:n}){try{if(t){const s=await p(t,e.auth.users,"email");d(s,a)}else{let s=await e.auth.users.list({limit:i,sort:n});a==="table"?(s=s.map(o=>({...o,name:`${o.firstName} ${o.lastName}`})),d(s,"table",["id","email","name","emailVerified","enabled"])):d(s,a??"json")}}catch(s){l(s)}}const Rd={__proto__:null,builder:Ud,command:Ld,desc:Cd,handler:Jd},Gd=["metadata <email>","meta"],Zd="Get/set resource metadata",Fd=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 Kd({client:e,email:t,set:a}){try{if(a){let i=await p(t,e.auth.users,"email");const n=k(a);for(const s of Object.keys(n)){const o=n[s];o===""?await e.auth.users.deleteMetadata(i.id,s):await e.auth.users.setMetadata(i.id,s,o)}i=await p(t,e.auth.users,"email"),d(i.metadata,"json")}else{const i=await p(t,e.auth.users,"email");d(i.metadata,"json")}}catch(i){l(i)}}const zd={__proto__:null,builder:Fd,command:Gd,desc:Zd,handler:Kd},Vd=["update <email>"],Wd="Update a user",Bd=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 Hd(e){const t=e.client,a=e.email;try{const i=await p(a,t.auth.users,"email");if(e.interactive){const s=await b.prompt([{type:"input",name:"firstName",message:"First name",default:e.firstName||i.firstName},{type:"input",name:"lastName",message:"Last name",default:e.lastName||i.lastName},{type:"input",name:"roles",message:"Roles (comma separated)",default:(e.roles||i.roles).join(", ")},{type:"confirm",name:"emailVerified",message:"Verified?",default:Object.prototype.hasOwnProperty.call(e,"emailVerified")?e.emailVerified:i.emailVerified},{type:"confirm",name:"enabled",message:"Enabled?",default:Object.prototype.hasOwnProperty.call(e,"enabled")?e.enabled:i.enabled},{type:"confirm",name:"changePassword",message:"Change password?",default:!1},{type:"password",name:"password",message:"New password",when:({changePassword:o})=>o},{type:"password",name:"confirmPassword",message:"Confirm password",when:({changePassword:o})=>o}]);if(s.changePassword){if(s.password!==s.confirmPassword)throw new Error("Passwords do not match");e.password=s.password}e.firstName=s.firstName,e.lastName=s.lastName,e.roles=s.roles.split(",").map(o=>o.trim()),e.emailVerified=s.emailVerified,e.enabled=s.enabled}if(e.password){const s=te();await t.auth.users.update(i.id,{passwordResetToken:s}),await t.auth.resetPassword(i.email,e.password,i.applicationId,s)}const n=await t.auth.users.update(i.id,{firstName:e.firstName,lastName:e.lastName,roles:e.roles,emailVerified:e.emailVerified,enabled:e.enabled});e.format==="table"?d(n,"table"):d(n,"json")}catch(i){l(i)}}const Qd={__proto__:null,builder:Bd,command:Vd,desc:Wd,handler:Hd},Xd="users <command>",Yd="User commands",ec=e=>e.command(Rd).command(Od).command(qd).command(zd).command(Qd).demandCommand(1),tc={__proto__:null,builder:ec,command:Xd,desc:Yd},ic="@emuanalytics/flow-cli",ac="2.2.3",nc="module",sc="dist/index.mjs",oc="Robin Summerhill <robin.summerhill@emu-analytics.com>",rc="Copyright 2018 Emu Analytics Limited",dc={flow:"dist/index.mjs"},cc=["dist","maputnik"],lc={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"},pc={"@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"},mc={"@emuanalytics/flow-engine-client":"^2.2.3","@modelcontextprotocol/sdk":"^1.12.0","@types/yargs":"^17.0.35",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"},uc={"@types/serve-static":"^1.15.0"},yc="7978d7c70187e18b09369d7ef69f949243735239",fc={name:ic,version:ac,type:nc,main:sc,author:oc,license:rc,bin:dc,files:cc,scripts:lc,devDependencies:pc,dependencies:mc,overrides:uc,"lint-staged":{"./src/**/*.ts":["eslint"]},gitHead:yc},bc="version",gc="Display Flo.w Engine version information",hc=async({host:e,apiKey:t,client:a,format:i})=>{try{const n=await a.config(),s={"CLI version":fc.version,"Flo.w version":n.version.tag,"Flo.w host":e,"API key":t};d(s,i)}catch(n){l(n.message)}},wc={__proto__:null,command:bc,desc:gc,handler:hc},he=$e([".flowrc",".flowrc.json"]),$c=he?JSON.parse($.readFileSync(he).toString("utf8")):{},_c=e=>{e.client=new K(e.host,e.apiKey)},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(oi).command(Ji).command(sa).command(an).command(xn).command(Qn).command(Os).command(Qs).command(Uo).command(vr).command(ld).command(Dd).command(tc).command(wc).command(is).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.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"author": "Robin Summerhill <robin.summerhill@emu-analytics.com>",
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
"unbuild": "^3.6.1"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@emuanalytics/flow-engine-client": "^2.2.
|
|
43
|
+
"@emuanalytics/flow-engine-client": "^2.2.3",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
45
|
+
"@types/yargs": "^17.0.35",
|
|
45
46
|
"JSONStream": "^1.3.5",
|
|
46
47
|
"chalk": "^5.6.2",
|
|
47
48
|
"cli-spinner": "^0.2.10",
|
|
@@ -74,5 +75,5 @@
|
|
|
74
75
|
"eslint"
|
|
75
76
|
]
|
|
76
77
|
},
|
|
77
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "7978d7c70187e18b09369d7ef69f949243735239"
|
|
78
79
|
}
|