@sinch/cli 0.1.20-beta.0 → 0.1.21-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var Xn=Object.create;var Ve=Object.defineProperty;var eo=Object.getOwnPropertyDescriptor;var to=Object.getOwnPropertyNames;var no=Object.getPrototypeOf,oo=Object.prototype.hasOwnProperty;var he=(o,e)=>()=>(o&&(e=o(o=0)),e);var U=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports),Je=(o,e)=>{for(var n in e)Ve(o,n,{get:e[n],enumerable:!0})},At=(o,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of to(e))!oo.call(o,s)&&s!==n&&Ve(o,s,{get:()=>e[s],enumerable:!(i=eo(e,s))||i.enumerable});return o};var m=(o,e,n)=>(n=o!=null?Xn(no(o)):{},At(e||!o||!o.__esModule?Ve(n,"default",{value:o,enumerable:!0}):n,o)),F=o=>At(Ve({},"__esModule",{value:!0}),o);var Ze=U((Pi,io)=>{io.exports={name:"@sinch/cli",version:"0.1.20-beta.0",description:"Official Sinch CLI - Manage all Sinch products from your terminal",main:"dist/index.js",bin:{sinch:"bin/sinch"},scripts:{start:"tsx src/index.ts",dev:"tsx watch src/index.ts",build:"tsc && node scripts/post-build.js","build:prod":"tsup src/index.ts --format cjs --dts --clean --minify",typecheck:"tsc --noEmit",test:"jest","test:e2e":"jest --config tests/e2e/jest.config.js","test:e2e:staging":"jest --config tests/e2e/jest.config.js --env=staging",prepublishOnly:"npm run build:prod"},keywords:[],author:"Sinch <support@sinch.com> (https://www.sinch.com)",license:"MIT",private:!1,publishConfig:{access:"public",registry:"https://registry.npmjs.org/"},homepage:"https://www.sinch.com/products/apis/voice/",files:["dist/","bin/","README.md","LICENSE"],dependencies:{"@sinch/sdk-core":"^1.2.1","adm-zip":"^0.5.10",axios:"^1.7.9",blessed:"^0.1.81",chalk:"^4.1.2",chokidar:"^3.6.0","cli-spinners":"^2.9.2","cli-table3":"^0.6.3",clipboardy:"^2.3.0",commander:"^12.1.0",eventsource:"^4.0.0","form-data":"^4.0.0","fs-extra":"^11.1.1",inquirer:"8.2.6",keytar:"^7.9.0",ora:"^4.1.1"},devDependencies:{"@types/adm-zip":"^0.5.7","@types/blessed":"^0.1.25","@types/fs-extra":"^11.0.4","@types/inquirer":"^9.0.9","@types/keytar":"^4.4.0","@types/node":"^24.3.0",execa:"^5.1.1",jest:"^29.7.0",nodemon:"^3.0.1",tsup:"^8.5.0",tsx:"^4.20.4",typescript:"^5.9.2"},engines:{node:">=20.0.0",npm:">=9.0.0"}}});var Ie={};Je(Ie,{SinchAPI:()=>I});var Me,tt,Ee,I,x=he(()=>{"use strict";Me=m(require("axios")),tt=m(require("form-data")),Ee=m(require("chalk")),I=class{baseUrl;oauthUrl;projectId;timeout;credentials;client;constructor(e={}){this.baseUrl=e.apiUrl||"https://functions.api.sinch.com/",this.oauthUrl=e.oauthUrl||"https://auth.sinch.com/oauth2/token",this.projectId=e.projectId||"",this.timeout=6e4,this.credentials=e.credentials||null,this.client=Me.default.create({baseURL:this.baseUrl,timeout:this.timeout,headers:{"User-Agent":"sinch-functions-cli/1.0.0"}}),this.client.interceptors.request.use(async n=>{if(this.credentials){let i=await this.getValidToken();i&&(n.headers.Authorization=`${i.token_type} ${i.access_token}`)}return process.env.DEBUG_HTTP&&console.log(Ee.default.gray(`\u2192 ${n.method?.toUpperCase()} ${n.url}`)),n},n=>Promise.reject(n)),this.client.interceptors.response.use(n=>(process.env.DEBUG_HTTP&&console.log(Ee.default.gray(`\u2190 ${n.status} ${n.config.url}`)),n),async n=>{if(process.env.DEBUG_HTTP&&console.log(Ee.default.red(`\u2190 ${n.response?.status||"ERROR"} ${n.config?.url}`)),n.response?.status===401&&this.credentials&&n.config){let i=n.config;if(!i._retry){i._retry=!0;try{let s=await this.getValidToken();if(s)return i.headers.Authorization=`${s.token_type} ${s.access_token}`,this.client.request(i)}catch{console.error(Ee.default.yellow("Token refresh failed during retry"))}}}return Promise.reject(n)})}async listAllTemplates(e=null){try{let n=e?{category:e}:{};return(await this.client.get(`/v1/projects/${this.projectId}/templates`,{params:n})).data}catch(n){throw this._handleError(n,"Failed to list templates")}}async listRuntimeTemplates(e,n=null){try{let i=n?{category:n}:{};return(await this.client.get(`/v1/projects/${this.projectId}/templates/${e}`,{params:i})).data}catch(i){throw this._handleError(i,`Failed to list ${e} templates`)}}async getTemplateDetails(e,n){try{return(await this.client.get(`/v1/projects/${this.projectId}/templates/${e}/${n}`)).data}catch(i){throw this._handleError(i,`Failed to get template details for ${e}/${n}`)}}async downloadTemplate(e,n){try{let i=await this.client.get(`/v1/projects/${this.projectId}/templates/${e}/${n}/download`,{responseType:"arraybuffer"});return Buffer.from(i.data)}catch(i){throw this._handleError(i,`Failed to download template ${e}/${n}`)}}async listFunctions(){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions`)).data}catch(e){throw this._handleError(e,"Failed to list functions")}}async getFunction(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}`)).data}catch(n){throw this._handleError(n,`Failed to get function ${e}`)}}async deployFunction(e,n,i,s,r="public"){try{let a=new tt.default;return a.append("name",e),a.append("runtime",n),a.append("code",i,{filename:"function.zip"}),a.append("accessLevel",r),s&&a.append("configuration",JSON.stringify(s)),(await this.client.post(`/v1/projects/${this.projectId}/functions`,a,{headers:{...a.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(a){throw this._handleError(a,"Failed to deploy function")}}async updateFunction(e,n,i){try{let s=new tt.default;return s.append("code",n,{filename:"function.zip"}),i&&s.append("configuration",JSON.stringify(i)),(await this.client.put(`/v1/projects/${this.projectId}/functions/${e}`,s,{headers:{...s.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(s){throw this._handleError(s,`Failed to update function ${e}`)}}async deleteFunction(e){try{await this.client.delete(`/v1/projects/${this.projectId}/functions/${e}`)}catch(n){throw this._handleError(n,`Failed to delete function ${e}`)}}async downloadFunction(e){try{let n=await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/download`,{responseType:"arraybuffer"});return Buffer.from(n.data)}catch(n){throw this._handleError(n,`Failed to download function ${e}`)}}async getFunctionLogs(e,n={}){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/logs`,{params:n})).data}catch(i){throw this._handleError(i,`Failed to get logs for function ${e}`)}}async getFunctionStatus(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/deployment/status`)).data}catch(n){throw this._handleError(n,`Failed to get status for function ${e}`)}}async generateDocumentationFromCode(e,n="node",i="function"){try{return(await this.client.post(`/v1/projects/${this.projectId}/test/documentation`,{Code:e,Runtime:n,Name:i})).data}catch(s){throw this._handleError(s,"Failed to generate documentation from code")}}async generateDocumentationForFunction(e,n){try{let i=n?{MarkdownOverride:n}:{};return(await this.client.post(`/v1/projects/${this.projectId}/functions/${e}/documentation`,i)).data}catch(i){throw this._handleError(i,`Failed to generate documentation for function ${e}`)}}async getDocumentationForFunction(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/documentation`)).data}catch(n){throw this._handleError(n,`Failed to get documentation for function ${e}`)}}async streamDeployment(e,n,i){let s=c=>c.status?"status":c.progress?"progress":c.completed?"completed":c.failed||c.error?"failed":c.connected?"connected":"message",r=new URL(`v1/projects/${this.projectId}/functions/${e}/deployment/stream`,this.baseUrl).href,a={Accept:"text/event-stream","Cache-Control":"no-cache"};if(this.credentials)try{let c=await this.credentials.retrieve();if(c&&c.keyId&&c.keySecret){let u=Buffer.from(`${c.keyId}:${c.keySecret}`).toString("base64");a.Authorization=`Basic ${u}`}}catch(c){console.error("Failed to add authentication:",c)}let l=null;try{l=new AbortController;let c=await fetch(r,{method:"GET",headers:a,signal:l.signal});if(!c.ok)throw new Error(`SSE connection failed: ${c.status} ${c.statusText}`);if(!c.body)throw new Error("Response body is null");let u=c.body.getReader(),g=new TextDecoder,h="";return(async()=>{let w="";try{for(;;){let{done:A,value:J}=await u.read();if(A)break;let W=g.decode(J,{stream:!0});h+=W;let $=h.split(`
2
+ "use strict";var Xn=Object.create;var Ve=Object.defineProperty;var eo=Object.getOwnPropertyDescriptor;var to=Object.getOwnPropertyNames;var no=Object.getPrototypeOf,oo=Object.prototype.hasOwnProperty;var he=(o,e)=>()=>(o&&(e=o(o=0)),e);var U=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports),Je=(o,e)=>{for(var n in e)Ve(o,n,{get:e[n],enumerable:!0})},At=(o,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of to(e))!oo.call(o,s)&&s!==n&&Ve(o,s,{get:()=>e[s],enumerable:!(i=eo(e,s))||i.enumerable});return o};var m=(o,e,n)=>(n=o!=null?Xn(no(o)):{},At(e||!o||!o.__esModule?Ve(n,"default",{value:o,enumerable:!0}):n,o)),F=o=>At(Ve({},"__esModule",{value:!0}),o);var Ze=U((Pi,io)=>{io.exports={name:"@sinch/cli",version:"0.1.21-beta.0",description:"Official Sinch CLI - Manage all Sinch products from your terminal",main:"dist/index.js",bin:{sinch:"bin/sinch"},scripts:{start:"tsx src/index.ts",dev:"tsx watch src/index.ts",build:"tsc && node scripts/post-build.js","build:prod":"tsup src/index.ts --format cjs --dts --clean --minify",typecheck:"tsc --noEmit",test:"jest","test:e2e":"jest --config tests/e2e/jest.config.js","test:e2e:staging":"jest --config tests/e2e/jest.config.js --env=staging",prepublishOnly:"npm run build:prod"},keywords:[],author:"Sinch <support@sinch.com> (https://www.sinch.com)",license:"MIT",private:!1,publishConfig:{access:"public",registry:"https://registry.npmjs.org/"},homepage:"https://www.sinch.com/products/apis/voice/",files:["dist/","bin/","README.md","LICENSE"],dependencies:{"@sinch/sdk-core":"^1.2.1","adm-zip":"^0.5.10",axios:"^1.7.9",blessed:"^0.1.81",chalk:"^4.1.2",chokidar:"^3.6.0","cli-spinners":"^2.9.2","cli-table3":"^0.6.3",clipboardy:"^2.3.0",commander:"^12.1.0",eventsource:"^4.0.0","form-data":"^4.0.0","fs-extra":"^11.1.1",inquirer:"8.2.6",keytar:"^7.9.0",ora:"^4.1.1"},devDependencies:{"@types/adm-zip":"^0.5.7","@types/blessed":"^0.1.25","@types/fs-extra":"^11.0.4","@types/inquirer":"^9.0.9","@types/keytar":"^4.4.0","@types/node":"^24.3.0",execa:"^5.1.1",jest:"^29.7.0",nodemon:"^3.0.1",tsup:"^8.5.0",tsx:"^4.20.4",typescript:"^5.9.2"},engines:{node:">=20.0.0",npm:">=9.0.0"}}});var Ie={};Je(Ie,{SinchAPI:()=>I});var Me,tt,Ee,I,x=he(()=>{"use strict";Me=m(require("axios")),tt=m(require("form-data")),Ee=m(require("chalk")),I=class{baseUrl;oauthUrl;projectId;timeout;credentials;client;constructor(e={}){this.baseUrl=e.apiUrl||"https://functions.api.sinch.com/",this.oauthUrl=e.oauthUrl||"https://auth.sinch.com/oauth2/token",this.projectId=e.projectId||"",this.timeout=6e4,this.credentials=e.credentials||null,this.client=Me.default.create({baseURL:this.baseUrl,timeout:this.timeout,headers:{"User-Agent":"sinch-functions-cli/1.0.0"}}),this.client.interceptors.request.use(async n=>{if(this.credentials){let i=await this.getValidToken();i&&(n.headers.Authorization=`${i.token_type} ${i.access_token}`)}return process.env.DEBUG_HTTP&&console.log(Ee.default.gray(`\u2192 ${n.method?.toUpperCase()} ${n.url}`)),n},n=>Promise.reject(n)),this.client.interceptors.response.use(n=>(process.env.DEBUG_HTTP&&console.log(Ee.default.gray(`\u2190 ${n.status} ${n.config.url}`)),n),async n=>{if(process.env.DEBUG_HTTP&&console.log(Ee.default.red(`\u2190 ${n.response?.status||"ERROR"} ${n.config?.url}`)),n.response?.status===401&&this.credentials&&n.config){let i=n.config;if(!i._retry){i._retry=!0;try{let s=await this.getValidToken();if(s)return i.headers.Authorization=`${s.token_type} ${s.access_token}`,this.client.request(i)}catch{console.error(Ee.default.yellow("Token refresh failed during retry"))}}}return Promise.reject(n)})}async listAllTemplates(e=null){try{let n=e?{category:e}:{};return(await this.client.get(`/v1/projects/${this.projectId}/templates`,{params:n})).data}catch(n){throw this._handleError(n,"Failed to list templates")}}async listRuntimeTemplates(e,n=null){try{let i=n?{category:n}:{};return(await this.client.get(`/v1/projects/${this.projectId}/templates/${e}`,{params:i})).data}catch(i){throw this._handleError(i,`Failed to list ${e} templates`)}}async getTemplateDetails(e,n){try{return(await this.client.get(`/v1/projects/${this.projectId}/templates/${e}/${n}`)).data}catch(i){throw this._handleError(i,`Failed to get template details for ${e}/${n}`)}}async downloadTemplate(e,n){try{let i=await this.client.get(`/v1/projects/${this.projectId}/templates/${e}/${n}/download`,{responseType:"arraybuffer"});return Buffer.from(i.data)}catch(i){throw this._handleError(i,`Failed to download template ${e}/${n}`)}}async listFunctions(){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions`)).data}catch(e){throw this._handleError(e,"Failed to list functions")}}async getFunction(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}`)).data}catch(n){throw this._handleError(n,`Failed to get function ${e}`)}}async deployFunction(e,n,i,s,r="public"){try{let a=new tt.default;return a.append("name",e),a.append("runtime",n),a.append("code",i,{filename:"function.zip"}),a.append("accessLevel",r),s&&a.append("configuration",JSON.stringify(s)),(await this.client.post(`/v1/projects/${this.projectId}/functions`,a,{headers:{...a.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(a){throw this._handleError(a,"Failed to deploy function")}}async updateFunction(e,n,i){try{let s=new tt.default;return s.append("code",n,{filename:"function.zip"}),i&&s.append("configuration",JSON.stringify(i)),(await this.client.put(`/v1/projects/${this.projectId}/functions/${e}`,s,{headers:{...s.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(s){throw this._handleError(s,`Failed to update function ${e}`)}}async deleteFunction(e){try{await this.client.delete(`/v1/projects/${this.projectId}/functions/${e}`)}catch(n){throw this._handleError(n,`Failed to delete function ${e}`)}}async downloadFunction(e){try{let n=await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/download`,{responseType:"arraybuffer"});return Buffer.from(n.data)}catch(n){throw this._handleError(n,`Failed to download function ${e}`)}}async getFunctionLogs(e,n={}){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/logs`,{params:n})).data}catch(i){throw this._handleError(i,`Failed to get logs for function ${e}`)}}async getFunctionStatus(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/deployment/status`)).data}catch(n){throw this._handleError(n,`Failed to get status for function ${e}`)}}async generateDocumentationFromCode(e,n="node",i="function"){try{return(await this.client.post(`/v1/projects/${this.projectId}/test/documentation`,{Code:e,Runtime:n,Name:i})).data}catch(s){throw this._handleError(s,"Failed to generate documentation from code")}}async generateDocumentationForFunction(e,n){try{let i=n?{MarkdownOverride:n}:{};return(await this.client.post(`/v1/projects/${this.projectId}/functions/${e}/documentation`,i)).data}catch(i){throw this._handleError(i,`Failed to generate documentation for function ${e}`)}}async getDocumentationForFunction(e){try{return(await this.client.get(`/v1/projects/${this.projectId}/functions/${e}/documentation`)).data}catch(n){throw this._handleError(n,`Failed to get documentation for function ${e}`)}}async streamDeployment(e,n,i){let s=c=>c.status?"status":c.progress?"progress":c.completed?"completed":c.failed||c.error?"failed":c.connected?"connected":"message",r=new URL(`v1/projects/${this.projectId}/functions/${e}/deployment/stream`,this.baseUrl).href,a={Accept:"text/event-stream","Cache-Control":"no-cache"};if(this.credentials)try{let c=await this.credentials.retrieve();if(c&&c.keyId&&c.keySecret){let u=Buffer.from(`${c.keyId}:${c.keySecret}`).toString("base64");a.Authorization=`Basic ${u}`}}catch(c){console.error("Failed to add authentication:",c)}let l=null;try{l=new AbortController;let c=await fetch(r,{method:"GET",headers:a,signal:l.signal});if(!c.ok)throw new Error(`SSE connection failed: ${c.status} ${c.statusText}`);if(!c.body)throw new Error("Response body is null");let u=c.body.getReader(),g=new TextDecoder,h="";return(async()=>{let w="";try{for(;;){let{done:A,value:J}=await u.read();if(A)break;let W=g.decode(J,{stream:!0});h+=W;let $=h.split(`
3
3
  `);h=$.pop()||"";for(let b of $){if(b.startsWith("event:")){w=b.substring(6).trim();continue}if(b.startsWith("data:")){let S=b.substring(5).trim();if(S)try{let y=JSON.parse(S),v=w||s(y);n({type:v,data:y}),w=""}catch{n({type:w||"message",data:S}),w=""}}b===""&&(w="")}}}catch(A){A.name!=="AbortError"&&i&&i(A)}})(),()=>{l&&l.abort()}}catch(c){throw console.error("Failed to create SSE stream:",c),i&&i(c),c}}async checkHealth(){try{return(await this.client.get("/health")).data}catch(e){throw this._handleError(e,"Health check failed")}}async getValidToken(){if(!this.credentials)return null;try{let e=await this.credentials.getOAuthToken();if(e)return{access_token:e,token_type:"Bearer",expires_in:3600};let n=await this.credentials.getBasicAuthToken();return n?{access_token:n,token_type:"Basic",expires_in:3600}:null}catch(e){return console.error(Ee.default.yellow("Failed to get authentication token:",e.message)),null}}async authenticateOAuth(e,n){try{let i=this.oauthUrl,s=new URLSearchParams;s.append("grant_type","client_credentials");let a=(await Me.default.post(i,s,{headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:"Basic "+Buffer.from(`${e}:${n}`).toString("base64")},timeout:this.timeout})).data;return this.credentials&&await this.credentials.storeOAuthToken(a),a}catch(i){throw this._handleError(i,"OAuth authentication failed")}}_handleError(e,n){if(Me.default.isAxiosError(e)){let i=e;if(i.response){let s=i.response.status,r=i.response.data,a=r?.message||r?.error||i.message;throw s===401?new Error('Authentication required. Please run "sinch auth login" first.'):s===403?new Error(`Permission denied: ${a}`):s===404?new Error(`Resource not found: ${a}`):s>=500?new Error(`Server error: ${a}`):new Error(`${n}: ${a}`)}else throw i.request?(console.error("DEBUG: Axios error details:",{code:i.code,message:i.message,url:i.config?.url,baseURL:i.config?.baseURL,method:i.config?.method}),i.code==="ECONNREFUSED"?new Error(`Cannot connect to API at ${this.baseUrl}. Is the server running?`):i.code==="ETIMEDOUT"?new Error(`Request timeout after ${this.timeout}ms`):new Error(`${n}: No response from server (${i.code||"unknown error"})`)):new Error(`${n}: ${i.message}`)}else throw new Error(`${n}: ${e.message||e}`)}}});function He(){if(!nt)try{nt=require("keytar")}catch{return null}return nt}var Tt,te,nt,Fe,K,We,jt=he(()=>{"use strict";Tt=m(require("os")),te=m(require("chalk")),nt=null;Fe="sinch-functions-cli",K=Tt.userInfo().username,We=class{config;keychainAvailable=null;constructor(e){this.config=e}async isKeychainAvailable(){if(this.keychainAvailable!==null)return this.keychainAvailable;if(this.config.get("keySecretPlaintext"))return this.keychainAvailable=!1,!1;try{let e=He();if(!e)return this.keychainAvailable=!1,!1;let n=`${K}-test-${Date.now()}`;return await e.setPassword(Fe,n,"test"),await e.deletePassword(Fe,n),this.keychainAvailable=!0,!0}catch{return this.keychainAvailable=!1,!1}}async setPassword(e,n){if(!await this.isKeychainAvailable())return!1;let s=He();return s?(await s.setPassword(Fe,e,n),!0):!1}async getPassword(e){if(!await this.isKeychainAvailable())return null;let i=He();return i?await i.getPassword(Fe,e):null}async deletePassword(e){if(!await this.isKeychainAvailable())return!1;try{let i=He();return i?(await i.deletePassword(Fe,e),!0):!1}catch{return!0}}async store(e){let{projectId:n,keyId:i,keySecret:s,applicationKey:r,applicationSecret:a}=e,l=await this.setPassword(`${K}-keySecret`,s),c=await this.setPassword(r,a);l&&c?(this.config.set("keySecretPlaintext",null),this.config.set("applicationSecretPlaintext",null)):(console.log(te.default.yellow(`
4
4
  \u26A0\uFE0F OS keychain not available (Docker/CI environment detected)`)),console.log(te.default.yellow("\u26A0\uFE0F Storing secrets in plaintext config file")),console.log(te.default.gray("Location: ~/.sinch/config.json")),console.log(te.default.gray(`
5
5
  For better security, export as environment variables instead:`)),console.log(te.default.cyan(' export SINCH_KEY_SECRET="'+s+'"')),console.log(te.default.cyan(' export SINCH_APPLICATION_SECRET="'+a+'"')),this.config.set("keySecretPlaintext",s),this.config.set("applicationSecretPlaintext",a)),this.config.set("projectId",n),this.config.set("keyId",i),this.config.set("defaultApplicationKey",r),this.config.set("credentialsStored",!0),await this.config.save()}async storeOAuthToken(e){let{access_token:n,refresh_token:i,expires_in:s,token_type:r}=e,a=new Date(Date.now()+s*1e3).toISOString(),l=await this.setPassword(`${K}-oauth-access`,n),c=!0;i&&(c=await this.setPassword(`${K}-oauth-refresh`,i)),this.config.set("oauthTokenType",r),this.config.set("oauthExpiresAt",a),this.config.set("oauthStored",l&&c),await this.config.save()}async reAuthenticateOAuth(){try{let e=await this.retrieve();if(!e)return null;let{SinchAPI:n}=(x(),F(Ie)),s=await new n({apiUrl:this.config.get("apiUrl")||"https://api.functions.dev.sinchvoice.org",projectId:e.projectId,credentials:null}).authenticateOAuth(e.keyId,e.keySecret);return await this.storeOAuthToken(s),s.access_token}catch{return null}}async getOAuthToken(){if(!this.config.get("oauthStored"))return await this.reAuthenticateOAuth();let n=this.config.get("oauthExpiresAt");if(n&&new Date(n)<new Date){console.log(te.default.gray("Token expired, re-authenticating..."));let i=await this.reAuthenticateOAuth();return i?console.log(te.default.green("\u2713 Token renewed successfully")):console.warn(te.default.yellow("Failed to renew OAuth token. Please login again.")),i}return await this.getPassword(`${K}-oauth-access`)}async storeApplicationSecret(e,n){await this.setPassword(e,n),this.config.get("defaultApplicationKey")||(this.config.set("defaultApplicationKey",e),await this.config.save())}async retrieve(){let e=process.env.SINCH_KEY_ID,n=process.env.SINCH_KEY_SECRET,i=process.env.SINCH_APPLICATION_KEY,s=process.env.SINCH_APPLICATION_SECRET;if(e&&n&&i&&s)return{projectId:process.env.SINCH_PROJECT_ID||this.config.get("projectId")||"",keyId:e,keySecret:n,applicationKey:i,applicationSecret:s};if(!this.config.get("credentialsStored"))return null;let a=this.config.get("projectId"),l=this.config.get("keyId"),c=this.config.get("defaultApplicationKey"),u=await this.getPassword(`${K}-keySecret`),g=c?await this.getPassword(c):null;return(!u||!g)&&(u=u||n||null,g=g||s||null),(!u||!g)&&(u=u||this.config.get("keySecretPlaintext"),g=g||this.config.get("applicationSecretPlaintext")),!u||!g?null:{projectId:a,keyId:l,keySecret:u,applicationKey:c,applicationSecret:g}}async getApplicationCredentials(e=null){if(e||(e=this.config.get("defaultApplicationKey")),!e)return null;let n=await this.getPassword(e);return n||(n=process.env.SINCH_APPLICATION_SECRET||null),n||(n=this.config.get("applicationSecretPlaintext")),n?{applicationKey:e,applicationSecret:n}:null}async clear(){let e=this.config.get("defaultApplicationKey");await this.deletePassword(`${K}-keySecret`),await this.deletePassword(`${K}-oauth-access`),await this.deletePassword(`${K}-oauth-refresh`),e&&await this.deletePassword(e),this.config.set("projectId",null),this.config.set("keyId",null),this.config.set("defaultApplicationKey",null),this.config.set("credentialsStored",!1),this.config.set("oauthStored",!1),this.config.set("oauthTokenType",null),this.config.set("oauthExpiresAt",null),this.config.set("keySecretPlaintext",null),this.config.set("applicationSecretPlaintext",null),await this.config.save()}async hasCredentials(){if(!!(process.env.SINCH_KEY_ID&&process.env.SINCH_KEY_SECRET&&process.env.SINCH_APPLICATION_KEY&&process.env.SINCH_APPLICATION_SECRET))return!0;if(!this.config.get("credentialsStored"))return!1;if(this.config.get("keySecretPlaintext"))return!0;let i=await this.getPassword(`${K}-keySecret`),s=this.config.get("defaultApplicationKey"),r=s?await this.getPassword(s):null;return!!(i&&r)}async hasOAuthToken(){return this.config.get("oauthStored")?!!await this.getPassword(`${K}-oauth-access`):!1}getPublicInfo(){return{projectId:this.config.get("projectId"),keyId:this.config.get("keyId"),applicationKey:this.config.get("defaultApplicationKey"),hasCredentials:this.config.get("credentialsStored",!1),hasOAuth:this.config.get("oauthStored",!1),oauthExpiresAt:this.config.get("oauthExpiresAt")}}async createSinchClient(e=null){let n=await this.getApplicationCredentials(e);if(!n)throw new Error('No credentials found. Please run "sinch auth login" first.');let{SinchClient:i}=require("@sinch/sdk-core");return new i({applicationKey:n.applicationKey,applicationSecret:n.applicationSecret,projectId:this.config.get("projectId")})}async storeBasicAuthToken(e){await this.setPassword(`${K}-basic-auth`,e)&&(this.config.set("basicAuthStored",!0),await this.config.save())}async getBasicAuthToken(){return this.config.get("basicAuthStored")?await this.getPassword(`${K}-basic-auth`):null}async clearBasicAuthToken(){await this.deletePassword(`${K}-basic-auth`),this.config.set("basicAuthStored",!1),await this.config.save()}}});var we={};Je(we,{Config:()=>Ge,config:()=>p});var G,Oe,Dt,ot,Ge,p,j=he(()=>{"use strict";G=m(require("fs-extra")),Oe=m(require("path")),Dt=m(require("os")),ot=m(require("chalk"));jt();Ge=class{configDir;configFile;projectConfigFile;_config=null;_projectConfig=null;_credentials=null;constructor(){this.configDir=Oe.join(Dt.homedir(),".sinch"),this.configFile=Oe.join(this.configDir,"config.json"),this.projectConfigFile=Oe.join(process.cwd(),"sinch.json")}async load(){try{await G.ensureDir(this.configDir),await G.pathExists(this.configFile)?this._config=await G.readJson(this.configFile):(this._config=this._getDefaultConfig(),await this.save())}catch(e){console.warn(ot.default.yellow(`Warning: Could not load config: ${e.message}`)),this._config=this._getDefaultConfig()}this._credentials=new We(this);try{await G.pathExists(this.projectConfigFile)&&(this._projectConfig=await G.readJson(this.projectConfigFile))}catch(e){console.warn(ot.default.yellow(`Warning: Could not load project config: ${e.message}`))}}async save(){try{await G.ensureDir(this.configDir),await G.writeJson(this.configFile,this._config,{spaces:2})}catch(e){throw new Error(`Could not save config: ${e.message}`)}}async saveProjectConfig(){try{if(this._projectConfig){let e=Oe.join(process.cwd(),"sinch.json");await G.writeJson(e,this._projectConfig,{spaces:2})}}catch(e){throw new Error(`Could not save project config: ${e.message}`)}}get(e,n=null){if(!this._config)throw new Error("Config not loaded. Call load() first.");return this._projectConfig&&this._projectConfig[e]!==void 0?this._projectConfig[e]:this._config[e]!==void 0?this._config[e]:n}set(e,n,i=!1){if(!this._config)throw new Error("Config not loaded. Call load() first.");i?(this._projectConfig||(this._projectConfig={}),this._projectConfig[e]=n):this._config[e]=n}getApiConfig(){return{apiUrl:this.get("apiUrl"),oauthUrl:this.get("oauthUrl","https://auth.sinch.com/oauth2/token"),projectId:this.get("projectId"),credentials:this._credentials}}async initProject(e,n="node",i={},s=null){let r={name:e,runtime:n,description:`Sinch Functions project: ${e}`,created:new Date().toISOString()};return i&&Object.keys(i).length>0&&(r.variables=i),s&&(r.applicationKey=s),this._projectConfig=r,await this.saveProjectConfig(),r}isInProject(){return this._projectConfig!==null}getProjectConfig(){return this._projectConfig}_getDefaultConfig(){return{apiUrl:"https://functions.api.sinch.com",oauthUrl:"https://auth.sinch.com/oauth2/token",projectId:"default-project",created:new Date().toISOString()}}async storeCredentials(e){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return await this._credentials.store(e)}async getCredentials(){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return await this._credentials.retrieve()}async clearCredentials(){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return await this._credentials.clear()}async hasCredentials(){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return await this._credentials.hasCredentials()}getPublicCredentialInfo(){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return this._credentials.getPublicInfo()}async createSinchClient(e=null){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return!e&&this.isInProject()&&(e=this._projectConfig?.applicationKey||null),await this._credentials.createSinchClient(e)}async getApplicationCredentials(e=null){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return!e&&this.isInProject()&&(e=this._projectConfig?.applicationKey||null),await this._credentials.getApplicationCredentials(e)}async storeApplicationSecret(e,n){if(!this._credentials)throw new Error("Config not loaded. Call load() first.");return await this._credentials.storeApplicationSecret(e,n)}getTunnelPreference(){return this._projectConfig&&this._projectConfig.tunnel?.preference||null}async setTunnelPreference(e){if(!this._projectConfig)throw new Error("Not in a project directory");this._projectConfig.tunnel||(this._projectConfig.tunnel={}),this._projectConfig.tunnel.preference=e,this._projectConfig.tunnel.lastUpdated=new Date().toISOString(),await this.saveProjectConfig()}getTunnelSubdomain(){return this._projectConfig&&this._projectConfig.tunnel?.subdomain||null}async setTunnelSubdomain(e){if(!this._projectConfig)throw new Error("Not in a project directory");this._projectConfig.tunnel||(this._projectConfig.tunnel={}),this._projectConfig.tunnel.subdomain=e,this._projectConfig.tunnel.lastUpdated=new Date().toISOString(),await this.saveProjectConfig()}getTunnelUrl(){return this._projectConfig&&this._projectConfig.tunnel?.lastUrl||null}async setTunnelUrl(e){if(!this._projectConfig)throw new Error("Not in a project directory");this._projectConfig.tunnel||(this._projectConfig.tunnel={}),this._projectConfig.tunnel.lastUrl=e,this._projectConfig.tunnel.lastUpdated=new Date().toISOString(),await this.saveProjectConfig()}},p=new Ge});var Y,ce,it,Ft,st,t,B=he(()=>{"use strict";Y=m(require("chalk")),ce=m(require("fs")),it=m(require("path")),Ft=m(require("os")),st=class{verbose;logFile;constructor(e={}){this.verbose=e.verbose||process.env.VERBOSE==="true";let n="cli";try{let i=it.join(process.cwd(),"sinch.json");ce.existsSync(i)&&(n=JSON.parse(ce.readFileSync(i,"utf8")).name||"cli")}catch{}this.logFile=it.join(Ft.tmpdir(),`sinch-${n}.log`);try{ce.writeFileSync(this.logFile,`=== Sinch CLI Debug Log - ${new Date().toISOString()} ===
@@ -34,7 +34,7 @@ ${e}`))}list(e,n={}){let{indent:i=" ",bullet:s="\u2022"}=n;e.forEach(r=>{consol
34
34
  \u{1F4A1} To download this function, run:`),t.info(` ${E.default.cyan(`sinch functions download ${o.id}`)}`);break;case"status":t.info(`
35
35
  \u{1F4A1} To view status, run:`),t.info(` ${E.default.cyan(`sinch functions status ${o.id}`)}`);break;case"logs":t.info(`
36
36
  \u{1F4A1} To view logs, run:`),t.info(` ${E.default.cyan(`sinch functions logs ${o.id}`)}`);break;case"copy-url":o.containerAppUrl?(await require("clipboardy").write(o.containerAppUrl),t.success(`\u2705 URL copied to clipboard: ${o.containerAppUrl}`)):t.warn("\u26A0\uFE0F Function has no URL (not deployed)");break;case"delete":t.info(`
37
- \u{1F4A1} To delete this function, run:`),t.info(` ${E.default.cyan(`sinch functions delete ${o.id}`)}`);break;case"back":return}}Jt.exports=Vt});async function lt(o){if(!await p.hasCredentials())throw new Error('No Sinch credentials found. Run "sinch auth login" first.');let e=await p.getApplicationCredentials();if(!e)throw new Error('No application credentials found. Run "sinch auth login" first.');let i=`https://callingapi.sinch.com/v1/configuration/callbacks/applications/${e.applicationKey}/`,s={url:{primary:o}},r=`${e.applicationKey}:${e.applicationSecret}`,a=Buffer.from(r).toString("base64");await ct.default.post(i,s,{headers:{Authorization:`Basic ${a}`,"Content-Type":"application/json"},timeout:1e4}),await new Promise(g=>setTimeout(g,1e3));let u=(await ct.default.get(i,{headers:{Authorization:`Basic ${a}`},timeout:1e4})).data?.url?.primary;if(u!==o)throw t.debug(`Callback verification mismatch. Expected: ${o}, Got: ${u}`),new Error(`Callback URL update verification failed. Expected: ${o}, Got: ${u}`);t.debug("Voice callback URL successfully updated and verified")}var ct,Ht=he(()=>{"use strict";ct=m(require("axios"));j();B()});var zt={};Je(zt,{combineDocumentation:()=>Gt,detectRuntimeFromFile:()=>Yt,findFunctionFile:()=>Wt,generateDocsFromLocal:()=>ut,saveDocumentationToFile:()=>dt});async function ut(){let o=process.cwd(),e=await Wt(o);if(!e)throw new Error("No function file found. Make sure you're in a function directory with function.js, index.js, or handler.js");let n=await Te.readFile(e,"utf8"),i=Yt(e),s=ue.basename(o),a=await new I(p.getApiConfig()).generateDocumentationFromCode(n,i,s);return Gt(a)}async function Wt(o){let e=["function.js","index.js","handler.js","function.py","handler.py","Function.cs","Handler.cs"];for(let n of e){let i=ue.join(o,n);if(await Te.pathExists(i))return i}return null}function Gt(o){let e=o.documentation||"",n=o.diagram||o.callFlowDiagram;return n&&(e&&!e.endsWith(`
37
+ \u{1F4A1} To delete this function, run:`),t.info(` ${E.default.cyan(`sinch functions delete ${o.id}`)}`);break;case"back":return}}Jt.exports=Vt});async function lt(o){if(!await p.hasCredentials())throw new Error('No Sinch credentials found. Run "sinch auth login" first.');let e=await p.getApplicationCredentials();if(!e)throw new Error('No application credentials found. Run "sinch auth login" first.');let i=`https://callingapi.sinch.com/v1/configuration/callbacks/applications/${e.applicationKey}/`,s={url:{primary:o}},r=`${e.applicationKey}:${e.applicationSecret}`,a=Buffer.from(r).toString("base64");await ct.default.post(i,s,{headers:{Authorization:`Basic ${a}`,"Content-Type":"application/json"},timeout:1e4}),await new Promise(g=>setTimeout(g,1e3));let u=(await ct.default.get(i,{headers:{Authorization:`Basic ${a}`},timeout:1e4})).data?.url?.primary;if(u!==o)throw t.debug(`Callback verification mismatch. Expected: ${o}, Got: ${u}`),new Error(`Callback URL update verification failed. Expected: ${o}, Got: ${u}`);t.debug("Voice callback URL successfully updated and verified")}var ct,Ht=he(()=>{"use strict";ct=m(require("axios"));j();B()});var zt={};Je(zt,{combineDocumentation:()=>Gt,detectRuntimeFromFile:()=>Yt,findFunctionFile:()=>Wt,generateDocsFromLocal:()=>ut,saveDocumentationToFile:()=>dt});async function ut(){let o=process.cwd(),e=await Wt(o);if(!e)throw new Error("No function file found. Make sure you're in a function directory with function.js, function.ts, index.js, index.ts, handler.js, or handler.ts");let n=await Te.readFile(e,"utf8"),i=Yt(e),s=ue.basename(o),a=await new I(p.getApiConfig()).generateDocumentationFromCode(n,i,s);return Gt(a)}async function Wt(o){let e=["function.js","function.ts","index.js","index.ts","handler.js","handler.ts","function.py","handler.py","Function.cs","Handler.cs"];for(let n of e){let i=ue.join(o,n);if(await Te.pathExists(i))return i}return null}function Gt(o){let e=o.documentation||"",n=o.diagram||o.callFlowDiagram;return n&&(e&&!e.endsWith(`
38
38
 
39
39
  `)&&(e+=`
40
40
 
@@ -44,7 +44,7 @@ ${e}`))}list(e,n={}){let{indent:i=" ",bullet:s="\u2022"}=n;e.forEach(r=>{consol
44
44
  ---
45
45
 
46
46
  `,e+=`*Documentation generated on ${new Date(o.generatedAt).toLocaleString()} using Sinch AI*
47
- `),e}async function dt(o,e){let n=ue.resolve(e);await Te.writeFile(n,o,"utf8")}function Yt(o){switch(ue.extname(o).toLowerCase()){case".js":return"node";case".py":return"python";case".cs":return"csharp";default:return"node"}}var Te,ue,pt=he(()=>{"use strict";Te=m(require("fs-extra")),ue=m(require("path"));x();j()});var sn=U((Xi,on)=>{"use strict";var ne=m(require("fs-extra")),be=m(require("path")),k=m(require("chalk")),Zt=require("commander");x();j();B();O();Ht();var Xt=m(require("adm-zip")),en=m(require("os")),ft=m(require("inquirer")),Le=require("child_process"),tn=m(require("keytar")),nn=new Zt.Command("deploy");nn.description("Deploy function to production").option("-n, --name <name>","Function name (default: current directory)").option("--no-wait","Don't wait for deployment to complete").option("--non-interactive","Use default values without prompting (auto-generates docs)").option("--no-docs","no doc generation").option("--private","Deploy to private namespace (internal access only, not publicly accessible)").action(async o=>{try{if(await p.load(),!p.isInProject())throw new Error('Not in a Sinch Functions project directory. Run "sinch functions init" first.');let e=p.getProjectConfig(),n=o.name||e.name,i=e.runtime;await bo(e,o),i==="csharp"&&await $o(process.cwd()),d.start("\u{1F4E6} Packaging function...");let s=await yo(n);d.succeed("\u{1F4E6} Function packaged"),d.start("\u{1F527} Preparing configuration...");let r=await wo(i,n);d.succeed("\u{1F527} Configuration prepared");let a=await vo(e,o);d.start("\u{1F680} Submitting deployment...");let l=new I(p.getApiConfig()),c=await ne.default.readFile(s),u=await l.deployFunction(n,i,c,r,a);if(await ne.default.remove(s),d.succeed("\u{1F680} Deployment submitted"),p.set("functionId",u.id,!0),await p.saveProjectConfig(),t.newline(),t.info(`Function ID: ${k.default.cyan(u.id)}`),t.info(`Status: ${gt(u.status)}`),o.wait===!1){t.newline(),t.info("\u{1F4A1} Check deployment progress with:"),t.info(` ${k.default.cyan(`sinch functions status ${u.id}`)}`);return}await Co(l,u.id)}catch(e){d.stop(),t.error(`Failed to deploy function: ${e.message}`),process.exit(1)}});async function yo(o){let e=new Xt.default,n=process.cwd(),i=be.default.join(en.default.tmpdir(),`${o}-${Date.now()}.zip`),s=["node_modules","bin","obj",".git",".DS_Store","*.zip",".env",".env.local","npm-debug.log*",".sinch-tmp","examples","claude.md","readme.md",".vs",".vscode",".idea",".cursor","*.user","*.suo"];async function r(a,l=""){let c=await ne.default.readdir(a);for(let u of c){let g=be.default.join(a,u),h=be.default.join(l,u).replace(/\\/g,"/");if(s.some(w=>u.match(new RegExp(w.replace("*",".*")))))continue;if((await ne.default.stat(g)).isDirectory())await r(g,h);else{let w=await ne.default.readFile(g);e.addFile(h,w)}}}return await r(n),e.writeZip(i),i}async function wo(o,e){let n={},i=null;try{i=await p.getCredentials()}catch(s){t.debug("Could not load credentials from secure storage: "+s.message)}if(o==="csharp"){let s=be.default.join(process.cwd(),"appsettings.json");if(await ne.default.pathExists(s))try{let r=await ne.default.readJson(s);for(let a of Object.keys(r)){if(a==="Logging"||a==="AllowedHosts")continue;let l=r[a];if(l===""||l===null||l===void 0){let c="",u=!1;try{let h=(0,Le.execSync)("dotnet user-secrets list",{cwd:process.cwd(),encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim().split(`
47
+ `),e}async function dt(o,e){let n=ue.resolve(e);await Te.writeFile(n,o,"utf8")}function Yt(o){switch(ue.extname(o).toLowerCase()){case".js":case".ts":return"node";case".py":return"python";case".cs":return"csharp";default:return"node"}}var Te,ue,pt=he(()=>{"use strict";Te=m(require("fs-extra")),ue=m(require("path"));x();j()});var sn=U((Xi,on)=>{"use strict";var ne=m(require("fs-extra")),be=m(require("path")),k=m(require("chalk")),Zt=require("commander");x();j();B();O();Ht();var Xt=m(require("adm-zip")),en=m(require("os")),ft=m(require("inquirer")),Le=require("child_process"),tn=m(require("keytar")),nn=new Zt.Command("deploy");nn.description("Deploy function to production").option("-n, --name <name>","Function name (default: current directory)").option("--no-wait","Don't wait for deployment to complete").option("--non-interactive","Use default values without prompting (auto-generates docs)").option("--no-docs","no doc generation").option("--private","Deploy to private namespace (internal access only, not publicly accessible)").action(async o=>{try{if(await p.load(),!p.isInProject())throw new Error('Not in a Sinch Functions project directory. Run "sinch functions init" first.');let e=p.getProjectConfig(),n=o.name||e.name,i=e.runtime;await bo(e,o),i==="csharp"&&await $o(process.cwd()),d.start("\u{1F4E6} Packaging function...");let s=await yo(n);d.succeed("\u{1F4E6} Function packaged"),d.start("\u{1F527} Preparing configuration...");let r=await wo(i,n);d.succeed("\u{1F527} Configuration prepared");let a=await vo(e,o);d.start("\u{1F680} Submitting deployment...");let l=new I(p.getApiConfig()),c=await ne.default.readFile(s),u=await l.deployFunction(n,i,c,r,a);if(await ne.default.remove(s),d.succeed("\u{1F680} Deployment submitted"),p.set("functionId",u.id,!0),await p.saveProjectConfig(),t.newline(),t.info(`Function ID: ${k.default.cyan(u.id)}`),t.info(`Status: ${gt(u.status)}`),o.wait===!1){t.newline(),t.info("\u{1F4A1} Check deployment progress with:"),t.info(` ${k.default.cyan(`sinch functions status ${u.id}`)}`);return}await Co(l,u.id)}catch(e){d.stop(),t.error(`Failed to deploy function: ${e.message}`),process.exit(1)}});async function yo(o){let e=new Xt.default,n=process.cwd(),i=be.default.join(en.default.tmpdir(),`${o}-${Date.now()}.zip`),s=["node_modules","bin","obj",".git",".DS_Store","*.zip",".env",".env.local","npm-debug.log*",".sinch-tmp","examples","claude.md","readme.md",".vs",".vscode",".idea",".cursor","*.user","*.suo"];async function r(a,l=""){let c=await ne.default.readdir(a);for(let u of c){let g=be.default.join(a,u),h=be.default.join(l,u).replace(/\\/g,"/");if(s.some(w=>u.match(new RegExp(w.replace("*",".*")))))continue;if((await ne.default.stat(g)).isDirectory())await r(g,h);else{let w=await ne.default.readFile(g);e.addFile(h,w)}}}return await r(n),e.writeZip(i),i}async function wo(o,e){let n={},i=null;try{i=await p.getCredentials()}catch(s){t.debug("Could not load credentials from secure storage: "+s.message)}if(o==="csharp"){let s=be.default.join(process.cwd(),"appsettings.json");if(await ne.default.pathExists(s))try{let r=await ne.default.readJson(s);for(let a of Object.keys(r)){if(a==="Logging"||a==="AllowedHosts")continue;let l=r[a];if(l===""||l===null||l===void 0){let c="",u=!1;try{let h=(0,Le.execSync)("dotnet user-secrets list",{cwd:process.cwd(),encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim().split(`
48
48
  `);for(let C of h){let w=C.match(/^([^=]+)\s*=\s*(.+)$/);if(w&&w[1]&&w[2]&&w[1].trim()===a){c=w[2].trim(),u=!0,t.debug(`Found ${a} in dotnet user-secrets`);break}}}catch{}!c&&i&&(a==="PROJECT_ID"&&i.projectId?c=i.projectId:a==="PROJECT_ID_API_KEY"&&i.keyId?c=i.keyId:a==="PROJECT_ID_API_SECRET"&&i.keySecret?(c=i.keySecret,u=!0):a==="VOICE_APPLICATION_KEY"&&i.applicationKey?c=i.applicationKey:a==="VOICE_APPLICATION_SECRET"&&i.applicationSecret&&(c=i.applicationSecret,u=!0)),c&&(n[a]={Value:c,Secret:u})}else n[a]={Value:String(l),Secret:!1}}}catch(r){t.debug("Could not read appsettings.json: "+r.message)}}else{let s=be.default.join(process.cwd(),".env");if(await ne.default.pathExists(s))try{let r=await ne.default.readFile(s,"utf8"),a={};r.split(`
49
49
  `).forEach(c=>{if(c.trim()&&!c.trim().startsWith("#")){let[u,...g]=c.split("=");u&&(a[u.trim()]=g.join("=").trim())}});let l=["PORT","NODE_ENV","SINCH_TUNNEL"];for(let[c,u]of Object.entries(a))if(!l.includes(c))if(u===""||u===null||u===void 0){let g="",h=!1;if(i&&(c==="PROJECT_ID"&&i.projectId?g=i.projectId:c==="PROJECT_ID_API_KEY"&&i.keyId?g=i.keyId:c==="PROJECT_ID_API_SECRET"&&i.keySecret?(g=i.keySecret,h=!0):c==="VOICE_APPLICATION_KEY"&&i.applicationKey?g=i.applicationKey:c==="VOICE_APPLICATION_SECRET"&&i.applicationSecret&&(g=i.applicationSecret,h=!0)),!g)try{let C="sinch-functions-cli",w=`${e}-${c}`;g=await tn.default.getPassword(C,w)||"",g&&(h=!0,t.debug(`Loaded custom secret ${c} from keychain`))}catch(C){t.debug(`Could not load ${c} from keychain: ${C.message}`)}g&&(n[c]={Value:g,Secret:h})}else n[c]={Value:u,Secret:!1}}catch(r){t.debug("Could not read .env file: "+r.message)}}return n}async function Co(o,e){let i=Date.now(),s=!1,r;return t.newline(),d.start("\u23F3 Connecting to deployment stream..."),new Promise((a,l)=>{let c,u;o.streamDeployment(e,g=>{let h=Math.floor((Date.now()-i)/1e3);switch(g.type){case"connected":d.text="\u23F3 Deployment stream connected, waiting for updates...";break;case"status":let C=g.data.data.message;d.text=`\u23F3 ${C} (${h}s)`;break;case"progress":let w=g.data.data.message,A=g.data.data.percentComplete;A?d.text=`\u23F3 ${w} (${A}%, ${h}s)`:d.text=`\u23F3 ${w} (${h}s)`;break;case"completed":s=!0,r=g.data.data.url,d.succeed("\u2705 Function deployed successfully"),o.getFunction(e).then(async W=>{if(t.newline(),t.info("\u{1F389} Deployment Complete!"),t.info(` Function: ${k.default.cyan(W.name)}`),t.info(` Status: ${gt("Running")}`),t.info(` URL: ${k.default.blue(r)}`),r){t.newline(),t.info("\u{1F4DE} Voice Integration:");try{d.start("Updating Sinch Voice callback URL..."),await lt(r),d.succeed("Voice callback URL updated automatically"),t.info(` Callback URL set to: ${k.default.cyan(r)}`)}catch($){d.fail("Failed to update callback URL automatically"),t.warn(` Error: ${$.message}`),t.info(` Manual setup: ${k.default.cyan(`sinch voice callback-url ${r}`)}`)}t.info(` Test function: ${k.default.cyan(`curl ${r}`)}`)}t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 View logs: ${k.default.cyan(`sinch functions logs ${e} --follow`)}`),t.info(` \u2022 Check status: ${k.default.cyan(`sinch functions status ${e}`)}`),c&&c(),u&&clearTimeout(u),a()}).catch(W=>{t.error(`Failed to get function details: ${W.message}`),c&&c(),u&&clearTimeout(u),a()});break;case"failed":s=!0;let J=g.data.data.errorMessage;d.fail("\u274C Deployment failed"),t.newline(),t.error("Deployment failed:"),J&&t.error(` ${J}`),t.newline(),t.info("\u{1F4A1} Troubleshooting:"),t.info(` \u2022 Check logs: ${k.default.cyan(`sinch functions logs ${e}`)}`),t.info(` \u2022 Check status: ${k.default.cyan(`sinch functions status ${e}`)}`),c&&c(),u&&clearTimeout(u),process.exit(1);break}},g=>{t.debug("SSE stream error, falling back to polling:",g),d.text="\u23F3 Deployment stream unavailable, checking status...",c&&c(),Qt(o,e,i,3e5).then(a).catch(l)}).then(g=>{t.debug("SSE stream setup successful, cleanup function received"),c=g,u=setTimeout(()=>{s||(d.fail("\u23F0 Deployment timeout"),t.newline(),t.warn("Deployment is taking longer than expected"),t.info("\u{1F4A1} Check deployment progress with:"),t.info(` ${k.default.cyan(`sinch functions status ${e}`)}`),c&&c(),a())},3e5)}).catch(g=>{t.debug(`Failed to set up SSE stream, falling back to polling: ${g.message}`),Qt(o,e,i,3e5).then(a).catch(l)})})}async function Qt(o,e,n,i){for(;Date.now()-n<i;){try{let a=(await o.getFunctionStatus(e)).function;if(a.status==="Running"){if(d.succeed("\u2705 Function deployed successfully"),t.newline(),t.info("\u{1F389} Deployment Complete!"),t.info(` Function: ${k.default.cyan(a.name)}`),t.info(` Status: ${gt(a.status)}`),t.info(` URL: ${k.default.blue(a.containerAppUrl)}`),a.containerAppUrl){t.newline(),t.info("\u{1F4DE} Voice Integration:");try{d.start("Updating Sinch Voice callback URL..."),await lt(a.containerAppUrl),d.succeed("Voice callback URL updated automatically"),t.info(` Callback URL set to: ${k.default.cyan(a.containerAppUrl)}`)}catch(c){d.fail("Failed to update callback URL automatically"),t.warn(` Error: ${c.message}`),t.info(` Manual setup: ${k.default.cyan(`sinch voice callback-url ${a.containerAppUrl}`)}`)}t.info(` Test function: ${k.default.cyan(`curl ${a.containerAppUrl}`)}`)}t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 View logs: ${k.default.cyan(`sinch functions logs ${e} --follow`)}`),t.info(` \u2022 Check status: ${k.default.cyan(`sinch functions status ${e}`)}`);return}a.status==="Failed"&&(d.fail("\u274C Deployment failed"),t.newline(),t.error("Deployment failed:"),a.errorMessage&&t.error(` ${a.errorMessage}`),t.newline(),t.info("\u{1F4A1} Troubleshooting:"),t.info(` \u2022 Check logs: ${k.default.cyan(`sinch functions logs ${e}`)}`),t.info(` \u2022 Check status: ${k.default.cyan(`sinch functions status ${e}`)}`),process.exit(1));let l=Math.floor((Date.now()-n)/1e3);d.text=`\u23F3 Deploying function... (${a.status}, ${l}s)`}catch(r){t.debug(`Status check failed: ${r.message}`)}await new Promise(r=>setTimeout(r,3e3))}d.fail("\u23F0 Deployment timeout"),t.newline(),t.warn("Deployment is taking longer than expected"),t.info("\u{1F4A1} Check deployment progress with:"),t.info(` ${k.default.cyan(`sinch functions status ${e}`)}`)}function gt(o){return{Running:k.default.green("\u2705 Running"),Failed:k.default.red("\u274C Failed"),Pending:k.default.yellow("\u23F3 Pending"),Building:k.default.blue("\u{1F528} Building")}[o]||k.default.gray(o)}async function bo(o,e){let n=!1;if(e.docs===!1){t.info("Skipping documentation generation (--no-docs flag)");return}if(e.nonInteractive)t.info("Generating documentation (non-interactive mode)"),n=!0;else{if(o.docsPreference==="never")return;if(So(o))n=!0;else{let i=await ft.default.prompt([{type:"list",name:"docsChoice",message:"Generate/update documentation before deployment?",choices:[{name:"Yes - Generate documentation this time",value:"yes"},{name:"No - Skip documentation this time",value:"no"},{name:"Always - Generate documentation and remember preference",value:"always"},{name:"Never - Don't generate documentation and remember preference",value:"never"}],default:"yes"}]);i.docsChoice==="yes"?n=!0:i.docsChoice==="no"?n=!1:i.docsChoice==="always"?(n=!0,p.set("docsPreference","always",!0),await p.saveProjectConfig()):i.docsChoice==="never"&&(n=!1,p.set("docsPreference","never",!0),await p.saveProjectConfig())}}if(n)try{d.start("\u{1F4DD} Generating documentation...");let{generateDocsFromLocal:i,saveDocumentationToFile:s}=(pt(),F(zt)),r=await i(),a=be.default.basename(process.cwd());await s(r,"README.md",a),d.succeed("\u{1F4DD} Documentation updated in README.md")}catch(i){d.warn(`\u26A0\uFE0F Failed to generate documentation: ${i.message}`),t.warn("Continuing with deployment...")}}function So(o){return o.docsPreference==="always"}async function vo(o,e){if(e.private||o.accessPreference==="always-private")return"private";if(o.accessPreference==="always-public"||e.nonInteractive)return"public";let i=(await ft.default.prompt([{type:"list",name:"accessChoice",message:"Deployment access level?",choices:[{name:"Public - Accessible from the internet",value:"public"},{name:"Private - Internal access only (Sinch services)",value:"private"},{name:"Always Public - Remember this choice",value:"always-public"},{name:"Always Private - Remember this choice",value:"always-private"}],default:"public"}])).accessChoice;return i==="always-public"?(p.set("accessPreference","always-public",!0),await p.saveProjectConfig(),"public"):i==="always-private"?(p.set("accessPreference","always-private",!0),await p.saveProjectConfig(),"private"):i}async function $o(o){d.start("Building function locally...");try{await new Promise((i,s)=>{let r=(0,Le.spawn)("dotnet",["build","-c","Release"],{cwd:o,stdio:"pipe",shell:!0}),a="",l="";r.stdout?.on("data",c=>{l+=c}),r.stderr?.on("data",c=>{a+=c}),r.on("close",c=>{c!==0?s(new Error(`Build failed:
50
50
  ${a||l}`)):i()})}),d.succeed("Build successful")}catch(i){throw d.fail("Build failed"),i}d.start("Starting function for validation...");let e=null,n="";try{if(e=(0,Le.spawn)("dotnet",["run","--no-build","-c","Release"],{cwd:o,stdio:"pipe",shell:!0}),e.stdout?.on("data",s=>{n+=s.toString()}),e.stderr?.on("data",s=>{n+=s.toString()}),await Po("http://localhost:3000/health",3e4))d.succeed("Function started and health check passed");else throw d.fail("Function failed to start within timeout"),n&&console.error(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinch/cli",
3
- "version": "0.1.20-beta.0",
3
+ "version": "0.1.21-beta.0",
4
4
  "description": "Official Sinch CLI - Manage all Sinch products from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {