stacktape 3.2.0-beta.9 → 3.2.1

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/bin/stacktape.js CHANGED
@@ -33,6 +33,52 @@ const PLATFORM_MAP = {
33
33
  'linux-x64-musl': { fileName: 'alpine.tar.gz', extract: extractTarGz }
34
34
  };
35
35
 
36
+ // ANSI color codes
37
+ const colors = {
38
+ reset: '\x1B[0m',
39
+ dim: '\x1B[2m',
40
+ green: '\x1B[32m',
41
+ cyan: '\x1B[36m',
42
+ red: '\x1B[31m',
43
+ bold: '\x1B[1m'
44
+ };
45
+
46
+ const isTTY = process.stdout.isTTY;
47
+
48
+ const printLogo = () => {
49
+ const { dim, green, reset, bold } = colors;
50
+ console.info(`
51
+ ${dim} _____ _ _ _ ${reset}
52
+ ${dim} / ____| | | | | | ${reset}
53
+ ${green} | (___ | |_ __ _ ___| | _| |_ __ _ _ __ ___ ${reset}
54
+ ${green} \\___ \\| __/ _\` |/ __| |/ / __/ _\` | '_ \\ / _ \\ ${reset}
55
+ ${green} ____) | || (_| | (__| <| || (_| | |_) | __/ ${reset}
56
+ ${green} |_____/ \\__\\__,_|\\___|_|\\_\\\\__\\__,_| .__/ \\___| ${reset}
57
+ ${green} | | ${reset}
58
+ ${green} |_| ${reset}
59
+
60
+ ${bold}${green}Stacktape ${PACKAGE_VERSION} installed successfully!${reset}
61
+
62
+ ${dim}To get started, run:${reset}
63
+
64
+ stacktape init
65
+
66
+ ${dim}For more information visit ${reset}https://docs.stacktape.com
67
+ `);
68
+ };
69
+
70
+ const printProgress = (downloaded, total) => {
71
+ if (!isTTY) return;
72
+
73
+ const width = 40;
74
+ const percent = total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : 0;
75
+ const filled = Math.round((percent / 100) * width);
76
+ const empty = width - filled;
77
+
78
+ const bar = `${'#'.repeat(filled)}${'-'.repeat(empty)}`;
79
+ process.stdout.write(`\r${colors.cyan}${bar} ${percent}%${colors.reset}`);
80
+ };
81
+
36
82
  function detectPlatform() {
37
83
  const currentPlatform = platform();
38
84
  const currentArch = arch();
@@ -52,7 +98,7 @@ function detectPlatform() {
52
98
  const platformKey = `${currentPlatform}-${currentArch}`;
53
99
 
54
100
  if (!PLATFORM_MAP[platformKey]) {
55
- console.error(`Error: Unsupported platform ${currentPlatform}-${currentArch}`);
101
+ console.error(`${colors.red}Error: Unsupported platform ${currentPlatform}-${currentArch}${colors.reset}`);
56
102
  console.error('Stacktape binaries are available for:');
57
103
  Object.keys(PLATFORM_MAP).forEach((key) => {
58
104
  console.error(` - ${key}`);
@@ -69,31 +115,9 @@ async function downloadFile(url, destPath, retries = 3) {
69
115
  await new Promise((resolve, reject) => {
70
116
  const fileStream = createWriteStream(destPath);
71
117
 
72
- httpsGet(url, (response) => {
73
- // Follow redirects
118
+ const handleResponse = (response) => {
74
119
  if (response.statusCode === 301 || response.statusCode === 302) {
75
- httpsGet(response.headers.location, (redirectResponse) => {
76
- if (redirectResponse.statusCode !== 200) {
77
- reject(new Error(`Failed to download: ${redirectResponse.statusCode}`));
78
- return;
79
- }
80
-
81
- const totalBytes = Number.parseInt(redirectResponse.headers['content-length'], 10);
82
- let downloadedBytes = 0;
83
-
84
- redirectResponse.on('data', (chunk) => {
85
- downloadedBytes += chunk.length;
86
- const percent = totalBytes ? ((downloadedBytes / totalBytes) * 100).toFixed(1) : '?';
87
- process.stdout.write(`\rDownloading... ${percent}%`);
88
- });
89
-
90
- redirectResponse.pipe(fileStream);
91
- fileStream.on('finish', () => {
92
- fileStream.close();
93
- process.stdout.write('\n');
94
- resolve();
95
- });
96
- }).on('error', reject);
120
+ httpsGet(response.headers.location, handleResponse).on('error', reject);
97
121
  return;
98
122
  }
99
123
 
@@ -102,22 +126,23 @@ async function downloadFile(url, destPath, retries = 3) {
102
126
  return;
103
127
  }
104
128
 
105
- const totalBytes = Number.parseInt(response.headers['content-length'], 10);
129
+ const totalBytes = Number.parseInt(response.headers['content-length'], 10) || 0;
106
130
  let downloadedBytes = 0;
107
131
 
108
132
  response.on('data', (chunk) => {
109
133
  downloadedBytes += chunk.length;
110
- const percent = totalBytes ? ((downloadedBytes / totalBytes) * 100).toFixed(1) : '?';
111
- process.stdout.write(`\rDownloading... ${percent}%`);
134
+ printProgress(downloadedBytes, totalBytes);
112
135
  });
113
136
 
114
137
  response.pipe(fileStream);
115
138
  fileStream.on('finish', () => {
116
139
  fileStream.close();
117
- process.stdout.write('\n');
140
+ if (isTTY) process.stdout.write('\n');
118
141
  resolve();
119
142
  });
120
- }).on('error', reject);
143
+ };
144
+
145
+ httpsGet(url, handleResponse).on('error', reject);
121
146
  });
122
147
 
123
148
  return; // Success
@@ -125,7 +150,7 @@ async function downloadFile(url, destPath, retries = 3) {
125
150
  if (i === retries - 1) {
126
151
  throw error;
127
152
  }
128
- console.info(`\nRetrying download (${i + 1}/${retries})...`);
153
+ console.info(`\n${colors.dim}Retrying download (${i + 1}/${retries})...${colors.reset}`);
129
154
  }
130
155
  }
131
156
  }
@@ -191,7 +216,7 @@ async function ensureBinary() {
191
216
  return binaryPath;
192
217
  }
193
218
 
194
- console.info(`Stacktape binary not found. Installing version ${version} for ${platformKey}...`);
219
+ console.info(`${colors.dim}Installing Stacktape ${version} for ${platformKey}...${colors.reset}`);
195
220
 
196
221
  mkdirSync(cacheDir, { recursive: true });
197
222
 
@@ -199,10 +224,10 @@ async function ensureBinary() {
199
224
  const archivePath = join(cacheDir, platformInfo.fileName);
200
225
 
201
226
  try {
202
- console.info(`Downloading from ${downloadUrl}...`);
227
+ console.info(`${colors.dim}Downloading from GitHub releases...${colors.reset}`);
203
228
  await downloadFile(downloadUrl, archivePath);
204
229
 
205
- console.info('Extracting...');
230
+ console.info(`${colors.dim}Extracting...${colors.reset}`);
206
231
  await platformInfo.extract(archivePath, cacheDir);
207
232
 
208
233
  setExecutablePermissions(cacheDir);
@@ -213,12 +238,12 @@ async function ensureBinary() {
213
238
  throw new Error(`Binary not found after extraction: ${binaryPath}`);
214
239
  }
215
240
 
216
- console.info(`✓ Stacktape ${version} installed successfully`);
241
+ printLogo();
217
242
 
218
243
  return binaryPath;
219
244
  } catch (error) {
220
245
  console.error(`
221
- Error installing Stacktape:
246
+ ${colors.red}Error installing Stacktape:${colors.reset}
222
247
  ${error.message}
223
248
 
224
249
  You can also install Stacktape directly using:
@@ -287,13 +312,13 @@ async function main() {
287
312
  });
288
313
 
289
314
  if (result.error) {
290
- console.error(`Error executing Stacktape binary: ${result.error.message}`);
315
+ console.error(`${colors.red}Error executing Stacktape binary: ${result.error.message}${colors.reset}`);
291
316
  process.exit(1);
292
317
  }
293
318
 
294
319
  process.exit(result.status || 0);
295
320
  } catch (error) {
296
- console.error('Unexpected error:', error.message);
321
+ console.error(`${colors.red}Unexpected error: ${error.message}${colors.reset}`);
297
322
  process.exit(1);
298
323
  }
299
324
  }