tingly-box 0.2601.51800-preview → 0.2601.110000-preview

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.
Files changed (2) hide show
  1. package/bin.js +80 -48
  2. package/package.json +3 -2
package/bin.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { execFileSync } from "child_process";
4
- import { chmodSync, createWriteStream, existsSync, fsyncSync, mkdirSync } from "fs";
5
- import { tmpdir } from "os";
4
+ import { chmodSync, createWriteStream, existsSync, fsyncSync, mkdirSync, statSync } from "fs";
6
5
  import { join } from "path";
7
6
  import { Readable } from "stream";
8
7
  import { ProxyAgent } from "undici";
8
+ import unzipper from "unzipper";
9
9
 
10
10
  // Configuration for binary downloads
11
11
  const BASE_URL = "https://github.com/tingly-dev/tingly-box/releases/download/";
@@ -15,7 +15,7 @@ const LATEST_RELEASE_API_URL = "https://github.com/tingly-dev/tingly-box/release
15
15
 
16
16
  // Default branch to use when not specified via transport version
17
17
  // This will be replaced during the NPX build process
18
- const BINARY_RELEASE_BRANCH = 'v0.2601.051800-preview';
18
+ const BINARY_RELEASE_BRANCH = 'v0.2601.110000-preview';
19
19
 
20
20
  // Create proxy agent from environment variables (HTTP_PROXY, HTTPS_PROXY)
21
21
  // Only create ProxyAgent if proxy is configured, otherwise use undefined (direct connection)
@@ -199,62 +199,94 @@ async function downloadAndExtractZip(url, extractDir, binaryName) {
199
199
  process.exit(1);
200
200
  }
201
201
 
202
- // Create a temporary file for the ZIP
203
- const zipPath = join(tmpdir(), `tingly-box-${Date.now()}.zip`);
204
- const fileStream = createWriteStream(zipPath, { flags: "w" });
205
-
206
202
  const contentLength = res.headers.get("content-length");
207
203
  const totalSize = contentLength ? parseInt(contentLength, 10) : null;
208
204
  let downloadedSize = 0;
209
205
 
