voiceai-sdk
Version:
Official SDK for SLNG.AI Voice API - Text-to-Speech, Speech-to-Text, and LLM services
148 lines (126 loc) • 4.54 kB
JavaScript
/**
* Anonymous telemetry for SDK install tracking
* Runs on postinstall to help improve the SDK
*
* Privacy:
* - No personal information collected
* - User agent hashed for anonymity
* - Fails silently with no user impact
* - Can be disabled with SLNG_DISABLE_TELEMETRY=1
*/
const https = require('https');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
// Exit early if telemetry disabled
if (process.env.SLNG_DISABLE_TELEMETRY === '1' || process.env.SLNG_DISABLE_TELEMETRY === 'true') {
process.exit(0);
}
// Exit early in CI environments (common CI environment variables)
const CI_VARS = ['CI', 'CONTINUOUS_INTEGRATION', 'BUILD_ID', 'GITHUB_ACTIONS', 'GITLAB_CI', 'TRAVIS', 'CIRCLECI'];
if (CI_VARS.some(env => process.env[env])) {
process.exit(0);
}
async function trackInstall() {
try {
// Get package version dynamically
const packageJsonPath = path.join(__dirname, '..', 'package.json');
let version = '1.0.0'; // fallback
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
version = packageJson.version || version;
} catch (e) {
// Use fallback version if package.json read fails
}
// Create project fingerprint from user agent and install path
const userAgent = process.env.npm_config_user_agent || 'unknown';
const installPath = process.cwd();
const fingerprint = crypto
.createHash('sha256')
.update(userAgent + installPath)
.digest('hex')
.substring(0, 16); // Use first 16 chars for brevity
// Check if this is a repeat install (simple file-based tracking)
const telemetryDir = path.join(__dirname, '..', '.telemetry');
const installMarkerPath = path.join(telemetryDir, 'install.json');
let isFirstInstall = true;
let previousInstalls = 0;
let firstInstallDate = new Date().toISOString();
try {
if (fs.existsSync(installMarkerPath)) {
const installData = JSON.parse(fs.readFileSync(installMarkerPath, 'utf8'));
isFirstInstall = false;
previousInstalls = installData.count || 0;
firstInstallDate = installData.firstInstall || firstInstallDate;
}
} catch (e) {
// Treat as first install if marker file is corrupted
}
// Prepare telemetry payload
const payload = {
event: 'sdk_install',
sdk_version: version,
project_id: fingerprint,
is_first_install: isFirstInstall,
install_count: previousInstalls + 1,
first_install_date: firstInstallDate,
install_date: new Date().toISOString(),
node_version: process.version,
platform: process.platform,
arch: process.arch,
// Basic package manager detection
package_manager: userAgent.includes('yarn') ? 'yarn' :
userAgent.includes('pnpm') ? 'pnpm' : 'npm'
};
// Send telemetry (non-blocking)
const postData = JSON.stringify(payload);
const options = {
hostname: 'app.slng.ai',
path: '/api/sdk-install',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'User-Agent': `voiceai-sdk/${version}`
},
timeout: 3000 // 3 second timeout
};
const req = https.request(options, (res) => {
// Consume response to prevent hanging
res.on('data', () => {});
res.on('end', () => {});
});
req.on('error', () => {
// Fail silently - don't impact user experience
});
req.on('timeout', () => {
req.destroy();
});
req.write(postData);
req.end();
// Update install marker (create directory if needed)
try {
if (!fs.existsSync(telemetryDir)) {
fs.mkdirSync(telemetryDir, { recursive: true });
}
const installData = {
count: previousInstalls + 1,
firstInstall: firstInstallDate,
lastInstall: new Date().toISOString(),
version: version
};
fs.writeFileSync(installMarkerPath, JSON.stringify(installData, null, 2));
} catch (e) {
// Fail silently if we can't write marker file
}
} catch (error) {
// Fail silently - telemetry should never break the user's workflow
}
}
// Run telemetry with additional safety
setTimeout(() => {
trackInstall().catch(() => {
// Final safety net - absolutely no errors should escape
});
}, 100); // Small delay to ensure npm install completes