node-nim 10.9.70 → 10.9.71-beta.87
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/package.json +6 -6
- package/script/download-sdk.js +319 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-nim",
|
|
3
|
-
"version": "10.9.
|
|
3
|
+
"version": "10.9.71-beta.87",
|
|
4
4
|
"description": "NetEase IM nodejs wrapper based on NetEase IM C++ SDK",
|
|
5
5
|
"main": "dist/node-nim.js",
|
|
6
6
|
"bin": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"build_ts": "rimraf ./dist && rimraf ./types && tsc",
|
|
21
21
|
"build_html_doc": "typedoc --plugin typedoc-github-theme",
|
|
22
22
|
"prepublishOnly": "npm run build_ts",
|
|
23
|
-
"
|
|
24
|
-
"download_sdk": "node
|
|
23
|
+
"postinstall": "node script/download-sdk.js",
|
|
24
|
+
"download_sdk": "node script/download-sdk.js",
|
|
25
25
|
"publish_to_netease_npm": "node script/publish-to-netease-npm.js http://npm.netease.im/",
|
|
26
26
|
"publish_to_npmjs": "node script/publish-to-netease-npm.js https://registry.npmjs.org",
|
|
27
27
|
"test": "npx cross-env BABEL_ENV=test mocha"
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"axios": "^1.6.2",
|
|
36
37
|
"compare-versions": "^4.1.4",
|
|
37
|
-
"
|
|
38
|
-
"eventemitter3": "^4.0.7"
|
|
39
|
-
"node-fetch": "^2.6.9"
|
|
38
|
+
"decompress": "^4.2.1",
|
|
39
|
+
"eventemitter3": "^4.0.7"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@babel/preset-env": "^7.24.0",
|
package/script/download-sdk.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const axios = require('axios')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const compareVersions = require('compare-versions')
|
|
5
|
-
const
|
|
5
|
+
const decompress = require('decompress')
|
|
6
6
|
|
|
7
7
|
// Global variables
|
|
8
8
|
const default_arch = 'universal'
|
|
@@ -14,56 +14,136 @@ const product = 'nim'
|
|
|
14
14
|
const savePath = path.join(__dirname, '..', 'temporary')
|
|
15
15
|
|
|
16
16
|
if (process.env.npm_config_ignoredownloadsdk) {
|
|
17
|
-
console.log('
|
|
17
|
+
console.log('[node-nim] Ignore download product')
|
|
18
18
|
process.exit(0)
|
|
19
19
|
}
|
|
20
20
|
let version
|
|
21
|
-
let downloadUrl = process.env.npm_config_nimsdkurl
|
|
22
21
|
if (process.env.npm_package_version) {
|
|
23
22
|
version = process.env.npm_package_version.split('-')[0]
|
|
24
23
|
}
|
|
25
24
|
if (process.env.npm_config_nimsdkversion) {
|
|
26
25
|
version = process.env.npm_config_nimsdkversion
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
|
|
28
|
+
// Simple logger that works with npm postinstall
|
|
29
|
+
// Use console.error to ensure output is visible when installed as dependency
|
|
30
|
+
// npm captures stdout but is less aggressive with stderr
|
|
31
|
+
function log(message) {
|
|
32
|
+
console.error(message)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Progress bar utility - real-time progress bar
|
|
36
|
+
function createProgressBar(total) {
|
|
37
|
+
let lastUpdate = 0
|
|
38
|
+
const updateInterval = 100 // Update every 100ms
|
|
39
|
+
return (loaded) => {
|
|
40
|
+
const now = Date.now()
|
|
41
|
+
const percent = Math.floor((loaded * 100) / total)
|
|
42
|
+
// Update based on time interval or when complete
|
|
43
|
+
if (now - lastUpdate < updateInterval && loaded < total) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
lastUpdate = now
|
|
47
|
+
const size = formatBytes(loaded)
|
|
48
|
+
const totalSize = formatBytes(total)
|
|
49
|
+
// Create progress bar
|
|
50
|
+
const barLength = 30
|
|
51
|
+
const filledLength = Math.floor((barLength * loaded) / total)
|
|
52
|
+
const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength)
|
|
53
|
+
// Use \r to overwrite the same line
|
|
54
|
+
process.stderr.write(`\r[node-nim] ⬇ ${bar} ${percent}% (${size}/${totalSize})`)
|
|
55
|
+
// Print newline when complete
|
|
56
|
+
if (loaded >= total) {
|
|
57
|
+
process.stderr.write('\n')
|
|
58
|
+
log(` ✅ Download complete`)
|
|
59
|
+
}
|
|
31
60
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Format bytes to human readable format
|
|
64
|
+
function formatBytes(bytes) {
|
|
65
|
+
if (bytes === 0) return '0 B'
|
|
66
|
+
const k = 1024
|
|
67
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
68
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
69
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Download and extract function using axios + decompress
|
|
73
|
+
async function downloadAndExtract(url, destination) {
|
|
74
|
+
if (!fs.existsSync(destination)) {
|
|
75
|
+
fs.mkdirSync(destination, { recursive: true })
|
|
76
|
+
}
|
|
77
|
+
// Determine archive file name from URL
|
|
78
|
+
const urlPath = new URL(url).pathname
|
|
79
|
+
const fileName = path.basename(urlPath) || 'temp-archive'
|
|
80
|
+
const archivePath = path.join(destination, fileName)
|
|
81
|
+
log(` 📥 Starting download...`)
|
|
82
|
+
try {
|
|
83
|
+
// Download with progress tracking
|
|
84
|
+
const response = await axios({
|
|
85
|
+
method: 'GET',
|
|
86
|
+
url: url,
|
|
87
|
+
responseType: 'stream',
|
|
88
|
+
timeout: 300000, // 5 minutes timeout
|
|
89
|
+
onDownloadProgress: (progressEvent) => {
|
|
90
|
+
if (progressEvent.total) {
|
|
91
|
+
const progressBar = createProgressBar(progressEvent.total)
|
|
92
|
+
progressBar(progressEvent.loaded)
|
|
93
|
+
}
|
|
47
94
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
95
|
+
})
|
|
96
|
+
// Save the downloaded file
|
|
97
|
+
const writeStream = fs.createWriteStream(archivePath)
|
|
98
|
+
response.data.pipe(writeStream)
|
|
99
|
+
// Wait for download to complete
|
|
100
|
+
await new Promise((resolve, reject) => {
|
|
101
|
+
writeStream.on('finish', resolve)
|
|
102
|
+
writeStream.on('error', reject)
|
|
103
|
+
response.data.on('error', reject)
|
|
104
|
+
})
|
|
105
|
+
log(` 📦 Extracting archive...`)
|
|
106
|
+
// Extract using decompress (auto-detects format)
|
|
107
|
+
await decompress(archivePath, destination, {
|
|
108
|
+
filter: (file) => {
|
|
109
|
+
// Filter out macOS hidden files (._files) and __MACOSX folders
|
|
110
|
+
const filePath = file.path
|
|
111
|
+
return !filePath.includes('._') &&
|
|
112
|
+
!filePath.includes('__MACOSX') &&
|
|
113
|
+
!filePath.startsWith('._')
|
|
114
|
+
},
|
|
115
|
+
map: (file) => {
|
|
116
|
+
// Remove any leading directory if needed
|
|
117
|
+
file.path = file.path.replace(/^[^\/]+\//, '')
|
|
118
|
+
return file
|
|
54
119
|
}
|
|
55
120
|
})
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
121
|
+
log(` ✅ Extraction complete`)
|
|
122
|
+
// Clean up the temporary archive file
|
|
123
|
+
fs.unlinkSync(archivePath)
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// Clean up on error
|
|
126
|
+
if (fs.existsSync(archivePath)) {
|
|
127
|
+
fs.unlinkSync(archivePath)
|
|
59
128
|
}
|
|
60
|
-
|
|
129
|
+
throw error
|
|
61
130
|
}
|
|
131
|
+
}
|
|
132
|
+
async function downloadSDK(customPackageUrl) {
|
|
133
|
+
// Use custom URL if provided, otherwise fetch from official server
|
|
134
|
+
let downloadUrl = customPackageUrl
|
|
62
135
|
if (!downloadUrl) {
|
|
63
|
-
|
|
64
|
-
|
|
136
|
+
// Fetch package list from official server
|
|
137
|
+
const res = await axios.get('https://admin.netease.im/public-service/free/publish/list')
|
|
138
|
+
const publishData = res.data.data[channel]
|
|
139
|
+
// Find package URL for specified version or latest
|
|
140
|
+
downloadUrl = findPackageUrl(publishData, version, platform, arch, product)
|
|
141
|
+
if (!downloadUrl) {
|
|
142
|
+
log(` ❌ ERROR: Package not found for ${platform} (${arch})`)
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
log(` 🚀 Preparing to download package for ${platform} (${arch})`)
|
|
65
146
|
}
|
|
66
|
-
console.info(`[node-nim] Downloading prebuilt SDK from ${downloadUrl} to ${savePath}`)
|
|
67
147
|
// remove temporary download folder and target folder
|
|
68
148
|
const target = path.join(__dirname, '..', 'build', 'Release')
|
|
69
149
|
if (fs.existsSync(savePath)) {
|
|
@@ -74,12 +154,7 @@ async function downloadSDK(custom_sdk_url) {
|
|
|
74
154
|
}
|
|
75
155
|
// download sdk
|
|
76
156
|
try {
|
|
77
|
-
await
|
|
78
|
-
extract: true,
|
|
79
|
-
filter: (file) => {
|
|
80
|
-
return !file.path.includes('._')
|
|
81
|
-
}
|
|
82
|
-
})
|
|
157
|
+
await downloadAndExtract(downloadUrl, savePath)
|
|
83
158
|
// create build/Release folder
|
|
84
159
|
if (!fs.existsSync(target)) {
|
|
85
160
|
fs.mkdirSync(target, { recursive: true })
|
|
@@ -88,20 +163,217 @@ async function downloadSDK(custom_sdk_url) {
|
|
|
88
163
|
const from = path.join(savePath, platform === 'win32' ? 'bin' : 'lib')
|
|
89
164
|
const files = fs.readdirSync(from)
|
|
90
165
|
files.forEach((file) => {
|
|
91
|
-
|
|
166
|
+
log(` 📁 Installing ${file}`)
|
|
92
167
|
fs.renameSync(path.join(from, file), path.join(target, file))
|
|
93
168
|
})
|
|
94
169
|
// remove temporary download folder
|
|
95
170
|
fs.rmSync(savePath, { recursive: true })
|
|
96
|
-
|
|
171
|
+
log(` ✅ Package installation complete!`)
|
|
97
172
|
} catch (err) {
|
|
98
|
-
|
|
173
|
+
log(` ❌ ERROR: ${err.message}`)
|
|
174
|
+
throw err
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Helper function to find package download URL from publish data
|
|
178
|
+
function findPackageUrl(publishData, targetVersion, platform, arch, product) {
|
|
179
|
+
let latestVersion = '0.0.0'
|
|
180
|
+
let latestDownloadUrl = ''
|
|
181
|
+
let targetDownloadUrl = ''
|
|
182
|
+
// Check if package matches current platform/arch
|
|
183
|
+
const isMatchingPackage = (member) => {
|
|
184
|
+
return member.filename.includes(product) &&
|
|
185
|
+
member.filename.includes(platform) &&
|
|
186
|
+
member.filename.includes(arch)
|
|
187
|
+
}
|
|
188
|
+
// Iterate through all versions
|
|
189
|
+
Object.keys(publishData).forEach((versionKey) => {
|
|
190
|
+
const versionSDKs = publishData[versionKey]
|
|
191
|
+
// Track latest version
|
|
192
|
+
if (compareVersions.compare(latestVersion, versionKey, '<')) {
|
|
193
|
+
const latestPackage = versionSDKs.find(isMatchingPackage)
|
|
194
|
+
if (latestPackage) {
|
|
195
|
+
latestVersion = versionKey
|
|
196
|
+
latestDownloadUrl = latestPackage.cdnlink
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Find target version
|
|
200
|
+
if (targetVersion === versionKey) {
|
|
201
|
+
const targetPackage = versionSDKs.find(isMatchingPackage)
|
|
202
|
+
if (targetPackage) {
|
|
203
|
+
targetDownloadUrl = targetPackage.cdnlink
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
// Use target version if found, otherwise fallback to latest
|
|
208
|
+
if (targetDownloadUrl) {
|
|
209
|
+
return targetDownloadUrl
|
|
210
|
+
}
|
|
211
|
+
if (latestDownloadUrl) {
|
|
212
|
+
log(` ⚠️ Version ${targetVersion} not found, using latest version ${latestVersion}`)
|
|
213
|
+
return latestDownloadUrl
|
|
99
214
|
}
|
|
215
|
+
return null
|
|
100
216
|
}
|
|
217
|
+
|
|
218
|
+
// Parse directory listing from HTTP server
|
|
219
|
+
async function parseDirectoryListing(url) {
|
|
220
|
+
try {
|
|
221
|
+
const response = await axios.get(url)
|
|
222
|
+
const html = response.data
|
|
223
|
+
// Extract directory/file names from href attributes
|
|
224
|
+
// Matches patterns like: href="dirname/" or href="filename.tar.gz"
|
|
225
|
+
const hrefRegex = /href="([^"]+)"/g
|
|
226
|
+
const items = []
|
|
227
|
+
let match
|
|
228
|
+
while ((match = hrefRegex.exec(html)) !== null) {
|
|
229
|
+
const item = match[1]
|
|
230
|
+
// Skip parent directory and absolute URLs
|
|
231
|
+
if (item !== '../' && !item.startsWith('http') && !item.startsWith('/')) {
|
|
232
|
+
items.push(item)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return items
|
|
236
|
+
} catch (error) {
|
|
237
|
+
throw new Error(`Failed to parse directory listing from ${url}: ${error.message}`)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Find latest build number from directory listing
|
|
242
|
+
async function findLatestBuild(baseUrl, branch) {
|
|
243
|
+
const branchUrl = `${baseUrl}/${branch}/`
|
|
244
|
+
log(` 🔍 Searching for latest build in ${branchUrl}`)
|
|
245
|
+
const items = await parseDirectoryListing(branchUrl)
|
|
246
|
+
// Filter directories (end with /) and extract build numbers
|
|
247
|
+
const buildNumbers = items
|
|
248
|
+
.filter(item => item.endsWith('/'))
|
|
249
|
+
.map(item => parseInt(item.replace('/', '')))
|
|
250
|
+
.filter(num => !isNaN(num))
|
|
251
|
+
.sort((a, b) => b - a) // Sort in descending order
|
|
252
|
+
|
|
253
|
+
if (buildNumbers.length === 0) {
|
|
254
|
+
throw new Error(`No build directories found in ${branchUrl}`)
|
|
255
|
+
}
|
|
256
|
+
// Return all build numbers sorted by latest first
|
|
257
|
+
return buildNumbers
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Find latest build that contains the requested platform package
|
|
261
|
+
async function findLatestBuildWithPackage(baseUrl, branch, nodePlatform, nodeArch) {
|
|
262
|
+
const buildNumbers = await findLatestBuild(baseUrl, branch)
|
|
263
|
+
|
|
264
|
+
// Try builds from latest to oldest
|
|
265
|
+
for (const buildNumber of buildNumbers) {
|
|
266
|
+
try {
|
|
267
|
+
const buildUrl = `${baseUrl}/${branch}/${buildNumber}/`
|
|
268
|
+
log(` 🔍 Checking build ${buildNumber} for ${nodePlatform}-${nodeArch} package...`)
|
|
269
|
+
|
|
270
|
+
// Try to find package in this build
|
|
271
|
+
const packageUrl = await findPackage(buildUrl, nodePlatform, nodeArch)
|
|
272
|
+
log(` ✅ Found package in build ${buildNumber}`)
|
|
273
|
+
return { buildNumber, packageUrl }
|
|
274
|
+
} catch (error) {
|
|
275
|
+
log(` ⚠️ Build ${buildNumber} does not contain ${nodePlatform}-${nodeArch} package, trying previous build...`)
|
|
276
|
+
// Continue to next build
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
throw new Error(`No build found with ${nodePlatform}-${nodeArch} package for branch ${branch}`)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Map Node.js platform/arch to SDK directory format
|
|
284
|
+
function getPlatformArchDir(nodePlatform, nodeArch) {
|
|
285
|
+
// darwin-arm64, darwin-x64, win32-ia32, linux-arm64, linux-x64
|
|
286
|
+
let platform = nodePlatform
|
|
287
|
+
let arch = nodeArch
|
|
288
|
+
// Map Node.js arch to package arch format
|
|
289
|
+
if (nodePlatform === 'darwin') {
|
|
290
|
+
arch = nodeArch === 'arm64' ? 'arm64' : 'x64'
|
|
291
|
+
} else if (nodePlatform === 'win32') {
|
|
292
|
+
platform = 'win32'
|
|
293
|
+
arch = nodeArch === 'ia32' ? 'ia32' : nodeArch === 'x64' ? 'x64' : nodeArch
|
|
294
|
+
} else if (nodePlatform === 'linux') {
|
|
295
|
+
arch = nodeArch === 'arm64' ? 'arm64' : 'x64'
|
|
296
|
+
}
|
|
297
|
+
return `${platform}-${arch}/`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Find package from directory listing
|
|
301
|
+
async function findPackage(buildUrl, nodePlatform, nodeArch) {
|
|
302
|
+
const platformArchDir = getPlatformArchDir(nodePlatform, nodeArch)
|
|
303
|
+
const fullUrl = `${buildUrl}${platformArchDir}`
|
|
304
|
+
log(` 🔍 Searching for package in ${fullUrl}`)
|
|
305
|
+
const items = await parseDirectoryListing(fullUrl)
|
|
306
|
+
// Filter tar.gz files and exclude symbol files
|
|
307
|
+
const packageFiles = items.filter(item => {
|
|
308
|
+
if (!item.endsWith('.tar.gz') && !item.endsWith('.zip')) {
|
|
309
|
+
return false
|
|
310
|
+
}
|
|
311
|
+
// Exclude symbol files based on platform
|
|
312
|
+
if (nodePlatform === 'darwin' && item.includes('-dSYM')) {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
if (nodePlatform === 'win32' && item.includes('-PDB')) {
|
|
316
|
+
return false
|
|
317
|
+
}
|
|
318
|
+
if (nodePlatform === 'linux' && item.includes('-with-symbol')) {
|
|
319
|
+
return false
|
|
320
|
+
}
|
|
321
|
+
// Must start with nim- prefix
|
|
322
|
+
return item.startsWith('nim-')
|
|
323
|
+
})
|
|
324
|
+
if (packageFiles.length === 0) {
|
|
325
|
+
throw new Error(`No package found in ${fullUrl}`)
|
|
326
|
+
}
|
|
327
|
+
// If multiple files, prefer the first one (they should be the same package, just different compression)
|
|
328
|
+
const packageFile = packageFiles[0]
|
|
329
|
+
log(` ✅ Found package: ${packageFile}`)
|
|
330
|
+
return `${fullUrl}${packageFile}`
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Build package URL from branch name
|
|
334
|
+
async function buildPackageUrlFromBranch(branch, nodePlatform, nodeArch) {
|
|
335
|
+
// Base64 encoded internal server URL (decode when needed)
|
|
336
|
+
const encodedBaseUrl = 'aHR0cDovLzEwLjIxOS4yNS4xMjc6ODgvSU0tTmF0aXZlL0Rlc2t0b3A='
|
|
337
|
+
const baseUrl = Buffer.from(encodedBaseUrl, 'base64').toString('utf-8')
|
|
338
|
+
log(` 🌿 Resolving package URL for branch: ${branch}`)
|
|
339
|
+
|
|
340
|
+
// Find latest build that contains the requested platform package
|
|
341
|
+
// This will automatically fallback to previous builds if the latest doesn't have the package
|
|
342
|
+
const { buildNumber, packageUrl } = await findLatestBuildWithPackage(baseUrl, branch, nodePlatform, nodeArch)
|
|
343
|
+
|
|
344
|
+
return packageUrl
|
|
345
|
+
}
|
|
346
|
+
|
|
101
347
|
if (require.main === module) {
|
|
102
348
|
const args = process.argv
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
349
|
+
const urlIndex = args.indexOf('--nimSdkUrl')
|
|
350
|
+
const branchIndex = args.indexOf('--branch')
|
|
351
|
+
;(async () => {
|
|
352
|
+
try {
|
|
353
|
+
let url
|
|
354
|
+
let downloadUrl = process.env.npm_config_nimsdkurl
|
|
355
|
+
let branch = process.env.npm_config_branch
|
|
356
|
+
|
|
357
|
+
// 优先使用环境变量,然后才是命令行参数
|
|
358
|
+
if (branch || (branchIndex !== -1 && args[branchIndex + 1])) {
|
|
359
|
+
// Build URL from branch name
|
|
360
|
+
branch = branch || args[branchIndex + 1]
|
|
361
|
+
url = await buildPackageUrlFromBranch(branch, platform, process.arch)
|
|
362
|
+
} else if (downloadUrl || (urlIndex !== -1 && args[urlIndex + 1])) {
|
|
363
|
+
// Use provided URL directly
|
|
364
|
+
url = downloadUrl || args[urlIndex + 1]
|
|
365
|
+
// If URL is a directory path (ends with /), find the package
|
|
366
|
+
if (url.endsWith('/')) {
|
|
367
|
+
url = await findPackage(url, platform, arch)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Pass undefined to downloadSDK when no custom URL is provided
|
|
371
|
+
// This allows it to fetch the latest package from admin.netease.im
|
|
372
|
+
await downloadSDK(url)
|
|
373
|
+
} catch (error) {
|
|
374
|
+
log(` ❌ ERROR: ${error.message}`)
|
|
375
|
+
process.exit(1)
|
|
376
|
+
}
|
|
377
|
+
})()
|
|
106
378
|
}
|
|
107
379
|
exports.downloadSDK = downloadSDK
|