@_xtribe/cli 2.2.72 → 2.2.76

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/install-tribe.js +96 -8
  2. package/package.json +1 -1
package/install-tribe.js CHANGED
@@ -64,9 +64,49 @@ function showWelcome() {
64
64
  }
65
65
 
66
66
 
67
+ // Fetch latest release version from GitHub
68
+ async function getLatestVersion() {
69
+ return new Promise((resolve, reject) => {
70
+ const url = 'https://api.github.com/repos/TRIBE-INC/releases/releases/latest';
71
+ https.get(url, {
72
+ headers: { 'User-Agent': 'TRIBE-CLI-Installer' }
73
+ }, (response) => {
74
+ let data = '';
75
+ response.on('data', chunk => data += chunk);
76
+ response.on('end', () => {
77
+ try {
78
+ const release = JSON.parse(data);
79
+ resolve(release.tag_name); // e.g., "v2.2.74"
80
+ } catch (error) {
81
+ resolve(null); // Ignore errors, proceed with install
82
+ }
83
+ });
84
+ }).on('error', () => resolve(null));
85
+ });
86
+ }
87
+
88
+ // Get installed version by running tribe --version
89
+ function getInstalledVersion() {
90
+ const tribeDest = path.join(tribeBinDir, 'tribe');
91
+ if (!fs.existsSync(tribeDest)) return null;
92
+
93
+ try {
94
+ const output = execSync(`"${tribeDest}" --version 2>/dev/null || true`, {
95
+ encoding: 'utf8',
96
+ timeout: 5000
97
+ });
98
+ // Extract version like "v2.2.74" or "2.2.74"
99
+ const match = output.match(/v?(\d+\.\d+\.\d+)/);
100
+ return match ? `v${match[1]}` : null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
67
106
  async function downloadFile(url, dest) {
68
107
  return new Promise((resolve, reject) => {
69
- const file = fs.createWriteStream(dest);
108
+ // Create file with execute permissions from the start (0755)
109
+ const file = fs.createWriteStream(dest, { mode: 0o755 });
70
110
  https.get(url, (response) => {
71
111
  if (response.statusCode === 302 || response.statusCode === 301) {
72
112
  return downloadFile(response.headers.location, dest).then(resolve, reject);
@@ -78,6 +118,12 @@ async function downloadFile(url, dest) {
78
118
  response.pipe(file);
79
119
  file.on('finish', () => {
80
120
  file.close();
121
+ // Ensure execute permissions are set after download completes
122
+ try {
123
+ fs.chmodSync(dest, 0o755);
124
+ } catch (e) {
125
+ // Ignore chmod errors - permissions were set during creation
126
+ }
81
127
  resolve();
82
128
  });
83
129
  }).on('error', reject);
@@ -87,9 +133,38 @@ async function downloadFile(url, dest) {
87
133
  async function installTribeCLI() {
88
134
  const tribeDest = path.join(tribeBinDir, 'tribe');
89
135
 
90
- // Check if update needed
136
+ // Check if update needed by comparing versions
91
137
  const exists = fs.existsSync(tribeDest);
92
- const spinner = ora(exists ? 'Updating CLI...' : 'Installing CLI...').start();
138
+ const installedVersion = getInstalledVersion();
139
+ const latestVersion = await getLatestVersion();
140
+
141
+ // Determine if we need to download
142
+ let needsDownload = !exists;
143
+ let statusMsg = 'Installing CLI...';
144
+
145
+ if (exists && latestVersion && installedVersion) {
146
+ // Compare versions (strip 'v' prefix for comparison)
147
+ const installed = installedVersion.replace(/^v/, '');
148
+ const latest = latestVersion.replace(/^v/, '');
149
+ if (installed !== latest) {
150
+ needsDownload = true;
151
+ statusMsg = `Updating CLI (${installedVersion} → ${latestVersion})...`;
152
+ } else {
153
+ statusMsg = `CLI is up to date (${latestVersion})`;
154
+ }
155
+ } else if (exists) {
156
+ // Can't determine versions, force update to be safe
157
+ needsDownload = true;
158
+ statusMsg = 'Updating CLI...';
159
+ }
160
+
161
+ const spinner = ora(statusMsg).start();
162
+
163
+ // Skip download if already up to date
164
+ if (exists && !needsDownload) {
165
+ spinner.succeed(`CLI is up to date (${latestVersion || 'latest'})`);
166
+ return true;
167
+ }
93
168
 
94
169
  try {
95
170
  // Clean up any existing tribe binaries first
@@ -105,7 +180,7 @@ async function installTribeCLI() {
105
180
  const downloadUrl = `https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`;
106
181
 
107
182
  await downloadFile(downloadUrl, tribeDest);
108
- fs.chmodSync(tribeDest, '755');
183
+ fs.chmodSync(tribeDest, 0o755);
109
184
 
110
185
  // Remove macOS quarantine if needed
111
186
  if (platform === 'darwin') {
@@ -116,7 +191,20 @@ async function installTribeCLI() {
116
191
  }
117
192
  }
118
193
 
119
- spinner.succeed('CLI ready');
194
+ // Save version info for future update checks
195
+ if (latestVersion) {
196
+ try {
197
+ const versionFile = path.join(tribeDir, 'version.json');
198
+ fs.writeFileSync(versionFile, JSON.stringify({
199
+ version: latestVersion,
200
+ installedAt: new Date().toISOString()
201
+ }, null, 2));
202
+ } catch {
203
+ // Not critical
204
+ }
205
+ }
206
+
207
+ spinner.succeed(`CLI ready (${latestVersion || 'latest'})`);
120
208
  return true;
121
209
  } catch (error) {
122
210
  spinner.fail(`Installation failed: ${error.message}`);
@@ -166,7 +254,7 @@ async function installTribeCLIQuiet() {
166
254
  const downloadUrl = `https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`;
167
255
 
168
256
  await downloadFile(downloadUrl, tribeDest);
169
- fs.chmodSync(tribeDest, '755');
257
+ fs.chmodSync(tribeDest, 0o755);
170
258
 
171
259
  // Also download tutor-collector binary (CRITICAL - this is the core product!)
172
260
  const tutorCollectorDest = path.join(tribeBinDir, 'tutor-collector');
@@ -180,7 +268,7 @@ async function installTribeCLIQuiet() {
180
268
  const communityUrl = `https://github.com/TRIBE-INC/tutor-server-community-release/releases/latest/download/tutor-collector-${platform}-${arch}`;
181
269
  await downloadFile(communityUrl, tutorCollectorDest);
182
270
  }
183
- fs.chmodSync(tutorCollectorDest, '755');
271
+ fs.chmodSync(tutorCollectorDest, 0o755);
184
272
 
185
273
  // Download realtime-sync binary (for tribe realtime command)
186
274
  const realtimeSyncDest = path.join(tribeBinDir, 'realtime-sync');
@@ -188,7 +276,7 @@ async function installTribeCLIQuiet() {
188
276
 
189
277
  try {
190
278
  await downloadFile(realtimeSyncUrl, realtimeSyncDest);
191
- fs.chmodSync(realtimeSyncDest, '755');
279
+ fs.chmodSync(realtimeSyncDest, 0o755);
192
280
  } catch (error) {
193
281
  // realtime-sync is optional, don't fail install if missing
194
282
  console.log('Note: realtime-sync not available in this release');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_xtribe/cli",
3
- "version": "2.2.72",
3
+ "version": "2.2.76",
4
4
  "description": "TRIBE - Privacy-first AI development analytics. Self-host your telemetry, skip authentication, or run completely offline. Your data stays on your machine.",
5
5
  "main": "install-tribe.js",
6
6
  "bin": {