@sinch/cli 0.1.12 → 0.1.13-dev.2141993141
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/README.md +44 -0
- package/dist/index.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,6 +198,50 @@ DEBUG_HTTP=1 sinch functions deploy
|
|
|
198
198
|
|
|
199
199
|
Contributions are welcome! Visit our [GitLab repository](https://gitlab.com/sinch/sinch-projects/voice/functions/sinch-cli) for development setup and contribution guidelines.
|
|
200
200
|
|
|
201
|
+
### Release Process
|
|
202
|
+
|
|
203
|
+
To publish a new version of the CLI:
|
|
204
|
+
|
|
205
|
+
#### 1. Production Release
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Update version in package.json and create git tag
|
|
209
|
+
npm version patch # 0.1.0 → 0.1.1 (bug fixes)
|
|
210
|
+
npm version minor # 0.1.1 → 0.2.0 (new features)
|
|
211
|
+
npm version major # 0.2.0 → 1.0.0 (breaking changes)
|
|
212
|
+
|
|
213
|
+
# Or specify exact version
|
|
214
|
+
npm version 1.0.0
|
|
215
|
+
|
|
216
|
+
# Push commits and tags
|
|
217
|
+
git push
|
|
218
|
+
git push --tags
|
|
219
|
+
|
|
220
|
+
# CI will automatically build and publish to npm @latest
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### 2. Beta Release
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Create next beta version (recommended)
|
|
227
|
+
npm version prepatch --preid=beta # 0.1.13 → 0.1.14-beta.0
|
|
228
|
+
|
|
229
|
+
# Or increment existing beta
|
|
230
|
+
npm version prerelease --preid=beta # 0.1.14-beta.0 → 0.1.14-beta.1
|
|
231
|
+
|
|
232
|
+
# Push commits and tags
|
|
233
|
+
git push
|
|
234
|
+
git push --tags
|
|
235
|
+
|
|
236
|
+
# CI will automatically publish to npm @beta
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### 3. Dev Releases (Automatic)
|
|
240
|
+
|
|
241
|
+
Dev releases are **automatically published** on every commit to `main` with version `X.Y.Z-dev.{PIPELINE_ID}` and tagged as `@dev` on npm.
|
|
242
|
+
|
|
243
|
+
**Note**: Always run `npm version` before creating tags. The CI pipeline verifies that package.json version matches the git tag and will fail if they don't match.
|
|
244
|
+
|
|
201
245
|
## License
|
|
202
246
|
|
|
203
247
|
MIT License - Copyright © 2025 Sinch
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var Wn=Object.create;var Ue=Object.defineProperty;var Gn=Object.getOwnPropertyDescriptor;var Yn=Object.getOwnPropertyNames;var zn=Object.getPrototypeOf,Qn=Object.prototype.hasOwnProperty;var pe=(o,e)=>()=>(o&&(e=o(o=0)),e);var x=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports),Be=(o,e)=>{for(var n in e)Ue(o,n,{get:e[n],enumerable:!0})},vt=(o,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Yn(e))!Qn.call(o,r)&&r!==n&&Ue(o,r,{get:()=>e[r],enumerable:!(i=Gn(e,r))||i.enumerable});return o};var m=(o,e,n)=>(n=o!=null?Wn(zn(o)):{},vt(e||!o||!o.__esModule?Ue(n,"default",{value:o,enumerable:!0}):n,o)),D=o=>vt(Ue({},"__esModule",{value:!0}),o);var ze=x((wi,Zn)=>{Zn.exports={name:"@sinch/cli",version:"0.1.
|
|
2
|
+
"use strict";var Wn=Object.create;var Ue=Object.defineProperty;var Gn=Object.getOwnPropertyDescriptor;var Yn=Object.getOwnPropertyNames;var zn=Object.getPrototypeOf,Qn=Object.prototype.hasOwnProperty;var pe=(o,e)=>()=>(o&&(e=o(o=0)),e);var x=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports),Be=(o,e)=>{for(var n in e)Ue(o,n,{get:e[n],enumerable:!0})},vt=(o,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Yn(e))!Qn.call(o,r)&&r!==n&&Ue(o,r,{get:()=>e[r],enumerable:!(i=Gn(e,r))||i.enumerable});return o};var m=(o,e,n)=>(n=o!=null?Wn(zn(o)):{},vt(e||!o||!o.__esModule?Ue(n,"default",{value:o,enumerable:!0}):n,o)),D=o=>vt(Ue({},"__esModule",{value:!0}),o);var ze=x((wi,Zn)=>{Zn.exports={name:"@sinch/cli",version:"0.1.13",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 $e={};Be($e,{SinchAPI:()=>E});var Ke,Xe,ve,E,_=pe(()=>{"use strict";Ke=m(require("axios")),Xe=m(require("form-data")),ve=m(require("chalk")),E=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=Ke.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(ve.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(ve.default.gray(`\u2190 ${n.status} ${n.config.url}`)),n),async n=>{if(process.env.DEBUG_HTTP&&console.log(ve.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 r=await this.getValidToken();if(r)return i.headers.Authorization=`${r.token_type} ${r.access_token}`,this.client.request(i)}catch{console.error(ve.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,r){try{let s=new Xe.default;return s.append("name",e),s.append("runtime",n),s.append("code",i,{filename:"function.zip"}),r&&s.append("configuration",JSON.stringify(r)),(await this.client.post(`/v1/projects/${this.projectId}/functions`,s,{headers:{...s.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(s){throw this._handleError(s,"Failed to deploy function")}}async updateFunction(e,n,i){try{let r=new Xe.default;return r.append("code",n,{filename:"function.zip"}),i&&r.append("configuration",JSON.stringify(i)),(await this.client.put(`/v1/projects/${this.projectId}/functions/${e}`,r,{headers:{...r.getHeaders()},maxContentLength:1/0,maxBodyLength:1/0})).data}catch(r){throw this._handleError(r,`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(r){throw this._handleError(r,"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 r=l=>l.status?"status":l.progress?"progress":l.completed?"completed":l.failed||l.error?"failed":l.connected?"connected":"message",s=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 l=await this.credentials.retrieve();if(l&&l.keyId&&l.keySecret){let u=Buffer.from(`${l.keyId}:${l.keySecret}`).toString("base64");a.Authorization=`Basic ${u}`}}catch(l){console.error("Failed to add authentication:",l)}let c=null;try{c=new AbortController;let l=await fetch(s,{method:"GET",headers:a,signal:c.signal});if(!l.ok)throw new Error(`SSE connection failed: ${l.status} ${l.statusText}`);if(!l.body)throw new Error("Response body is null");let u=l.body.getReader(),g=new TextDecoder,h="";return(async()=>{let w="";try{for(;;){let{done:j,value:q}=await u.read();if(j)break;let $=g.decode(q,{stream:!0});h+=$;let b=h.split(`
|
|
3
3
|
`);h=b.pop()||"";for(let C of b){if(C.startsWith("event:")){w=C.substring(6).trim();continue}if(C.startsWith("data:")){let y=C.substring(5).trim();if(y)try{let P=JSON.parse(y),Z=w||r(P);n({type:Z,data:P}),w=""}catch{n({type:w||"message",data:y}),w=""}}C===""&&(w="")}}}catch(j){j.name!=="AbortError"&&i&&i(j)}})(),()=>{c&&c.abort()}}catch(l){throw console.error("Failed to create SSE stream:",l),i&&i(l),l}}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(ve.default.yellow("Failed to get authentication token:",e.message)),null}}async authenticateOAuth(e,n){try{let i=this.oauthUrl,r=new URLSearchParams;r.append("grant_type","client_credentials");let a=(await Ke.default.post(i,r,{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(Ke.default.isAxiosError(e)){let i=e;if(i.response){let r=i.response.status,s=i.response.data,a=s?.message||s?.error||i.message;throw r===401?new Error('Authentication required. Please run "sinch auth login" first.'):r===403?new Error(`Permission denied: ${a}`):r===404?new Error(`Resource not found: ${a}`):r>=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 qe(){if(!et)try{et=require("keytar")}catch{return null}return et}var kt,X,et,Te,N,Ve,Pt=pe(()=>{"use strict";kt=m(require("os")),X=m(require("chalk")),et=null;Te="sinch-functions-cli",N=kt.userInfo().username,Ve=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=qe();if(!e)return this.keychainAvailable=!1,!1;let n=`${N}-test-${Date.now()}`;return await e.setPassword(Te,n,"test"),await e.deletePassword(Te,n),this.keychainAvailable=!0,!0}catch{return this.keychainAvailable=!1,!1}}async setPassword(e,n){if(!await this.isKeychainAvailable())return!1;let r=qe();return r?(await r.setPassword(Te,e,n),!0):!1}async getPassword(e){if(!await this.isKeychainAvailable())return null;let i=qe();return i?await i.getPassword(Te,e):null}async deletePassword(e){if(!await this.isKeychainAvailable())return!1;try{let i=qe();return i?(await i.deletePassword(Te,e),!0):!1}catch{return!0}}async store(e){let{projectId:n,keyId:i,keySecret:r,applicationKey:s,applicationSecret:a}=e,c=await this.setPassword(`${N}-keySecret`,r),l=await this.setPassword(s,a);c&&l?(this.config.set("keySecretPlaintext",null),this.config.set("applicationSecretPlaintext",null)):(console.log(X.default.yellow(`
|
|
4
4
|
\u26A0\uFE0F OS keychain not available (Docker/CI environment detected)`)),console.log(X.default.yellow("\u26A0\uFE0F Storing secrets in plaintext config file")),console.log(X.default.gray("Location: ~/.sinch/config.json")),console.log(X.default.gray(`
|
|
5
|
-
For better security, export as environment variables instead:`)),console.log(X.default.cyan(' export SINCH_KEY_SECRET="'+r+'"')),console.log(X.default.cyan(' export SINCH_APPLICATION_SECRET="'+a+'"')),this.config.set("keySecretPlaintext",r),this.config.set("applicationSecretPlaintext",a)),this.config.set("projectId",n),this.config.set("keyId",i),this.config.set("defaultApplicationKey",s),this.config.set("credentialsStored",!0),await this.config.save()}async storeOAuthToken(e){let{access_token:n,refresh_token:i,expires_in:r,token_type:s}=e,a=new Date(Date.now()+r*1e3).toISOString(),c=await this.setPassword(`${N}-oauth-access`,n),l=!0;i&&(l=await this.setPassword(`${N}-oauth-refresh`,i)),this.config.set("oauthTokenType",s),this.config.set("oauthExpiresAt",a),this.config.set("oauthStored",c&&l),await this.config.save()}async reAuthenticateOAuth(){try{let e=await this.retrieve();if(!e)return null;let{SinchAPI:n}=(_(),D($e)),r=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(r),r.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(X.default.gray("Token expired, re-authenticating..."));let i=await this.reAuthenticateOAuth();return i?console.log(X.default.green("\u2713 Token renewed successfully")):console.warn(X.default.yellow("Failed to renew OAuth token. Please login again.")),i}return await this.getPassword(`${N}-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,r=process.env.SINCH_APPLICATION_SECRET;if(e&&n&&i&&r)return{projectId:process.env.SINCH_PROJECT_ID||this.config.get("projectId")||"",keyId:e,keySecret:n,applicationKey:i,applicationSecret:r};if(!this.config.get("credentialsStored"))return null;let a=this.config.get("projectId"),c=this.config.get("keyId"),l=this.config.get("defaultApplicationKey"),u=await this.getPassword(`${N}-keySecret`),g=l?await this.getPassword(l):null;return(!u||!g)&&(u=u||n||null,g=g||r||null),(!u||!g)&&(u=u||this.config.get("keySecretPlaintext"),g=g||this.config.get("applicationSecretPlaintext")),!u||!g?null:{projectId:a,keyId:c,keySecret:u,applicationKey:l,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(`${N}-keySecret`),await this.deletePassword(`${N}-oauth-access`),await this.deletePassword(`${N}-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(`${N}-keySecret`),r=this.config.get("defaultApplicationKey"),s=r?await this.getPassword(r):null;return!!(i&&s)}async hasOAuthToken(){return this.config.get("oauthStored")?!!await this.getPassword(`${N}-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(`${N}-basic-auth`,e)&&(this.config.set("basicAuthStored",!0),await this.config.save())}async getBasicAuthToken(){return this.config.get("basicAuthStored")?await this.getPassword(`${N}-basic-auth`):null}async clearBasicAuthToken(){await this.deletePassword(`${N}-basic-auth`),this.config.set("basicAuthStored",!1),await this.config.save()}}});var ge={};Be(ge,{Config:()=>Je,config:()=>p});var H,je,Et,tt,Je,p,R=pe(()=>{"use strict";H=m(require("fs-extra")),je=m(require("path")),Et=m(require("os")),tt=m(require("chalk"));Pt();Je=class{configDir;configFile;projectConfigFile;_config=null;_projectConfig=null;_credentials=null;constructor(){this.configDir=je.join(Et.homedir(),".sinch"),this.configFile=je.join(this.configDir,"config.json"),this.projectConfigFile=je.join(process.cwd(),"sinch.json")}async load(){try{await H.ensureDir(this.configDir),await H.pathExists(this.configFile)?this._config=await H.readJson(this.configFile):(this._config=this._getDefaultConfig(),await this.save())}catch(e){console.warn(tt.default.yellow(`Warning: Could not load config: ${e.message}`)),this._config=this._getDefaultConfig()}this._credentials=new Ve(this);try{await H.pathExists(this.projectConfigFile)&&(this._projectConfig=await H.readJson(this.projectConfigFile))}catch(e){console.warn(tt.default.yellow(`Warning: Could not load project config: ${e.message}`))}}async save(){try{await H.ensureDir(this.configDir),await H.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=je.join(process.cwd(),"sinch.json");await H.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={},r=null){let s={name:e,runtime:n,version:"1.0.0",description:`Sinch Functions project: ${e}`,created:new Date().toISOString()};return i&&Object.keys(i).length>0&&(s.variables=i),r&&(s.applicationKey=r),this._projectConfig=s,await this.saveProjectConfig(),s}isInProject(){return this._projectConfig!==null}getProjectConfig(){return this._projectConfig}_getDefaultConfig(){return{apiUrl:"https://api.functions.dev.sinchvoice.org",oauthUrl:"https://auth.sinch.com/oauth2/token",projectId:"default-project",created:new Date().toISOString(),version:"1.0.0"}}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 Je});var W,se,nt,It,ot,t,U=pe(()=>{"use strict";W=m(require("chalk")),se=m(require("fs")),nt=m(require("path")),It=m(require("os")),ot=class{verbose;logFile;constructor(e={}){this.verbose=e.verbose||process.env.VERBOSE==="true";let n="cli";try{let i=nt.join(process.cwd(),"sinch.json");se.existsSync(i)&&(n=JSON.parse(se.readFileSync(i,"utf8")).name||"cli")}catch{}this.logFile=nt.join(It.tmpdir(),`sinch-${n}.log`);try{se.writeFileSync(this.logFile,`=== Sinch CLI Debug Log - ${new Date().toISOString()} ===
|
|
5
|
+
For better security, export as environment variables instead:`)),console.log(X.default.cyan(' export SINCH_KEY_SECRET="'+r+'"')),console.log(X.default.cyan(' export SINCH_APPLICATION_SECRET="'+a+'"')),this.config.set("keySecretPlaintext",r),this.config.set("applicationSecretPlaintext",a)),this.config.set("projectId",n),this.config.set("keyId",i),this.config.set("defaultApplicationKey",s),this.config.set("credentialsStored",!0),await this.config.save()}async storeOAuthToken(e){let{access_token:n,refresh_token:i,expires_in:r,token_type:s}=e,a=new Date(Date.now()+r*1e3).toISOString(),c=await this.setPassword(`${N}-oauth-access`,n),l=!0;i&&(l=await this.setPassword(`${N}-oauth-refresh`,i)),this.config.set("oauthTokenType",s),this.config.set("oauthExpiresAt",a),this.config.set("oauthStored",c&&l),await this.config.save()}async reAuthenticateOAuth(){try{let e=await this.retrieve();if(!e)return null;let{SinchAPI:n}=(_(),D($e)),r=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(r),r.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(X.default.gray("Token expired, re-authenticating..."));let i=await this.reAuthenticateOAuth();return i?console.log(X.default.green("\u2713 Token renewed successfully")):console.warn(X.default.yellow("Failed to renew OAuth token. Please login again.")),i}return await this.getPassword(`${N}-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,r=process.env.SINCH_APPLICATION_SECRET;if(e&&n&&i&&r)return{projectId:process.env.SINCH_PROJECT_ID||this.config.get("projectId")||"",keyId:e,keySecret:n,applicationKey:i,applicationSecret:r};if(!this.config.get("credentialsStored"))return null;let a=this.config.get("projectId"),c=this.config.get("keyId"),l=this.config.get("defaultApplicationKey"),u=await this.getPassword(`${N}-keySecret`),g=l?await this.getPassword(l):null;return(!u||!g)&&(u=u||n||null,g=g||r||null),(!u||!g)&&(u=u||this.config.get("keySecretPlaintext"),g=g||this.config.get("applicationSecretPlaintext")),!u||!g?null:{projectId:a,keyId:c,keySecret:u,applicationKey:l,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(`${N}-keySecret`),await this.deletePassword(`${N}-oauth-access`),await this.deletePassword(`${N}-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(`${N}-keySecret`),r=this.config.get("defaultApplicationKey"),s=r?await this.getPassword(r):null;return!!(i&&s)}async hasOAuthToken(){return this.config.get("oauthStored")?!!await this.getPassword(`${N}-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(`${N}-basic-auth`,e)&&(this.config.set("basicAuthStored",!0),await this.config.save())}async getBasicAuthToken(){return this.config.get("basicAuthStored")?await this.getPassword(`${N}-basic-auth`):null}async clearBasicAuthToken(){await this.deletePassword(`${N}-basic-auth`),this.config.set("basicAuthStored",!1),await this.config.save()}}});var ge={};Be(ge,{Config:()=>Je,config:()=>p});var H,je,Et,tt,Je,p,R=pe(()=>{"use strict";H=m(require("fs-extra")),je=m(require("path")),Et=m(require("os")),tt=m(require("chalk"));Pt();Je=class{configDir;configFile;projectConfigFile;_config=null;_projectConfig=null;_credentials=null;constructor(){this.configDir=je.join(Et.homedir(),".sinch"),this.configFile=je.join(this.configDir,"config.json"),this.projectConfigFile=je.join(process.cwd(),"sinch.json")}async load(){try{await H.ensureDir(this.configDir),await H.pathExists(this.configFile)?this._config=await H.readJson(this.configFile):(this._config=this._getDefaultConfig(),await this.save())}catch(e){console.warn(tt.default.yellow(`Warning: Could not load config: ${e.message}`)),this._config=this._getDefaultConfig()}this._credentials=new Ve(this);try{await H.pathExists(this.projectConfigFile)&&(this._projectConfig=await H.readJson(this.projectConfigFile))}catch(e){console.warn(tt.default.yellow(`Warning: Could not load project config: ${e.message}`))}}async save(){try{await H.ensureDir(this.configDir),await H.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=je.join(process.cwd(),"sinch.json");await H.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={},r=null){let s={name:e,runtime:n,description:`Sinch Functions project: ${e}`,created:new Date().toISOString()};return i&&Object.keys(i).length>0&&(s.variables=i),r&&(s.applicationKey=r),this._projectConfig=s,await this.saveProjectConfig(),s}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 Je});var W,se,nt,It,ot,t,U=pe(()=>{"use strict";W=m(require("chalk")),se=m(require("fs")),nt=m(require("path")),It=m(require("os")),ot=class{verbose;logFile;constructor(e={}){this.verbose=e.verbose||process.env.VERBOSE==="true";let n="cli";try{let i=nt.join(process.cwd(),"sinch.json");se.existsSync(i)&&(n=JSON.parse(se.readFileSync(i,"utf8")).name||"cli")}catch{}this.logFile=nt.join(It.tmpdir(),`sinch-${n}.log`);try{se.writeFileSync(this.logFile,`=== Sinch CLI Debug Log - ${new Date().toISOString()} ===
|
|
6
6
|
`,{flag:"w"})}catch{}}writeToFile(e,n,...i){try{let s=`[${new Date().toISOString()}] ${e}: ${n} ${i.length>0?JSON.stringify(i):""}
|
|
7
7
|
`;se.appendFileSync(this.logFile,s)}catch{}}info(e,...n){console.log(W.default.blue("\u2139"),e,...n),this.writeToFile("INFO",e,...n)}success(e,...n){console.log(W.default.green("\u2705"),e,...n),this.writeToFile("SUCCESS",e,...n)}warn(e,...n){console.log(W.default.yellow("\u26A0\uFE0F "),e,...n),this.writeToFile("WARN",e,...n)}error(e,...n){console.log(W.default.red("\u274C"),e,...n),this.writeToFile("ERROR",e,...n)}debug(e,...n){this.writeToFile("DEBUG",e,...n),(this.verbose||process.env.DEBUG)&&console.log(W.default.gray("\u{1F41B}"),W.default.gray(e),...n)}log(e,...n){console.log(e,...n)}title(e){console.log(W.default.blue.bold(`
|
|
8
8
|
\u{1F3AF} ${e}`))}subtitle(e){console.log(W.default.gray(`
|
|
9
|
-
${e}`))}list(e,n={}){let{indent:i=" ",bullet:r="\u2022"}=n;e.forEach(s=>{console.log(`${i}${W.default.gray(r)} ${s}`)})}table(e,n){let i=e.map(s=>s.length);n.forEach(s=>{s.forEach((a,c)=>{i[c]=Math.max(i[c]??0,String(a).length)})});let r=e.map((s,a)=>s.padEnd(i[a]??0)).join(" ");console.log(W.default.bold(r)),console.log(W.default.gray("-".repeat(r.length))),n.forEach(s=>{let a=s.map((c,l)=>String(c).padEnd(i[l]??0)).join(" ");console.log(a)})}newline(){console.log()}},t=new ot});var ke={};Be(ke,{Spinner:()=>Me,spinner:()=>d});var At,Rt,Me,d,F=pe(()=>{"use strict";At=m(require("ora")),Rt=m(require("cli-spinners")),Me=class{spinner=null;spinnerType=Rt.default.dots;start(e){return this.spinner=(0,At.default)({text:e,spinner:this.spinnerType}).start(),this}succeed(e){return this.spinner&&(this.spinner.succeed(e),this.spinner=null),this}fail(e){return this.spinner&&(this.spinner.fail(e),this.spinner=null),this}info(e){return this.spinner&&(this.spinner.info(e),this.spinner=null),this}warn(e){return this.spinner&&(this.spinner.warn(e),this.spinner=null),this}stop(){return this.spinner&&(this.spinner.stop(),this.spinner=null),this}update(e){return this.spinner&&(this.spinner.text=e),this}get isSpinning(){return this.spinner?.isSpinning??!1}get text(){return this.spinner?.text??""}set text(e){this.spinner&&(this.spinner.text=e)}},d=new Me});var Ot=x((Ti,Ft)=>{"use strict";var L=m(require("fs-extra")),ae=m(require("path")),B=m(require("chalk")),Pe=m(require("inquirer")),Tt=require("commander");R();U();F();_();var De=require("child_process"),jt=m(require("adm-zip")),Dt=new Tt.Command("init");Dt.description("Initialize a new Sinch Functions project").argument("[template]","Template to use (simple-ivr, customer-lookup, smart-routing)").option("--name <name>","Function name (default: template name)").option("--runtime <runtime>","Runtime to use (node, csharp)").option("--skip-install","Skip npm install for Node.js projects").option("--non-interactive","Use default values without prompting").action(async(o,e)=>{try{await p.load();let n=process.cwd(),i=new E(p.getApiConfig()),r=e.runtime;r||(e.nonInteractive?r="node":r=(await Pe.default.prompt([{type:"list",name:"runtime",message:"Choose a runtime:",choices:[{name:"Node.js",value:"node"},{name:"C# (.NET)",value:"csharp"}],default:"node"}])).runtime),d.start(`Fetching available templates for ${r}...`);let s=await i.listRuntimeTemplates(r);if(d.stop(),!s.templates||s.templates.length===0)throw new Error(`No templates available for runtime '${r}'`);let a=o;if(!a)if(e.nonInteractive){let b=s.templates.filter(C=>C.name&&C.name.trim());if(b.length>0)a=b[0]?.name||"",t.info(`Using default template: ${a} (non-interactive mode)`);else throw new Error("No templates available")}else{let b=s.templates.filter(y=>y.name&&y.name.trim()).map(y=>({name:`${y.name} - ${(y.description||"No description available").substring(0,100)}`,value:y.name}));a=(await Pe.default.prompt([{type:"list",name:"selectedTemplate",message:"Choose a template:",choices:b,pageSize:10}])).selectedTemplate}let c=e.name;c||(e.nonInteractive?c=a:c=(await Pe.default.prompt([{type:"input",name:"name",message:"Function name:",default:a,validate:C=>!C||C.trim().length===0?"Function name is required":/^[a-z][a-z0-9-]*$/.test(C.trim())?!0:"Function name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"}])).name.trim());let l=ae.default.join(n,c);if(await L.default.pathExists(l)){let b=!1;if(e.nonInteractive?(b=!0,t.info(`Directory '${c}' already exists. Overwriting (non-interactive mode)...`)):b=(await Pe.default.prompt([{type:"confirm",name:"overwrite",message:`Directory '${c}' already exists. Overwrite?`,default:!1}])).overwrite,!b){t.info("Initialization cancelled");return}await L.default.remove(l)}await L.default.ensureDir(l);let u=s.templates.find(b=>b.name===a);if(!u){let b=s.templates.map(C=>C.name).join(", ");throw new Error(`Template '${a}' not found. Available templates: ${b}`)}let g=["node","csharp"];if(!g.includes(r))throw new Error(`Runtime '${r}' not supported. Supported runtimes: ${g.join(", ")}`);d.start(`\u{1F680} Initializing ${c} with ${a} template (${r})...`);let h=await i.downloadTemplate(r,a);new jt.default(h).extractAllTo(l,!0),d.succeed("\u{1F4E6} Template extracted");let w={},j=ae.default.join(l,"template.json");if(await L.default.pathExists(j)){let b=await L.default.readJson(j);if(b.variables&&Object.keys(b.variables).length>0)if(e.nonInteractive){t.info("\u{1F4DD} Using default values for template variables (non-interactive mode):");for(let[C,y]of Object.entries(b.variables)){let P;typeof y=="string"?P=y:P=y.default||"",w[C]=P,t.info(` ${C}: ${P}`)}}else{t.newline(),t.info("\u{1F4DD} Configure template variables:");for(let[C,y]of Object.entries(b.variables)){let P;typeof y=="string"?P={type:"input",name:C,message:`${C.replace(/_/g," ").toLowerCase()}:`,default:y}:(P={type:y.type==="number"?"number":"input",name:C,message:`${y.description||C.replace(/_/g," ").toLowerCase()}:`,default:y.default},y.type==="number"&&(P.validate=M=>y.min!==void 0&&M<y.min?`Value must be at least ${y.min}`:y.max!==void 0&&M>y.max?`Value must be at most ${y.max}`:!0));let Z=await Pe.default.prompt([P]);w[C]=Z[C]}}await L.default.remove(j)}d.start("\u{1F527} Finalizing function configuration...");let q=ae.default.join(l,"sinch.json"),$;if(await L.default.pathExists(q)){if($=await L.default.readJson(q),$.name=c,$.description=`Sinch Functions function: ${c}`,$.functionId=null,delete $.projectId,delete $.voiceAppId,$.variables)for(let[b,C]of Object.entries($.variables))typeof C=="string"&&(C==="your-project-id"||C==="{{PROJECT_ID}}"?$.variables[b]=p.get("projectId"):(C==="your-application-key"||C==="{{SINCH_APPLICATION_KEY}}")&&($.variables[b]=p.get("defaultApplicationKey")));$.created=new Date().toISOString(),Object.keys(w).length>0&&($.variables={...$.variables,...w}),$.secrets&&delete $.secrets}else $={name:c,functionId:null,runtime:r,
|
|
9
|
+
${e}`))}list(e,n={}){let{indent:i=" ",bullet:r="\u2022"}=n;e.forEach(s=>{console.log(`${i}${W.default.gray(r)} ${s}`)})}table(e,n){let i=e.map(s=>s.length);n.forEach(s=>{s.forEach((a,c)=>{i[c]=Math.max(i[c]??0,String(a).length)})});let r=e.map((s,a)=>s.padEnd(i[a]??0)).join(" ");console.log(W.default.bold(r)),console.log(W.default.gray("-".repeat(r.length))),n.forEach(s=>{let a=s.map((c,l)=>String(c).padEnd(i[l]??0)).join(" ");console.log(a)})}newline(){console.log()}},t=new ot});var ke={};Be(ke,{Spinner:()=>Me,spinner:()=>d});var At,Rt,Me,d,F=pe(()=>{"use strict";At=m(require("ora")),Rt=m(require("cli-spinners")),Me=class{spinner=null;spinnerType=Rt.default.dots;start(e){return this.spinner=(0,At.default)({text:e,spinner:this.spinnerType}).start(),this}succeed(e){return this.spinner&&(this.spinner.succeed(e),this.spinner=null),this}fail(e){return this.spinner&&(this.spinner.fail(e),this.spinner=null),this}info(e){return this.spinner&&(this.spinner.info(e),this.spinner=null),this}warn(e){return this.spinner&&(this.spinner.warn(e),this.spinner=null),this}stop(){return this.spinner&&(this.spinner.stop(),this.spinner=null),this}update(e){return this.spinner&&(this.spinner.text=e),this}get isSpinning(){return this.spinner?.isSpinning??!1}get text(){return this.spinner?.text??""}set text(e){this.spinner&&(this.spinner.text=e)}},d=new Me});var Ot=x((Ti,Ft)=>{"use strict";var L=m(require("fs-extra")),ae=m(require("path")),B=m(require("chalk")),Pe=m(require("inquirer")),Tt=require("commander");R();U();F();_();var De=require("child_process"),jt=m(require("adm-zip")),Dt=new Tt.Command("init");Dt.description("Initialize a new Sinch Functions project").argument("[template]","Template to use (simple-ivr, customer-lookup, smart-routing)").option("--name <name>","Function name (default: template name)").option("--runtime <runtime>","Runtime to use (node, csharp)").option("--skip-install","Skip npm install for Node.js projects").option("--non-interactive","Use default values without prompting").action(async(o,e)=>{try{await p.load();let n=process.cwd(),i=new E(p.getApiConfig()),r=e.runtime;r||(e.nonInteractive?r="node":r=(await Pe.default.prompt([{type:"list",name:"runtime",message:"Choose a runtime:",choices:[{name:"Node.js",value:"node"},{name:"C# (.NET)",value:"csharp"}],default:"node"}])).runtime),d.start(`Fetching available templates for ${r}...`);let s=await i.listRuntimeTemplates(r);if(d.stop(),!s.templates||s.templates.length===0)throw new Error(`No templates available for runtime '${r}'`);let a=o;if(!a)if(e.nonInteractive){let b=s.templates.filter(C=>C.name&&C.name.trim());if(b.length>0)a=b[0]?.name||"",t.info(`Using default template: ${a} (non-interactive mode)`);else throw new Error("No templates available")}else{let b=s.templates.filter(y=>y.name&&y.name.trim()).map(y=>({name:`${y.name} - ${(y.description||"No description available").substring(0,100)}`,value:y.name}));a=(await Pe.default.prompt([{type:"list",name:"selectedTemplate",message:"Choose a template:",choices:b,pageSize:10}])).selectedTemplate}let c=e.name;c||(e.nonInteractive?c=a:c=(await Pe.default.prompt([{type:"input",name:"name",message:"Function name:",default:a,validate:C=>!C||C.trim().length===0?"Function name is required":/^[a-z][a-z0-9-]*$/.test(C.trim())?!0:"Function name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens"}])).name.trim());let l=ae.default.join(n,c);if(await L.default.pathExists(l)){let b=!1;if(e.nonInteractive?(b=!0,t.info(`Directory '${c}' already exists. Overwriting (non-interactive mode)...`)):b=(await Pe.default.prompt([{type:"confirm",name:"overwrite",message:`Directory '${c}' already exists. Overwrite?`,default:!1}])).overwrite,!b){t.info("Initialization cancelled");return}await L.default.remove(l)}await L.default.ensureDir(l);let u=s.templates.find(b=>b.name===a);if(!u){let b=s.templates.map(C=>C.name).join(", ");throw new Error(`Template '${a}' not found. Available templates: ${b}`)}let g=["node","csharp"];if(!g.includes(r))throw new Error(`Runtime '${r}' not supported. Supported runtimes: ${g.join(", ")}`);d.start(`\u{1F680} Initializing ${c} with ${a} template (${r})...`);let h=await i.downloadTemplate(r,a);new jt.default(h).extractAllTo(l,!0),d.succeed("\u{1F4E6} Template extracted");let w={},j=ae.default.join(l,"template.json");if(await L.default.pathExists(j)){let b=await L.default.readJson(j);if(b.variables&&Object.keys(b.variables).length>0)if(e.nonInteractive){t.info("\u{1F4DD} Using default values for template variables (non-interactive mode):");for(let[C,y]of Object.entries(b.variables)){let P;typeof y=="string"?P=y:P=y.default||"",w[C]=P,t.info(` ${C}: ${P}`)}}else{t.newline(),t.info("\u{1F4DD} Configure template variables:");for(let[C,y]of Object.entries(b.variables)){let P;typeof y=="string"?P={type:"input",name:C,message:`${C.replace(/_/g," ").toLowerCase()}:`,default:y}:(P={type:y.type==="number"?"number":"input",name:C,message:`${y.description||C.replace(/_/g," ").toLowerCase()}:`,default:y.default},y.type==="number"&&(P.validate=M=>y.min!==void 0&&M<y.min?`Value must be at least ${y.min}`:y.max!==void 0&&M>y.max?`Value must be at most ${y.max}`:!0));let Z=await Pe.default.prompt([P]);w[C]=Z[C]}}await L.default.remove(j)}d.start("\u{1F527} Finalizing function configuration...");let q=ae.default.join(l,"sinch.json"),$;if(await L.default.pathExists(q)){if($=await L.default.readJson(q),$.name=c,$.description=`Sinch Functions function: ${c}`,$.functionId=null,delete $.projectId,delete $.voiceAppId,$.variables)for(let[b,C]of Object.entries($.variables))typeof C=="string"&&(C==="your-project-id"||C==="{{PROJECT_ID}}"?$.variables[b]=p.get("projectId"):(C==="your-application-key"||C==="{{SINCH_APPLICATION_KEY}}")&&($.variables[b]=p.get("defaultApplicationKey")));$.created=new Date().toISOString(),Object.keys(w).length>0&&($.variables={...$.variables,...w}),$.secrets&&delete $.secrets}else $={name:c,functionId:null,runtime:r,description:`Sinch Functions function: ${c}`,created:new Date().toISOString(),variables:w,custom:{}};if(await L.default.writeJson(q,$,{spaces:2}),r==="node"){let b=ae.default.join(l,"package.json");if(await L.default.pathExists(b)){let I=await L.default.readJson(b);I.name=c,I.description=$.description,await L.default.writeJson(b,I,{spaces:2})}let C=ae.default.join(l,".env"),y=`# Sinch Functions Environment Variables
|
|
10
10
|
`;y+=`# Generated by sinch functions init
|
|
11
11
|
|
|
12
12
|
`,y+=`# Template Variables
|
|
@@ -72,7 +72,7 @@ ${r}
|
|
|
72
72
|
-d '${JSON.stringify(s,null,2)}'`}catch{e?n+=` -d '${o.requestBody.replace(/'/g,"\\'")}'`:n+=` \\
|
|
73
73
|
-d '${o.requestBody}'`}n.toLowerCase().includes("content-type")||(e?n+=' -H "Content-Type: application/json"':n+=` \\
|
|
74
74
|
-H "Content-Type: application/json"`)}return e?n+=" -v":n+=` \\
|
|
75
|
-
-v`,n}wn.exports=ft});var kn=x((pr,$n)=>{"use strict";var V=m(require("chalk")),bn=m(require("inquirer")),Sn=require("commander");_();R();U();F();var vn=new Sn.Command("delete");vn.alias("del").description("Delete a function").argument("<function-id>","Function ID to delete").option("-f, --force","Skip confirmation").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig());d.start("Loading function details...");let i=await n.getFunction(o);if(d.stop(),t.newline(),t.info("\u{1F5D1}\uFE0F Function to delete:"),t.info(` Name: ${V.default.cyan(i.name)}`),t.info(` ID: ${V.default.gray(i.id)}`),t.info(` Runtime: ${V.default.yellow(i.runtime)}`),t.info(` Status: ${Vo(i.status)}`),t.info(` Created: ${new Date(i.createdAt).toLocaleString()}`),i.containerAppUrl&&t.info(` URL: ${V.default.blue(i.containerAppUrl)}`),!e.force){t.newline(),t.warn("\u26A0\uFE0F This action cannot be undone!");let{confirm:r}=await bn.default.prompt([{type:"confirm",name:"confirm",message:`Are you sure you want to delete function '${i.name}'?`,default:!1}]);if(!r){t.info("Deletion cancelled");return}}d.start("Deleting function..."),await n.deleteFunction(o),d.succeed(`Function '${i.name}' deleted successfully`),p.isInProject()&&p.getProjectConfig()?.functionId===o&&(p.set("functionId",void 0,!0),await p.saveProjectConfig(),t.newline(),t.info("\u{1F4A1} Removed function ID from sinch.json"),t.info(" Deploy again with: sinch functions deploy")),t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 List remaining functions: ${V.default.cyan("sinch functions list")}`),t.info(` \u2022 Deploy new function: ${V.default.cyan("sinch functions deploy")}`)}catch(n){d.stop(),n.message.includes("404")||n.message.includes("not found")?(t.error(`Function '${o}' not found`),t.info("\u{1F4A1} List available functions with:"),t.info(` ${V.default.cyan("sinch functions list")}`)):t.error(`Failed to delete function: ${n.message}`),process.exit(1)}});function Vo(o){return{Running:V.default.green("\u2705 Running"),Failed:V.default.red("\u274C Failed"),Pending:V.default.yellow("\u23F3 Pending"),Building:V.default.blue("\u{1F528} Building")}[o]||V.default.gray(o)}$n.exports=vn});var In=x((wr,En)=>{"use strict";var Pn=require("commander"),gt=m(require("fs-extra")),mt=m(require("path")),Ce=m(require("chalk"));R();U();F();lt();_();var ht=new Pn.Command("docs");ht.description("Generate documentation for voice functions (saves to README.md)");ht.option("-f, --fresh","Force regeneration (skip cache)").option("-u, --update","Update documentation for deployed function via API").action(async o=>{try{await p.load(),o.update?await Mo():await Jo(o)}catch(e){d.fail("\u274C Failed to generate documentation"),e.message.includes("No function file found")?t.error("No function file found. Make sure you're in a function directory with function.js, index.js, or handler.js"):e.message.includes("unauthorized")||e.message.includes("401")?t.error("Authentication failed. Run `sinch auth login` first."):e.message.includes("AI service")?t.error("AI documentation service unavailable. Please try again later."):e.message.includes("Function not found")||e.message.includes("404")?t.error("Function not found. Make sure the function is deployed first."):t.error(`Error: ${e.message}`),t.debug("Full error:",e),process.exit(1)}});async function Jo(o){d.start("\u{1F916} Generating documentation...");let e=await at();d.succeed("\u2705 Documentation generated!"),await ct(e,"README.md"),await Ho()}async function Mo(){let o=mt.default.basename(process.cwd());d.start("\u{1F4D6} Reading local README.md...");let e=mt.default.join(process.cwd(),"README.md");if(!await gt.default.pathExists(e))throw new Error("README.md not found. Run `sinch functions docs` first to generate documentation locally.");if(!(await gt.default.readFile(e,"utf8")).trim())throw new Error("README.md is empty. Run `sinch functions docs` first to generate documentation.");d.text="\u{1F50D} Finding deployed function...";let s=(await new E(p.getApiConfig()).listFunctions()).find(a=>a.name===o);if(!s)throw new Error(`Function '${o}' not found in project. Make sure it's deployed first.`);d.text="\u{1F4E4} Uploading documentation content...",t.info("Documentation content ready for update"),d.succeed("\u2705 Documentation updated!"),t.newline(),t.success(`\u{1F4C4} Documentation updated for deployed function: ${Ce.default.cyan(o)}`),t.info(""),t.info("\u2728 Your function documentation has been updated:"),t.info(` \u2022 Function ID: ${Ce.default.gray(s.id)}`),t.info(` \u2022 Source: ${Ce.default.gray("Local README.md")}`),t.info(` \u2022 Updated: ${Ce.default.gray(new Date().toLocaleString())}`),t.info(` \u2022 View in dashboard: ${Ce.default.cyan("Dashboard \u2192 Functions \u2192 "+o)}`),t.newline(),t.info("\u{1F4A1} The updated documentation is now available in the dashboard!")}async function Ho(){t.newline(),t.success(`\u{1F4C4} Documentation updated in: ${Ce.default.cyan("README.md")}`),t.info(""),t.info("\u2728 Your function documentation is ready:"),t.info(" \u2022 Review and edit the AI-generated content as needed"),t.info(" \u2022 Sinch-themed diagrams will render in markdown viewers"),t.info(" \u2022 File is ready for version control and deployment"),t.newline(),t.info("\u{1F4A1} Popular markdown viewers:"),t.info(" \u2022 VS Code (built-in)"),t.info(" \u2022 GitHub (drag & drop file)"),t.info(" \u2022 Typora, Mark Text, or any markdown app")}En.exports=ht});var Dn=x(($r,jn)=>{"use strict";var O=m(require("chalk")),yt=m(require("inquirer")),An=require("commander"),Y=m(require("fs-extra")),le=m(require("path")),Rn=m(require("adm-zip"));_();R();U();F();var Tn=new An.Command("download");Tn.description("Download function source code").argument("[function-id]","Function ID to download (ULID format)").option("-o, --output <dir>","Output directory (default: ./functions/<function-name>)").option("--no-extract","Keep as ZIP file without extracting").option("--non-interactive","Skip interactive selection").option("--version <version>","Download specific version (if available)").option("-f, --force","Overwrite existing directory without prompting").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig()),i=o;if(!i&&!e.nonInteractive){let u=await Wo(n);if(!u){t.info("Download cancelled");return}i=u}i||(t.error("Function ID required. Provide as argument or select interactively"),t.info(`Usage: ${O.default.cyan("sinch functions download 01HX3KC5V8G3KCNZ8S5Y9ABCDE")}`),t.info(` or: ${O.default.cyan("sinch functions download")} (interactive selection)`),process.exit(1)),d.start("\u{1F4E1} Fetching function metadata...");let r=await n.getFunction(i);d.succeed("Function metadata loaded");let s=le.join("functions",r.name),a=e.output||s,c=le.resolve(a);if(await Y.pathExists(c)){if(!e.force){let{overwrite:u}=await yt.default.prompt([{type:"confirm",name:"overwrite",message:`Directory '${a}' already exists. Overwrite?`,default:!1}]);if(!u){t.info("Download cancelled");return}}d.start("\u{1F5D1}\uFE0F Removing existing directory..."),await Y.remove(c),d.succeed("Existing directory removed")}d.start("\u{1F4E5} Downloading function source code...");let l=await n.downloadFunction(i);if(d.succeed("Source code downloaded"),e.extract!==!1){d.start("\u{1F4E6} Extracting files..."),await Y.ensureDir(c);let u=new Rn.default(l),g=u.getEntries();u.extractAllTo(c,!0),d.succeed(`\u{1F4E6} Extracted ${g.length} files`);let h=le.join(c,"sinch-metadata.json"),S={functionId:r.id,name:r.name,runtime:r.runtime,status:r.status,downloadedAt:new Date().toISOString(),url:r.containerAppUrl,configuration:r.configuration};await Y.writeJson(h,S,{spaces:2}),t.newline(),t.success("\u2705 Function downloaded successfully!"),t.newline(),t.info(`\u{1F4C1} Location: ${O.default.cyan(a)}`),t.info(`\u{1F4CB} Files: ${g.length} files extracted`);let w=le.join(c,"sinch.json");await Y.pathExists(w)&&t.info("\u{1F527} Config: sinch.json found"),t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 Navigate: ${O.default.cyan(`cd ${a}`)}`),t.info(` \u2022 Run locally: ${O.default.cyan("sinch functions dev")}`),t.info(` \u2022 Deploy changes: ${O.default.cyan("sinch functions deploy")}`)}else{let u=`${r.name}.zip`,g=e.output?le.resolve(e.output):le.resolve(u);d.start(`\u{1F4BE} Saving as ${u}...`),await Y.writeFile(g,l),d.succeed("ZIP file saved"),t.newline(),t.success("\u2705 Function downloaded as ZIP!"),t.info(`\u{1F4C1} File: ${O.default.cyan(g)}`),t.info(`\u{1F4E6} Size: ${(l.length/1024).toFixed(2)} KB`)}}catch(n){d.stop(),n.message.includes("404")||n.message.includes("not found")?(t.error(`Function not found with ID: ${o}`),t.info("\u{1F4A1} List available functions with:"),t.info(` ${O.default.cyan("sinch functions list")}`),t.info("\u{1F4A1} Or use interactive selection:"),t.info(` ${O.default.cyan("sinch functions download")}`)):t.error(`Failed to download function: ${n.message}`),process.exit(1)}});async function Wo(o){try{d.start("\u{1F4CB} Loading functions...");let e=await o.listFunctions();if(d.stop(),!e.functions||e.functions.length===0)return t.warn("No functions found to download"),null;let n=e.functions.map(r=>({name:`${O.default.cyan(r.name.padEnd(20))} ${O.default.yellow(r.runtime.padEnd(8))} ${Go(r.status)}`,value:r.id,short:r.name}));t.newline();let{selectedFunction:i}=await yt.default.prompt([{type:"list",name:"selectedFunction",message:"Select a function to download:",choices:n,pageSize:15}]);return i}catch(e){throw d.stop(),e}}function Go(o){return{Running:O.default.green("\u2705 Running"),Failed:O.default.red("\u274C Failed"),Pending:O.default.yellow("\u23F3 Pending"),Building:O.default.blue("\u{1F528} Building")}[o]||O.default.gray(o)}jn.exports=Tn});var _n=x((kr,On)=>{"use strict";var Fn=require("commander"),Yo=Ot(),zo=Ut(),Qo=Xt(),Zo=rn(),Xo=un(),ei=Cn(),ti=kn(),ni=In(),oi=Dn(),z=new Fn.Command("functions");z.description("Manage Sinch Functions");z.addCommand(Yo);z.addCommand(zo);z.addCommand(Qo);z.addCommand(oi);z.addCommand(Zo);z.addCommand(Xo);z.addCommand(ei);z.addCommand(ti);z.addCommand(ni);On.exports=z});var Nn=x((Rr,xn)=>{"use strict";var Ln=require("commander"),J=m(require("chalk"));_();R();U();F();var ne=new Ln.Command("templates");ne.description("Browse and manage function templates");ne.command("list").alias("ls").description("List available templates").argument("[runtime]","Filter by runtime (node, csharp, python)").option("-c, --category <category>","Filter by category (voice, sms, verification)").option("-d, --detailed","Show detailed information").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig());d.start("Loading templates...");let i;if(o?i=await n.listRuntimeTemplates(o,e.category):i=await n.listAllTemplates(e.category),d.stop(),i.templates.length===0){t.info(`No templates found${o?` for ${o}`:""}${e.category?` in category ${e.category}`:""}`);return}let r=`Available Templates${o?` (${o})`:""}${e.category?` - ${e.category}`:""}`;if(t.title(r),e.detailed)i.templates.forEach((s,a)=>{a>0&&t.newline(),t.log(J.default.bold.blue(`${s.name}`)),t.log(` Runtime: ${s.runtime}`),t.log(` Category: ${s.category}`),t.log(` Description: ${s.description}`),s.tags&&s.tags.length>0&&t.log(` Tags: ${s.tags.map(c=>J.default.gray(c)).join(", ")}`),t.log(` Path: ${J.default.gray(s.path)}`)});else{let s=i.templates.map(a=>[a.name,a.runtime,a.category,a.description.substring(0,50)+(a.description.length>50?"...":""),a.tags?a.tags.slice(0,2).join(", ")+(a.tags.length>2?"...":""):""]);t.table(["Name","Runtime","Category","Description","Tags"],s)}t.newline(),t.info(`Found ${i.templates.length} template${i.templates.length===1?"":"s"}`),t.info('Use "sinch templates show <runtime>/<template>" for details')}catch(n){d.stop(),t.error(`Failed to list templates: ${n.message}`),process.exit(1)}});ne.command("show").description("Show detailed template information").argument("<template>",'Template name in format "runtime/template-name"').action(async o=>{try{await p.load();let e=new E(p.getApiConfig());if(!o.includes("/"))throw new Error('Template must be in format "runtime/template-name" (e.g., "node/simple-voice-ivr")');let n=o.split("/"),i=n[0],r=n[1];if(!i||!r)throw new Error('Template must be in format "runtime/template-name" (e.g., "node/simple-voice-ivr")');d.start(`Loading template details for ${o}...`);let s=await e.getTemplateDetails(i,r);d.stop(),t.title(`Template: ${s.name}`),t.newline(),t.log(J.default.bold("Basic Information:")),t.log(` Runtime: ${s.runtime}`),t.log(` Category: ${s.category}`),t.log(` Description: ${s.description}`),s.tags&&s.tags.length>0&&t.log(` Tags: ${s.tags.map(a=>J.default.blue(a)).join(", ")}`),s.variables&&Object.keys(s.variables).length>0&&(t.newline(),t.log(J.default.bold("Template Variables:")),Object.entries(s.variables).forEach(([a,c])=>{t.log(` ${a}: ${J.default.gray(c)}`)})),s.files&&s.files.length>0&&(t.newline(),t.log(J.default.bold("Files:")),s.files.forEach(a=>{t.log(` ${J.default.gray("\u2022")} ${a}`)})),s.readmeContent&&(t.newline(),t.log(J.default.bold("README:")),s.readmeContent.split(`
|
|
75
|
+
-v`,n}wn.exports=ft});var kn=x((pr,$n)=>{"use strict";var V=m(require("chalk")),bn=m(require("inquirer")),Sn=require("commander");_();R();U();F();var vn=new Sn.Command("delete");vn.alias("del").description("Delete a function").argument("<function-id>","Function ID to delete").option("-f, --force","Skip confirmation").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig());d.start("Loading function details...");let i=await n.getFunction(o);if(d.stop(),t.newline(),t.info("\u{1F5D1}\uFE0F Function to delete:"),t.info(` Name: ${V.default.cyan(i.name)}`),t.info(` ID: ${V.default.gray(i.id)}`),t.info(` Runtime: ${V.default.yellow(i.runtime)}`),t.info(` Status: ${Vo(i.status)}`),t.info(` Created: ${new Date(i.createdAt).toLocaleString()}`),i.containerAppUrl&&t.info(` URL: ${V.default.blue(i.containerAppUrl)}`),!e.force){t.newline(),t.warn("\u26A0\uFE0F This action cannot be undone!");let{confirm:r}=await bn.default.prompt([{type:"confirm",name:"confirm",message:`Are you sure you want to delete function '${i.name}'?`,default:!1}]);if(!r){t.info("Deletion cancelled");return}}d.start("Deleting function..."),await n.deleteFunction(o),d.succeed(`Function '${i.name}' deleted successfully`),p.isInProject()&&p.getProjectConfig()?.functionId===o&&(p.set("functionId",void 0,!0),await p.saveProjectConfig(),t.newline(),t.info("\u{1F4A1} Removed function ID from sinch.json"),t.info(" Deploy again with: sinch functions deploy")),t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 List remaining functions: ${V.default.cyan("sinch functions list")}`),t.info(` \u2022 Deploy new function: ${V.default.cyan("sinch functions deploy")}`)}catch(n){d.stop(),n.message.includes("404")||n.message.includes("not found")?(t.error(`Function '${o}' not found`),t.info("\u{1F4A1} List available functions with:"),t.info(` ${V.default.cyan("sinch functions list")}`)):t.error(`Failed to delete function: ${n.message}`),process.exit(1)}});function Vo(o){return{Running:V.default.green("\u2705 Running"),Failed:V.default.red("\u274C Failed"),Pending:V.default.yellow("\u23F3 Pending"),Building:V.default.blue("\u{1F528} Building")}[o]||V.default.gray(o)}$n.exports=vn});var In=x((wr,En)=>{"use strict";var Pn=require("commander"),gt=m(require("fs-extra")),mt=m(require("path")),Ce=m(require("chalk"));R();U();F();lt();_();var ht=new Pn.Command("docs");ht.description("Generate documentation for voice functions (saves to README.md)");ht.option("-f, --fresh","Force regeneration (skip cache)").option("-u, --update","Update documentation for deployed function via API").action(async o=>{try{await p.load(),o.update?await Mo():await Jo(o)}catch(e){d.fail("\u274C Failed to generate documentation"),e.message.includes("No function file found")?t.error("No function file found. Make sure you're in a function directory with function.js, index.js, or handler.js"):e.message.includes("unauthorized")||e.message.includes("401")?t.error("Authentication failed. Run `sinch auth login` first."):e.message.includes("AI service")?t.error("AI documentation service unavailable. Please try again later."):e.message.includes("Function not found")||e.message.includes("404")?t.error("Function not found. Make sure the function is deployed first."):t.error(`Error: ${e.message}`),t.debug("Full error:",e),process.exit(1)}});async function Jo(o){d.start("\u{1F916} Generating documentation...");let e=await at();d.succeed("\u2705 Documentation generated!"),await ct(e,"README.md"),await Ho()}async function Mo(){let o=mt.default.basename(process.cwd());d.start("\u{1F4D6} Reading local README.md...");let e=mt.default.join(process.cwd(),"README.md");if(!await gt.default.pathExists(e))throw new Error("README.md not found. Run `sinch functions docs` first to generate documentation locally.");if(!(await gt.default.readFile(e,"utf8")).trim())throw new Error("README.md is empty. Run `sinch functions docs` first to generate documentation.");d.text="\u{1F50D} Finding deployed function...";let s=(await new E(p.getApiConfig()).listFunctions()).find(a=>a.name===o);if(!s)throw new Error(`Function '${o}' not found in project. Make sure it's deployed first.`);d.text="\u{1F4E4} Uploading documentation content...",t.info("Documentation content ready for update"),d.succeed("\u2705 Documentation updated!"),t.newline(),t.success(`\u{1F4C4} Documentation updated for deployed function: ${Ce.default.cyan(o)}`),t.info(""),t.info("\u2728 Your function documentation has been updated:"),t.info(` \u2022 Function ID: ${Ce.default.gray(s.id)}`),t.info(` \u2022 Source: ${Ce.default.gray("Local README.md")}`),t.info(` \u2022 Updated: ${Ce.default.gray(new Date().toLocaleString())}`),t.info(` \u2022 View in dashboard: ${Ce.default.cyan("Dashboard \u2192 Functions \u2192 "+o)}`),t.newline(),t.info("\u{1F4A1} The updated documentation is now available in the dashboard!")}async function Ho(){t.newline(),t.success(`\u{1F4C4} Documentation updated in: ${Ce.default.cyan("README.md")}`),t.info(""),t.info("\u2728 Your function documentation is ready:"),t.info(" \u2022 Review and edit the AI-generated content as needed"),t.info(" \u2022 Sinch-themed diagrams will render in markdown viewers"),t.info(" \u2022 File is ready for version control and deployment"),t.newline(),t.info("\u{1F4A1} Popular markdown viewers:"),t.info(" \u2022 VS Code (built-in)"),t.info(" \u2022 GitHub (drag & drop file)"),t.info(" \u2022 Typora, Mark Text, or any markdown app")}En.exports=ht});var Dn=x(($r,jn)=>{"use strict";var O=m(require("chalk")),yt=m(require("inquirer")),An=require("commander"),Y=m(require("fs-extra")),le=m(require("path")),Rn=m(require("adm-zip"));_();R();U();F();var Tn=new An.Command("download");Tn.description("Download function source code").argument("[function-id]","Function ID to download (ULID format)").option("-o, --output <dir>","Output directory (default: ./functions/<function-name>)").option("--no-extract","Keep as ZIP file without extracting").option("--non-interactive","Skip interactive selection").option("-f, --force","Overwrite existing directory without prompting").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig()),i=o;if(!i&&!e.nonInteractive){let u=await Wo(n);if(!u){t.info("Download cancelled");return}i=u}i||(t.error("Function ID required. Provide as argument or select interactively"),t.info(`Usage: ${O.default.cyan("sinch functions download 01HX3KC5V8G3KCNZ8S5Y9ABCDE")}`),t.info(` or: ${O.default.cyan("sinch functions download")} (interactive selection)`),process.exit(1)),d.start("\u{1F4E1} Fetching function metadata...");let r=await n.getFunction(i);d.succeed("Function metadata loaded");let s=le.join("functions",r.name),a=e.output||s,c=le.resolve(a);if(await Y.pathExists(c)){if(!e.force){let{overwrite:u}=await yt.default.prompt([{type:"confirm",name:"overwrite",message:`Directory '${a}' already exists. Overwrite?`,default:!1}]);if(!u){t.info("Download cancelled");return}}d.start("\u{1F5D1}\uFE0F Removing existing directory..."),await Y.remove(c),d.succeed("Existing directory removed")}d.start("\u{1F4E5} Downloading function source code...");let l=await n.downloadFunction(i);if(d.succeed("Source code downloaded"),e.extract!==!1){d.start("\u{1F4E6} Extracting files..."),await Y.ensureDir(c);let u=new Rn.default(l),g=u.getEntries();u.extractAllTo(c,!0),d.succeed(`\u{1F4E6} Extracted ${g.length} files`);let h=le.join(c,"sinch-metadata.json"),S={functionId:r.id,name:r.name,runtime:r.runtime,status:r.status,downloadedAt:new Date().toISOString(),url:r.containerAppUrl,configuration:r.configuration};await Y.writeJson(h,S,{spaces:2}),t.newline(),t.success("\u2705 Function downloaded successfully!"),t.newline(),t.info(`\u{1F4C1} Location: ${O.default.cyan(a)}`),t.info(`\u{1F4CB} Files: ${g.length} files extracted`);let w=le.join(c,"sinch.json");await Y.pathExists(w)&&t.info("\u{1F527} Config: sinch.json found"),t.newline(),t.info("\u{1F4A1} Next steps:"),t.info(` \u2022 Navigate: ${O.default.cyan(`cd ${a}`)}`),t.info(` \u2022 Run locally: ${O.default.cyan("sinch functions dev")}`),t.info(` \u2022 Deploy changes: ${O.default.cyan("sinch functions deploy")}`)}else{let u=`${r.name}.zip`,g=e.output?le.resolve(e.output):le.resolve(u);d.start(`\u{1F4BE} Saving as ${u}...`),await Y.writeFile(g,l),d.succeed("ZIP file saved"),t.newline(),t.success("\u2705 Function downloaded as ZIP!"),t.info(`\u{1F4C1} File: ${O.default.cyan(g)}`),t.info(`\u{1F4E6} Size: ${(l.length/1024).toFixed(2)} KB`)}}catch(n){d.stop(),n.message.includes("404")||n.message.includes("not found")?(t.error(`Function not found with ID: ${o}`),t.info("\u{1F4A1} List available functions with:"),t.info(` ${O.default.cyan("sinch functions list")}`),t.info("\u{1F4A1} Or use interactive selection:"),t.info(` ${O.default.cyan("sinch functions download")}`)):t.error(`Failed to download function: ${n.message}`),process.exit(1)}});async function Wo(o){try{d.start("\u{1F4CB} Loading functions...");let e=await o.listFunctions();if(d.stop(),!e.functions||e.functions.length===0)return t.warn("No functions found to download"),null;let n=e.functions.map(r=>({name:`${O.default.cyan(r.name.padEnd(20))} ${O.default.yellow(r.runtime.padEnd(8))} ${Go(r.status)}`,value:r.id,short:r.name}));t.newline();let{selectedFunction:i}=await yt.default.prompt([{type:"list",name:"selectedFunction",message:"Select a function to download:",choices:n,pageSize:15}]);return i}catch(e){throw d.stop(),e}}function Go(o){return{Running:O.default.green("\u2705 Running"),Failed:O.default.red("\u274C Failed"),Pending:O.default.yellow("\u23F3 Pending"),Building:O.default.blue("\u{1F528} Building")}[o]||O.default.gray(o)}jn.exports=Tn});var _n=x((kr,On)=>{"use strict";var Fn=require("commander"),Yo=Ot(),zo=Ut(),Qo=Xt(),Zo=rn(),Xo=un(),ei=Cn(),ti=kn(),ni=In(),oi=Dn(),z=new Fn.Command("functions");z.description("Manage Sinch Functions");z.addCommand(Yo);z.addCommand(zo);z.addCommand(Qo);z.addCommand(oi);z.addCommand(Zo);z.addCommand(Xo);z.addCommand(ei);z.addCommand(ti);z.addCommand(ni);On.exports=z});var Nn=x((Rr,xn)=>{"use strict";var Ln=require("commander"),J=m(require("chalk"));_();R();U();F();var ne=new Ln.Command("templates");ne.description("Browse and manage function templates");ne.command("list").alias("ls").description("List available templates").argument("[runtime]","Filter by runtime (node, csharp, python)").option("-c, --category <category>","Filter by category (voice, sms, verification)").option("-d, --detailed","Show detailed information").action(async(o,e)=>{try{await p.load();let n=new E(p.getApiConfig());d.start("Loading templates...");let i;if(o?i=await n.listRuntimeTemplates(o,e.category):i=await n.listAllTemplates(e.category),d.stop(),i.templates.length===0){t.info(`No templates found${o?` for ${o}`:""}${e.category?` in category ${e.category}`:""}`);return}let r=`Available Templates${o?` (${o})`:""}${e.category?` - ${e.category}`:""}`;if(t.title(r),e.detailed)i.templates.forEach((s,a)=>{a>0&&t.newline(),t.log(J.default.bold.blue(`${s.name}`)),t.log(` Runtime: ${s.runtime}`),t.log(` Category: ${s.category}`),t.log(` Description: ${s.description}`),s.tags&&s.tags.length>0&&t.log(` Tags: ${s.tags.map(c=>J.default.gray(c)).join(", ")}`),t.log(` Path: ${J.default.gray(s.path)}`)});else{let s=i.templates.map(a=>[a.name,a.runtime,a.category,a.description.substring(0,50)+(a.description.length>50?"...":""),a.tags?a.tags.slice(0,2).join(", ")+(a.tags.length>2?"...":""):""]);t.table(["Name","Runtime","Category","Description","Tags"],s)}t.newline(),t.info(`Found ${i.templates.length} template${i.templates.length===1?"":"s"}`),t.info('Use "sinch templates show <runtime>/<template>" for details')}catch(n){d.stop(),t.error(`Failed to list templates: ${n.message}`),process.exit(1)}});ne.command("show").description("Show detailed template information").argument("<template>",'Template name in format "runtime/template-name"').action(async o=>{try{await p.load();let e=new E(p.getApiConfig());if(!o.includes("/"))throw new Error('Template must be in format "runtime/template-name" (e.g., "node/simple-voice-ivr")');let n=o.split("/"),i=n[0],r=n[1];if(!i||!r)throw new Error('Template must be in format "runtime/template-name" (e.g., "node/simple-voice-ivr")');d.start(`Loading template details for ${o}...`);let s=await e.getTemplateDetails(i,r);d.stop(),t.title(`Template: ${s.name}`),t.newline(),t.log(J.default.bold("Basic Information:")),t.log(` Runtime: ${s.runtime}`),t.log(` Category: ${s.category}`),t.log(` Description: ${s.description}`),s.tags&&s.tags.length>0&&t.log(` Tags: ${s.tags.map(a=>J.default.blue(a)).join(", ")}`),s.variables&&Object.keys(s.variables).length>0&&(t.newline(),t.log(J.default.bold("Template Variables:")),Object.entries(s.variables).forEach(([a,c])=>{t.log(` ${a}: ${J.default.gray(c)}`)})),s.files&&s.files.length>0&&(t.newline(),t.log(J.default.bold("Files:")),s.files.forEach(a=>{t.log(` ${J.default.gray("\u2022")} ${a}`)})),s.readmeContent&&(t.newline(),t.log(J.default.bold("README:")),s.readmeContent.split(`
|
|
76
76
|
`).slice(0,10).forEach(c=>{t.log(` ${J.default.gray(c)}`)}),s.readmeContent.split(`
|
|
77
77
|
`).length>10&&t.log(` ${J.default.gray("... (truncated)")}`)),t.newline(),t.info(`Use "sinch functions init --template ${o}" to create a function from this template`)}catch(e){d.stop(),t.error(`Failed to show template details: ${e.message}`),process.exit(1)}});ne.command("node").description("List Node.js templates").option("-c, --category <category>","Filter by category").action(async o=>{let e=ne.commands.find(n=>n.name()==="list");e&&await e.action("node",o)});ne.command("csharp").description("List C# templates").option("-c, --category <category>","Filter by category").action(async o=>{let e=ne.commands.find(n=>n.name()==="list");e&&await e.action("csharp",o)});ne.command("python").description("List Python templates").option("-c, --category <category>","Filter by category").action(async o=>{let e=ne.commands.find(n=>n.name()==="list");e&&await e.action("python",o)});xn.exports=ne});var qn=x((Fr,Kn)=>{"use strict";var Un=require("commander"),be=m(require("chalk")),Bn=m(require("inquirer"));R();U();F();var xe=new Un.Command("voice");xe.description("Manage Sinch Voice applications and settings");xe.command("callback-url").alias("webhook").description("Update webhook callback URL for your Voice application").argument("[url]","New callback URL (if not provided, will prompt)").option("-a, --app-key <key>","Application key (if not provided, will show selection)").action(async(o,e)=>{try{await p.load(),await wt()||(t.error('Not authenticated. Run "sinch auth login" first.'),process.exit(1));let n=await ii(),i=e.appKey;if(!i){let r=p.getPublicCredentialInfo()?.applicationKey;r||(t.error('No default application key found. Run "sinch auth login" first.'),process.exit(1)),i=r,t.info(`Using default application: ${i}`)}if(!o){let{newUrl:r}=await Bn.default.prompt([{type:"input",name:"newUrl",message:"Enter new callback URL:",validate:s=>{if(!s)return"URL is required";try{return new URL(s),!0}catch{return"Please enter a valid URL"}}}]);o=r}d.start("Updating callback URL...");try{let r=await n.voice.applications.get(i),s={...r,capability:{...r.capability,voice:{...r.capability?.voice,webhooks:{...r.capability?.voice?.webhooks,url:o,method:"POST"}}}};await n.voice.applications.update(i,s),d.succeed("Callback URL updated successfully!"),t.newline(),t.info(`Application: ${r.displayName||i}`),t.info(`New callback URL: ${o}`),t.newline(),t.info("\u{1F389} Your Voice functions will now receive webhooks at this URL"),t.info("\u{1F4A1} Test with: sinch functions dev --tunnel")}catch(r){throw d.fail("Failed to update callback URL"),r}}catch(n){d.stop(),t.error(`Failed to update callback URL: ${n.message}`),process.exit(1)}});xe.command("get-callbacks").description("Get current callback URLs with full debug output").option("-a, --app-key <key>","Application key (defaults to stored app key)").option("--curl","Show equivalent curl command").action(async o=>{try{await p.load(),await wt()||(t.error('Not authenticated. Run "sinch auth login" first.'),process.exit(1));let e=await p.getApplicationCredentials();e||(t.error('No application credentials found. Run "sinch auth login" first.'),process.exit(1));let n=o.appKey||e.applicationKey;if(t.info("\u{1F50D} Getting callback URLs..."),t.newline(),o.curl){let i=`${e.applicationKey}:${e.applicationSecret}`,r=Buffer.from(i).toString("base64");t.info("\u{1F4CB} CURL Command:"),t.info(be.default.gray("```bash"));let s=`curl -X GET \\
|
|
78
78
|
https://calling.api.sinch.com/calling/v1/applications/${n}/callbacks/urls \\
|