210
- await new Promise((resolve, reject) => {
211
- try {
212
- const nodeStream = Readable.fromWeb(res.body);
206
+ // Convert the fetch response body to a Node.js readable stream
207
+ const nodeStream = Readable.fromWeb(res.body);
208
+
209
+ // Collect the entire ZIP into a buffer
210
+ const chunks = [];
211
+ for await (const chunk of nodeStream) {
212
+ chunks.push(chunk);
213
+ downloadedSize += chunk.length;
214
+ if (totalSize) {
215
+ const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
216
+ process.stdout.write(`\r⏱️ Downloading: ${progress}% (${formatBytes(downloadedSize)}/${formatBytes(totalSize)})`);
217
+ } else {
218
+ process.stdout.write(`\r⏱️ Downloaded: ${formatBytes(downloadedSize)}`);
219
+ }
220
+ }
221
+ const zipBuffer = Buffer.concat(chunks);
213
222
 
214
- nodeStream.on("data", (chunk) => {
215
- downloadedSize += chunk.length;
216
- if (totalSize) {
217
- const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
218
- process.stdout.write(`\r⏱️ Downloading ZIP: ${progress}% (${formatBytes(downloadedSize)}/${formatBytes(totalSize)})`);
219
- } else {
220
- process.stdout.write(`\r⏱️ Downloaded: ${formatBytes(downloadedSize)}`);
221
- }
222
- });
223
+ // Extract ZIP from buffer using unzipper
224
+ try {
225
+ console.log(`\n📦 Extracting ZIP to ${extractDir}...`);
223
226
 
224
- nodeStream.pipe(fileStream);
225
- fileStream.on("finish", () => {
226
- process.stdout.write("\n");
227
- resolve();
228
- });
229
- fileStream.on("error", reject);
230
- nodeStream.on("error", reject);
231
- } catch (error) {
232
- reject(error);
227
+ const directory = await unzipper.Open.buffer(zipBuffer);
228
+
229
+ // Debug: List all entries in the ZIP
230
+ console.log(`📋 ZIP contents (${directory.files.length} entries):`);
231
+ for (const file of directory.files) {
232
+ console.log(` ${file.type}: ${file.path} (permissions: ${file.unixPermissions?.toString(8)})`);
233
233
  }
234
- });
235
234
 
236
- // Extract the ZIP file using system unzip command
237
- try {
238
- console.log(`📦 Extracting ZIP...`);
239
- execFileSync("unzip", ["-q", "-o", zipPath, "-d", extractDir]);
240
- console.log(`✅ Extracted ZIP to ${extractDir}`);
241
- } catch (error) {
242
- console.error(`❌ Failed to extract ZIP: ${error.message}`);
243
- // Fallback: try using Python to extract
244
- try {
245
- execFileSync("python3", ["-m", "zipfile", "-e", zipPath, extractDir]);
246
- console.log(`✅ Extracted ZIP using Python`);
247
- } catch (pythonError) {
248
- console.error(`❌ Failed to extract ZIP with Python too: ${pythonError.message}`);
249
- process.exit(1);
235
+ // Extract all files to the target directory
236
+ for (const file of directory.files) {
237
+ // Skip directory entries and __MACOSX metadata
238
+ if (file.type === 'Directory' || file.path.startsWith('__MACOSX/') || file.path.includes('.DS_Store')) {
239
+ console.log(`⏭️ Skipping: ${file.path} (type: ${file.type})`);
240
+ continue;
241
+ }
242
+
243
+ const filePath = join(extractDir, file.path);
244
+ // Get parent directory of the file in the ZIP
245
+ const pathParts = file.path.split('/');
246
+ pathParts.pop(); // Remove the filename
247
+ const fileDir = pathParts.length > 0 ? join(extractDir, ...pathParts) : extractDir;
248
+
249
+ console.log(`📄 Extracting: ${file.path} -> ${filePath}`);
250
+
251
+ // Ensure parent directory exists
252
+ if (fileDir !== extractDir && !existsSync(fileDir)) {
253
+ mkdirSync(fileDir, { recursive: true });
254
+ }
255
+
256
+ // Remove existing directory if it exists (this was created incorrectly before)
257
+ if (existsSync(filePath) && statSync(filePath).isDirectory()) {
258
+ console.log(`🧹 Removing incorrect directory: ${filePath}`);
259
+ // Can't easily remove a directory in Node without fs.rm (Node 14.14+)
260
+ // Skip and let user clean up manually
261
+ console.log(`⚠️ Please manually remove: rm -rf "${filePath}"`);
262
+ continue;
263
+ }
264
+
265
+ // Extract file
266
+ const content = await file.buffer();
267
+ const fileStream = createWriteStream(filePath);
268
+ await new Promise((resolve, reject) => {
269
+ fileStream.write(content, (err) => {
270
+ if (err) reject(err);
271
+ else {
272
+ fileStream.end();
273
+ resolve();
274
+ }
275
+ });
276
+ });
277
+ // Set file permissions after writing
278
+ if (process.platform !== "win32") {
279
+ // Use ZIP permissions if available, otherwise default to 0o755 (executable)
280
+ const permissions = file.unixPermissions && file.unixPermissions > 0 ? file.unixPermissions : 0o755;
281
+ chmodSync(filePath, permissions);
282
+ }
250
283
  }
251
- }
252
284
 
253
- // Clean up the ZIP file
254
- try {
255
- execFileSync("rm", ["-f", zipPath]);
285
+ console.log(`✅ Extracted ZIP to ${extractDir}`);
256
286
  } catch (error) {
257
- // Ignore cleanup errors
287
+ console.error(`\n❌ Failed to extract ZIP: ${error.message}`);
288
+ console.error(`Stack: ${error.stack}`);
289
+ process.exit(1);
258
290
  }
259
291
  }
260
292
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tingly-box",
3
- "version": "0.2601.51800-preview",
3
+ "version": "0.2601.110000-preview",
4
4
  "description": "High-performance AI gateway CLI - connect to multiple AI providers through a single API",
5
5
  "keywords": [
6
6
  "ai",
@@ -29,6 +29,7 @@
29
29
  },
30
30
  "type": "module",
31
31
  "dependencies": {
32
- "undici": "^7.0.0"
32
+ "undici": "^7.0.0",
33
+ "unzipper": "^0.12.3"
33
34
  }
34
35
  }