@sinch/cli 0.3.3 โ 0.3.4
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 +4 -0
- package/dist/index.js +472 -621
- package/package.json +12 -7
- package/scripts/README-E2E.md +242 -0
- package/scripts/e2e-test.sh +155 -0
- package/scripts/post-build.js +36 -0
- package/scripts/postinstall.js +197 -0
- package/scripts/setup-dev.js +189 -0
- package/scripts/smoke-test.js +90 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { execSync, spawn } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const SINCH_BIN = path.join(os.homedir(), '.sinch', 'bin');
|
|
9
|
+
const BINARY_NAME = process.platform === 'win32' ? 'sinch.exe' : 'sinch';
|
|
10
|
+
const BINARY_SRC = path.join(__dirname, '..', 'dist', 'sinch-bun.exe');
|
|
11
|
+
const BINARY_DEST = path.join(SINCH_BIN, BINARY_NAME);
|
|
12
|
+
|
|
13
|
+
async function setup() {
|
|
14
|
+
console.log('\n๐ง Setting up Sinch CLI development environment\n');
|
|
15
|
+
|
|
16
|
+
// 1. Copy Bun binary to ~/.sinch/bin/
|
|
17
|
+
console.log('๐ฆ Installing Bun binary...');
|
|
18
|
+
await fs.ensureDir(SINCH_BIN);
|
|
19
|
+
|
|
20
|
+
if (await fs.pathExists(BINARY_SRC)) {
|
|
21
|
+
await fs.copy(BINARY_SRC, BINARY_DEST, { overwrite: true });
|
|
22
|
+
if (process.platform !== 'win32') {
|
|
23
|
+
await fs.chmod(BINARY_DEST, 0o755);
|
|
24
|
+
}
|
|
25
|
+
console.log(` โ
Installed to ${BINARY_DEST}`);
|
|
26
|
+
} else {
|
|
27
|
+
console.log(' โ ๏ธ Bun binary not found โ run "npm run build:binary" first');
|
|
28
|
+
console.log(` Expected at: ${BINARY_SRC}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. Generate PowerShell profile additions
|
|
32
|
+
if (process.platform === 'win32') {
|
|
33
|
+
await setupPowerShell();
|
|
34
|
+
} else {
|
|
35
|
+
await setupShell();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. Install shell completions
|
|
39
|
+
console.log('\n๐ง Installing shell completions...');
|
|
40
|
+
try {
|
|
41
|
+
await installCompletions();
|
|
42
|
+
console.log(' โ
Completions installed');
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.log(` โ ๏ธ Completion install failed: ${e.message}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 4. Smoke test
|
|
48
|
+
console.log('\n๐งช Smoke test...');
|
|
49
|
+
try {
|
|
50
|
+
const version = execSync(`"${BINARY_DEST}" --version`, { encoding: 'utf8' }).trim();
|
|
51
|
+
console.log(` โ
sinch binary v${version}`);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.log(` โ ๏ธ Binary smoke test failed: ${e.message}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const devVersion = execSync('sinch --version', { encoding: 'utf8' }).trim();
|
|
58
|
+
console.log(` โ
sinch dev (npm link) v${devVersion}`);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.log(` โ ๏ธ npm link not set up: ${e.message}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log('\nโจ Setup complete!\n');
|
|
64
|
+
console.log('Commands:');
|
|
65
|
+
console.log(' s โ Fast Bun binary (for daily use)');
|
|
66
|
+
console.log(' sf <cmd> โ Shortcut for "s functions <cmd>"');
|
|
67
|
+
console.log(' sd <cmd> โ Dev version via npm link (for CLI development)');
|
|
68
|
+
console.log(' sinch <cmd> โ Same as sd (npm-linked)');
|
|
69
|
+
console.log('');
|
|
70
|
+
|
|
71
|
+
if (process.platform === 'win32') {
|
|
72
|
+
console.log('โก Restart PowerShell or run: . $PROFILE');
|
|
73
|
+
} else {
|
|
74
|
+
console.log('โก Restart your shell or run: source ~/.bashrc');
|
|
75
|
+
}
|
|
76
|
+
console.log('');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function setupPowerShell() {
|
|
80
|
+
console.log('\n๐ฅ๏ธ Setting up PowerShell aliases...');
|
|
81
|
+
|
|
82
|
+
const profileSnippet = `
|
|
83
|
+
# โโ Sinch CLI aliases (auto-generated by sinch-cli setup) โโ
|
|
84
|
+
function s { & "$env:USERPROFILE\\.sinch\\bin\\sinch.exe" @args }
|
|
85
|
+
function sf { & "$env:USERPROFILE\\.sinch\\bin\\sinch.exe" functions @args }
|
|
86
|
+
function sd { & sinch @args }
|
|
87
|
+
# โโ End Sinch CLI aliases โโ
|
|
88
|
+
`.trim();
|
|
89
|
+
|
|
90
|
+
// Find PowerShell profile
|
|
91
|
+
const profilePath = await getPowerShellProfilePath();
|
|
92
|
+
|
|
93
|
+
if (!profilePath) {
|
|
94
|
+
console.log(' โ ๏ธ Could not detect PowerShell profile path');
|
|
95
|
+
console.log(' Add these to your PowerShell profile manually:\n');
|
|
96
|
+
console.log(profileSnippet);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Read existing profile
|
|
101
|
+
let profileContent = '';
|
|
102
|
+
if (await fs.pathExists(profilePath)) {
|
|
103
|
+
profileContent = await fs.readFile(profilePath, 'utf8');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check if already installed
|
|
107
|
+
if (profileContent.includes('Sinch CLI aliases')) {
|
|
108
|
+
// Replace existing block
|
|
109
|
+
const replaced = profileContent.replace(
|
|
110
|
+
/# โโ Sinch CLI aliases.*?# โโ End Sinch CLI aliases โโ/s,
|
|
111
|
+
profileSnippet
|
|
112
|
+
);
|
|
113
|
+
await fs.writeFile(profilePath, replaced);
|
|
114
|
+
console.log(` โ
Updated aliases in ${profilePath}`);
|
|
115
|
+
} else {
|
|
116
|
+
// Append
|
|
117
|
+
await fs.writeFile(profilePath, profileContent + '\n\n' + profileSnippet + '\n');
|
|
118
|
+
console.log(` โ
Added aliases to ${profilePath}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function setupShell() {
|
|
123
|
+
console.log('\n๐ฅ๏ธ Setting up shell aliases...');
|
|
124
|
+
|
|
125
|
+
const snippet = `
|
|
126
|
+
# โโ Sinch CLI aliases (auto-generated by sinch-cli setup) โโ
|
|
127
|
+
alias s='$HOME/.sinch/bin/sinch'
|
|
128
|
+
sf() { $HOME/.sinch/bin/sinch functions "$@"; }
|
|
129
|
+
alias sd='sinch'
|
|
130
|
+
# โโ End Sinch CLI aliases โโ
|
|
131
|
+
`.trim();
|
|
132
|
+
|
|
133
|
+
// Try .bashrc, then .zshrc
|
|
134
|
+
const rcFile = (await fs.pathExists(path.join(os.homedir(), '.zshrc')))
|
|
135
|
+
? path.join(os.homedir(), '.zshrc')
|
|
136
|
+
: path.join(os.homedir(), '.bashrc');
|
|
137
|
+
|
|
138
|
+
let content = '';
|
|
139
|
+
if (await fs.pathExists(rcFile)) {
|
|
140
|
+
content = await fs.readFile(rcFile, 'utf8');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (content.includes('Sinch CLI aliases')) {
|
|
144
|
+
const replaced = content.replace(
|
|
145
|
+
/# โโ Sinch CLI aliases.*?# โโ End Sinch CLI aliases โโ/s,
|
|
146
|
+
snippet
|
|
147
|
+
);
|
|
148
|
+
await fs.writeFile(rcFile, replaced);
|
|
149
|
+
console.log(` โ
Updated aliases in ${rcFile}`);
|
|
150
|
+
} else {
|
|
151
|
+
await fs.writeFile(rcFile, content + '\n\n' + snippet + '\n');
|
|
152
|
+
console.log(` โ
Added aliases to ${rcFile}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function installCompletions() {
|
|
157
|
+
execSync('node scripts/postinstall.js', {
|
|
158
|
+
cwd: path.join(__dirname, '..'),
|
|
159
|
+
env: { ...process.env, SINCH_FORCE_POSTINSTALL: '1' },
|
|
160
|
+
stdio: 'pipe',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getPowerShellProfilePath() {
|
|
165
|
+
return new Promise((resolve) => {
|
|
166
|
+
// Try pwsh first, then powershell
|
|
167
|
+
const tryShell = (cmd) => {
|
|
168
|
+
return new Promise((res) => {
|
|
169
|
+
const ps = spawn(cmd, ['-NoProfile', '-Command', '$PROFILE'], {
|
|
170
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
171
|
+
});
|
|
172
|
+
let out = '';
|
|
173
|
+
ps.stdout.on('data', (d) => (out += d.toString()));
|
|
174
|
+
ps.on('close', (code) => res(code === 0 ? out.trim() : null));
|
|
175
|
+
ps.on('error', () => res(null));
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
tryShell('pwsh').then((p) => {
|
|
180
|
+
if (p) return resolve(p);
|
|
181
|
+
tryShell('powershell').then((p2) => resolve(p2));
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
setup().catch((err) => {
|
|
187
|
+
console.error('Setup failed:', err.message);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smoke test for the Bun-compiled binary.
|
|
5
|
+
* Verifies the binary works for key code paths that could differ from Node.js.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
const rawBinary =
|
|
13
|
+
process.argv[2] ||
|
|
14
|
+
path.join(os.homedir(), '.sinch', 'bin', process.platform === 'win32' ? 'sinch.exe' : 'sinch');
|
|
15
|
+
const BINARY = path.resolve(rawBinary);
|
|
16
|
+
|
|
17
|
+
let passed = 0;
|
|
18
|
+
let failed = 0;
|
|
19
|
+
|
|
20
|
+
function test(name, fn) {
|
|
21
|
+
try {
|
|
22
|
+
fn();
|
|
23
|
+
console.log(` โ
${name}`);
|
|
24
|
+
passed++;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.log(` โ ${name}: ${e.message}`);
|
|
27
|
+
failed++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function run(args) {
|
|
32
|
+
return execSync(`"${BINARY}" ${args}`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
timeout: 10000,
|
|
35
|
+
env: { ...process.env, CI: '1' },
|
|
36
|
+
}).trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(`\n๐งช Smoke testing: ${BINARY}\n`);
|
|
40
|
+
|
|
41
|
+
test('--version returns semver', () => {
|
|
42
|
+
const version = run('--version');
|
|
43
|
+
if (!/^\d+\.\d+\.\d+/.test(version)) {
|
|
44
|
+
throw new Error(`Got: ${version}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('--help shows all top-level commands', () => {
|
|
49
|
+
const help = run('--help');
|
|
50
|
+
const required = ['functions', 'templates', 'voice', 'auth', 'config', 'fax', 'sip', 'numbers'];
|
|
51
|
+
for (const cmd of required) {
|
|
52
|
+
if (!help.includes(cmd)) {
|
|
53
|
+
throw new Error(`Missing command: ${cmd}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('functions --help shows subcommands', () => {
|
|
59
|
+
const help = run('functions --help');
|
|
60
|
+
const required = ['init', 'list', 'deploy', 'dev', 'status', 'logs'];
|
|
61
|
+
for (const cmd of required) {
|
|
62
|
+
if (!help.includes(cmd)) {
|
|
63
|
+
throw new Error(`Missing subcommand: ${cmd}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('fax --help shows subcommands', () => {
|
|
69
|
+
const help = run('fax --help');
|
|
70
|
+
if (!help.includes('send') || !help.includes('list')) {
|
|
71
|
+
throw new Error('Missing fax subcommands');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('sip --help shows subcommands', () => {
|
|
76
|
+
const help = run('sip --help');
|
|
77
|
+
if (!help.includes('trunks') || !help.includes('endpoints')) {
|
|
78
|
+
throw new Error('Missing sip subcommands');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('config --help shows subcommands', () => {
|
|
83
|
+
const help = run('config --help');
|
|
84
|
+
if (!help.includes('set') || !help.includes('get') || !help.includes('profile')) {
|
|
85
|
+
throw new Error('Missing config subcommands');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
console.log(`\n${passed} passed, ${failed} failed\n`);
|
|
90
|
+
process.exit(failed > 0 ? 1 : 0);
|