@sinch/cli 0.4.3 → 0.4.6

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 CHANGED
@@ -137,7 +137,7 @@ const stripeKey = process.env.STRIPE_KEY;
137
137
 
138
138
  ### 🔄 Hot Reload Development
139
139
 
140
- Changes to your function are automatically reloaded during `sinch functions dev` via nodemon (Node.js) or dotnet watch (C#). No flags needed:
140
+ Changes to your function are automatically reloaded during `sinch functions dev` via file watching (Node.js) or dotnet watch (C#). No flags needed:
141
141
 
142
142
  ```bash
143
143
  sinch functions dev
package/bin/s CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // Debug binary - use node for breakpoints/inspector
4
- require('../dist/index.js');
3
+ // Shorthand alias same as bin/sinch
4
+ require('./sinch');
package/bin/sinch CHANGED
@@ -1,36 +1,17 @@
1
- #!/usr/bin/env node
2
-
3
- // Prefer compiled Bun binary (fast, single-file, no module issues).
4
- // Falls back to tsx→source for dev, or bun→dist for npm installs.
5
- const { execFileSync, execSync } = require('child_process');
6
- const path = require('path');
7
- const fs = require('fs');
8
-
9
- const binName = process.platform === 'win32' ? 'sinch-bun.exe' : 'sinch-bun';
10
- const binary = path.join(__dirname, '..', 'dist', binName);
11
-
12
- if (fs.existsSync(binary)) {
13
- try {
14
- const result = require('child_process').spawnSync(binary, process.argv.slice(2), {
15
- stdio: 'inherit',
16
- windowsHide: false,
17
- });
18
- process.exit(result.status ?? 1);
19
- } catch { /* fall through */ }
20
- }
21
-
22
- // Fallback: run source directly via tsx (works with ESM, no CJS issues)
23
- try {
24
- const src = path.join(__dirname, '..', 'src', 'index.ts');
25
- if (fs.existsSync(src)) {
26
- const result = require('child_process').spawnSync('npx', ['tsx', src, ...process.argv.slice(2)], {
27
- stdio: 'inherit',
28
- windowsHide: false,
29
- shell: true,
30
- });
31
- process.exit(result.status ?? 1);
32
- }
33
- } catch { /* fall through */ }
34
-
35
- // Last resort: try requiring dist (works if module format is compatible)
36
- require('../dist/index.js');
1
+ #!/bin/sh
2
+ # Native launcher — execs the Bun-compiled binary, no Node.js required.
3
+ # npm registers this via the "bin" field in package.json.
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ ARCH="$(uname -m)"
6
+ case "$ARCH" in
7
+ x86_64|amd64) ARCH="x64" ;;
8
+ aarch64|arm64) ARCH="arm64" ;;
9
+ esac
10
+ OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
11
+ BINARY="$SCRIPT_DIR/../dist/sinch-${OS}-${ARCH}"
12
+ if [ ! -f "$BINARY" ]; then
13
+ echo "Sinch CLI binary not found: $BINARY" >&2
14
+ echo "Reinstall with: npm install -g @sinch/cli" >&2
15
+ exit 1
16
+ fi
17
+ exec "$BINARY" "$@"
package/bin/sinch.cmd ADDED
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ "%~dp0..\dist\sinch-windows-x64.exe" %*
package/package.json CHANGED
@@ -1,99 +1,25 @@
1
1
  {
2
2
  "name": "@sinch/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.6",
4
4
  "description": "Official Sinch CLI - Manage all Sinch products from your terminal",
5
- "main": "dist/index.js",
6
5
  "bin": {
7
6
  "sinch": "bin/sinch",
8
7
  "s": "bin/s"
9
8
  },
9
+ "files": [
10
+ "bin/",
11
+ "scripts/postinstall.js",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
10
15
  "scripts": {
11
- "start": "tsx src/index.ts",
12
- "dev": "tsx watch src/index.ts",
13
- "build": "tsc && node scripts/post-build.js && npm run build:binary",
14
- "build:prod": "tsup",
15
- "typecheck": "tsc --noEmit",
16
- "test": "jest --config jest.config.js && jest --config tests/e2e/jest.config.js",
17
- "test:unit": "jest --config jest.config.js",
18
- "test:e2e": "jest --config tests/e2e/jest.config.js",
19
- "test:e2e:staging": "jest --config tests/e2e/jest.config.js --env=staging",
20
- "prepublishOnly": "npm run build:prod",
21
- "build:binary": "bun build src/index.ts --compile --minify --outfile dist/sinch-bun.exe",
22
- "test:smoke": "node scripts/smoke-test.js",
23
- "setup": "npm run build && npm link && npm run build:binary && node scripts/setup-dev.js",
24
- "format": "prettier --write .",
25
- "format:check": "prettier --check .",
26
- "prepare": "husky"
16
+ "postinstall": "node scripts/postinstall.js"
27
17
  },
28
- "keywords": [],
29
18
  "author": "Sinch <support@sinch.com> (https://www.sinch.com)",
30
19
  "license": "MIT",
31
- "private": false,
32
20
  "publishConfig": {
33
21
  "access": "public",
34
22
  "registry": "https://registry.npmjs.org/"
35
23
  },
36
- "homepage": "https://www.sinch.com/products/apis/voice/",
37
- "files": [
38
- "dist/",
39
- "bin/",
40
- "scripts/",
41
- "README.md",
42
- "LICENSE"
43
- ],
44
- "dependencies": {
45
- "@inquirer/prompts": "^8.2.0",
46
- "@opentelemetry/api": "^1.9.0",
47
- "@opentelemetry/exporter-trace-otlp-http": "^0.212.0",
48
- "@opentelemetry/resources": "^2.5.1",
49
- "@opentelemetry/sdk-node": "^0.212.0",
50
- "@opentelemetry/semantic-conventions": "^1.39.0",
51
- "@opentui/core": "^0.1.87",
52
- "@sinch/sdk-core": "^1.2.1",
53
- "adm-zip": "^0.5.10",
54
- "axios": "^1.13.3",
55
- "axios-retry": "^4.5.0",
56
- "chalk": "^4.1.2",
57
- "cli-table3": "^0.6.3",
58
- "clipboardy": "^5.1.0",
59
- "commander": "^14.0.0",
60
- "form-data": "^4.0.0",
61
- "fs-extra": "^11.3.3",
62
- "google-libphonenumber": "^3.2.44",
63
- "stage-js": "^1.0.1"
64
- },
65
- "optionalDependencies": {
66
- "@opentui/core-linux-x64": "^0.1.87",
67
- "@opentui/core-linux-arm64": "^0.1.87",
68
- "@opentui/core-darwin-x64": "^0.1.87",
69
- "@opentui/core-darwin-arm64": "^0.1.87",
70
- "@opentui/core-win32-x64": "^0.1.87",
71
- "@opentui/core-win32-arm64": "^0.1.87"
72
- },
73
- "devDependencies": {
74
- "@types/adm-zip": "^0.5.7",
75
- "@types/fs-extra": "^11.0.4",
76
- "@types/google-libphonenumber": "^7.4.30",
77
- "@types/jest": "^30.0.0",
78
- "@types/node": "24.10.9",
79
- "execa": "^5.1.1",
80
- "husky": "^9.1.7",
81
- "jest": "^30.0.0",
82
- "lint-staged": "^16.2.7",
83
- "nodemon": "^3.0.1",
84
- "prettier": "^3.8.1",
85
- "ts-jest": "^29.4.6",
86
- "tsup": "^8.5.0",
87
- "tsx": "^4.20.4",
88
- "typescript": "^5.9.2"
89
- },
90
- "engines": {
91
- "node": ">=20.0.0",
92
- "npm": ">=9.0.0"
93
- },
94
- "lint-staged": {
95
- "*.{js,ts,json,md}": [
96
- "prettier --write"
97
- ]
98
- }
24
+ "homepage": "https://www.sinch.com/products/apis/voice/"
99
25
  }
@@ -14,9 +14,11 @@
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
  const os = require('os');
17
+ const https = require('https');
17
18
  const { spawn } = require('child_process');
18
19
 
19
20
  const SINCH_DIR = path.join(os.homedir(), '.sinch');
21
+ const PROJECT_ID = '72974711';
20
22
 
21
23
  // Keep in sync with COMPLETION_COMMANDS in src/index.ts.
22
24
  // The postAction hook overwrites completions.json on every CLI run, so drift self-heals.
@@ -31,6 +33,7 @@ const COMPLETION_COMMANDS = {
31
33
  fax: ['send', 'list', 'get', 'cancel', 'auth-status', 'status'],
32
34
  conversation: ['send', 'messages', 'contacts', 'conversations', 'apps', 'webhooks'],
33
35
  skills: ['install', 'list', 'uninstall', 'update'],
36
+ porting: ['check', 'config', 'orders', 'documents', 'activation'],
34
37
  config: ['--set', '--get', '--list'],
35
38
  completion: ['--shell', '--install'],
36
39
  };
@@ -170,6 +173,137 @@ function installBashZshCompletion() {
170
173
  }
171
174
  }
172
175
 
176
+ // --- Binary download ---
177
+
178
+ const PLATFORM_MAP = {
179
+ 'linux-x64': 'sinch-linux-x64',
180
+ 'linux-arm64': 'sinch-linux-arm64',
181
+ 'darwin-x64': 'sinch-darwin-x64',
182
+ 'darwin-arm64': 'sinch-darwin-arm64',
183
+ 'win32-x64': 'sinch-windows-x64.exe',
184
+ 'win32-arm64': 'sinch-windows-x64.exe',
185
+ };
186
+
187
+ function getTokenFromNpmrc() {
188
+ // Read GitLab token from global .npmrc (same token used for registry auth)
189
+ try {
190
+ const { execSync } = require('child_process');
191
+ const globalConfig = execSync('npm config get globalconfig', { encoding: 'utf8' }).trim();
192
+ const content = fs.readFileSync(globalConfig, 'utf8');
193
+ const match = content.match(
194
+ new RegExp(`//gitlab\\.com/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken=(.+)`)
195
+ );
196
+ return match ? match[1].trim() : '';
197
+ } catch {
198
+ return '';
199
+ }
200
+ }
201
+
202
+ function downloadFile(url, dest, token) {
203
+ return new Promise((resolve, reject) => {
204
+ const headers = {};
205
+ if (token) headers['PRIVATE-TOKEN'] = token;
206
+
207
+ const get = (reqUrl) => {
208
+ https
209
+ .get(reqUrl, { headers }, (res) => {
210
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
211
+ get(res.headers.location); // follow redirect
212
+ return;
213
+ }
214
+ if (res.statusCode !== 200) {
215
+ reject(new Error(`Download failed: ${res.statusCode} from ${reqUrl}`));
216
+ return;
217
+ }
218
+ const file = fs.createWriteStream(dest);
219
+ res.pipe(file);
220
+ file.on('finish', () => {
221
+ file.close();
222
+ resolve();
223
+ });
224
+ file.on('error', reject);
225
+ })
226
+ .on('error', reject);
227
+ };
228
+ get(url);
229
+ });
230
+ }
231
+
232
+ async function downloadBinary(version) {
233
+ const key = `${process.platform}-${process.arch}`;
234
+ const binName = PLATFORM_MAP[key];
235
+ if (!binName) return; // unsupported platform — skip silently
236
+
237
+ const pkgRoot = path.join(__dirname, '..');
238
+ const distDir = path.join(pkgRoot, 'dist');
239
+ const dest = path.join(distDir, binName);
240
+
241
+ // Skip if binary already exists (local dev build)
242
+ if (fs.existsSync(dest)) return;
243
+
244
+ fs.mkdirSync(distDir, { recursive: true });
245
+
246
+ const baseUrl = `https://gitlab.com/api/v4/projects/${PROJECT_ID}/packages/generic/sinch-cli/${version}/${binName}`;
247
+ const token = process.env.SINCH_GITLAB_TOKEN || process.env.GITLAB_TOKEN || getTokenFromNpmrc();
248
+
249
+ try {
250
+ console.log(`Downloading Sinch CLI ${version} for ${key}...`);
251
+ await downloadFile(baseUrl, dest, token);
252
+
253
+ // Make executable on Unix
254
+ if (process.platform !== 'win32') {
255
+ fs.chmodSync(dest, 0o755);
256
+ }
257
+ console.log('Sinch CLI binary downloaded.');
258
+ } catch (err) {
259
+ // Clean up partial download
260
+ try {
261
+ fs.unlinkSync(dest);
262
+ } catch {}
263
+ console.error(`Failed to download Sinch CLI binary: ${err.message}`);
264
+ console.error(
265
+ `You can download manually from: https://gitlab.com/sinch/sinch-projects/voice/functions/sinch-cli/-/releases`
266
+ );
267
+ }
268
+ }
269
+
270
+ /**
271
+ * On Windows, npm generates .cmd and .ps1 shims that try to run bin/sinch via
272
+ * /bin/sh (broken on Windows). Replace them with our native .cmd launcher that
273
+ * execs the Bun binary directly — zero Node.js overhead.
274
+ */
275
+ function fixWindowsShims() {
276
+ if (process.platform !== 'win32') return;
277
+ try {
278
+ const prefix = process.env.npm_config_prefix;
279
+ if (!prefix || !path.isAbsolute(prefix)) return;
280
+
281
+ const globalBinDir = prefix;
282
+ const pkgRoot = path.join(__dirname, '..');
283
+ const binaryPath = path.join(pkgRoot, 'dist', 'sinch-windows-x64.exe');
284
+
285
+ // Write a .cmd that uses the absolute path to the binary inside the npm package.
286
+ // Can't copy bin/sinch.cmd because %~dp0 would resolve to the global bin dir,
287
+ // not the package dir where the binary lives.
288
+ fs.writeFileSync(path.join(globalBinDir, 'sinch.cmd'), `@echo off\r\n"${binaryPath}" %*\r\n`);
289
+
290
+ // Remove .ps1 shim (broken — tries /bin/sh) and bash shim (not needed on Windows)
291
+ try {
292
+ fs.unlinkSync(path.join(globalBinDir, 'sinch.ps1'));
293
+ } catch {}
294
+ try {
295
+ fs.unlinkSync(path.join(globalBinDir, 'sinch'));
296
+ } catch {}
297
+
298
+ // Clean up orphaned sinch.exe from previous installToGlobalBin versions
299
+ try {
300
+ fs.unlinkSync(path.join(globalBinDir, 'sinch.exe'));
301
+ } catch {}
302
+ } catch {
303
+ // Non-fatal — npm's .cmd shim may still work for node-based fallback
304
+ }
305
+ }
306
+
173
307
  // --- Main ---
174
308
 
175
309
  async function main() {
@@ -182,6 +316,14 @@ async function main() {
182
316
  fs.mkdirSync(SINCH_DIR, { recursive: true });
183
317
 
184
318
  const version = getVersion();
319
+
320
+ // Download the platform binary
321
+ await downloadBinary(version);
322
+
323
+ // Replace npm's broken Windows shims with native .cmd launcher
324
+ fixWindowsShims();
325
+
326
+ // Set up shell completions
185
327
  writeCompletionsJson(version);
186
328
 
187
329
  if (process.platform === 'win32') {