@_xtribe/cli 1.0.0-beta.22 → 1.0.0-beta.25
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/README.md +30 -7
- package/install-tribe.js +517 -134
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,12 +8,18 @@ TRIBE is an AI-powered multi-agent development system that manages your entire d
|
|
|
8
8
|
npx @_xtribe/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
That's it! This single command
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- ✅
|
|
15
|
-
- ✅
|
|
16
|
-
- ✅
|
|
11
|
+
That's it! This single command works on macOS, Linux, and Windows (WSL2).
|
|
12
|
+
|
|
13
|
+
**What it does:**
|
|
14
|
+
- ✅ Detects your platform automatically
|
|
15
|
+
- ✅ Installs all required tools (kubectl, TRIBE CLI)
|
|
16
|
+
- ✅ Sets up Kubernetes for your platform:
|
|
17
|
+
- **macOS**: Colima with Kubernetes
|
|
18
|
+
- **Linux**: K3s (lightweight Kubernetes)
|
|
19
|
+
- **Windows**: Guides to WSL2 setup
|
|
20
|
+
- ✅ Deploys the complete TRIBE cluster
|
|
21
|
+
- ✅ Configures your environment
|
|
22
|
+
- ✅ Guides you through creating your first project
|
|
17
23
|
|
|
18
24
|
## 🎯 What is TRIBE?
|
|
19
25
|
|
|
@@ -54,7 +60,12 @@ tribe review-task
|
|
|
54
60
|
|
|
55
61
|
## 🛠️ System Requirements
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
### Supported Platforms
|
|
64
|
+
- **macOS** (Intel & Apple Silicon)
|
|
65
|
+
- **Linux** (Ubuntu, Debian, Fedora, etc.)
|
|
66
|
+
- **Windows** - Use WSL2 with Ubuntu
|
|
67
|
+
|
|
68
|
+
### Hardware Requirements
|
|
58
69
|
- **4GB RAM** minimum (8GB recommended)
|
|
59
70
|
- **20GB disk space**
|
|
60
71
|
- **Node.js 16+**
|
|
@@ -90,6 +101,8 @@ For detailed documentation, visit the [TRIBE Flow Guide](https://github.com/0zen
|
|
|
90
101
|
## 🆘 Troubleshooting
|
|
91
102
|
|
|
92
103
|
### Cluster not starting?
|
|
104
|
+
|
|
105
|
+
**macOS:**
|
|
93
106
|
```bash
|
|
94
107
|
# Check if Colima is running
|
|
95
108
|
colima status
|
|
@@ -99,6 +112,16 @@ colima start --kubernetes
|
|
|
99
112
|
tribe start
|
|
100
113
|
```
|
|
101
114
|
|
|
115
|
+
**Linux:**
|
|
116
|
+
```bash
|
|
117
|
+
# Check if K3s is running
|
|
118
|
+
sudo systemctl status k3s
|
|
119
|
+
|
|
120
|
+
# Start manually if needed
|
|
121
|
+
sudo systemctl start k3s
|
|
122
|
+
tribe start
|
|
123
|
+
```
|
|
124
|
+
|
|
102
125
|
### Port conflicts?
|
|
103
126
|
```bash
|
|
104
127
|
# Check what's using ports
|
package/install-tribe.js
CHANGED
|
@@ -8,9 +8,13 @@ const { execSync, spawn } = require('child_process');
|
|
|
8
8
|
const chalk = require('chalk');
|
|
9
9
|
const ora = require('ora');
|
|
10
10
|
const which = require('which');
|
|
11
|
+
const fetch = require('node-fetch');
|
|
12
|
+
const crypto = require('crypto'); // Added for checksum verification
|
|
11
13
|
|
|
12
14
|
const platform = os.platform();
|
|
13
|
-
|
|
15
|
+
// Map Node.js arch to standard naming (x64 -> amd64, etc.)
|
|
16
|
+
const nodeArch = os.arch();
|
|
17
|
+
const arch = nodeArch === 'x64' ? 'amd64' : (nodeArch === 'arm64' || nodeArch === 'aarch64' ? 'arm64' : nodeArch);
|
|
14
18
|
const homeDir = os.homedir();
|
|
15
19
|
const binDir = path.join(homeDir, 'bin');
|
|
16
20
|
const tribeDir = path.join(homeDir, '.tribe');
|
|
@@ -83,8 +87,25 @@ async function downloadFile(url, dest) {
|
|
|
83
87
|
return;
|
|
84
88
|
}
|
|
85
89
|
response.pipe(file);
|
|
86
|
-
file.on('finish', () => {
|
|
90
|
+
file.on('finish', async () => {
|
|
87
91
|
file.close();
|
|
92
|
+
|
|
93
|
+
// Check if this might be a pointer file
|
|
94
|
+
const stats = fs.statSync(dest);
|
|
95
|
+
if (stats.size < 100) {
|
|
96
|
+
const content = fs.readFileSync(dest, 'utf8').trim();
|
|
97
|
+
// Check if content looks like a path (e.g., "main-121/tribe-linux-amd64")
|
|
98
|
+
if (content.match(/^[\w\-\/]+$/) && content.includes('/')) {
|
|
99
|
+
console.log(`📎 Following pointer to: ${content}`);
|
|
100
|
+
const baseUrl = url.substring(0, url.lastIndexOf('/'));
|
|
101
|
+
const actualBinaryUrl = `${baseUrl}/${content}`;
|
|
102
|
+
|
|
103
|
+
// Download the actual binary
|
|
104
|
+
fs.unlinkSync(dest); // Remove pointer file
|
|
105
|
+
await downloadFile(actualBinaryUrl, dest);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
88
109
|
resolve();
|
|
89
110
|
});
|
|
90
111
|
}).on('error', reject);
|
|
@@ -94,55 +115,91 @@ async function downloadFile(url, dest) {
|
|
|
94
115
|
// Docker installation removed - Colima provides Docker runtime
|
|
95
116
|
|
|
96
117
|
async function installK3s() {
|
|
97
|
-
if (platform !== 'linux') {
|
|
98
|
-
return true; // K3s is Linux only
|
|
99
|
-
}
|
|
100
|
-
|
|
101
118
|
const spinner = ora('Installing K3s (lightweight Kubernetes)...').start();
|
|
102
119
|
|
|
103
120
|
try {
|
|
104
|
-
// Check if already installed
|
|
121
|
+
// Check if K3s is already installed
|
|
105
122
|
try {
|
|
106
|
-
execSync('k3s
|
|
123
|
+
execSync('which k3s', { stdio: 'ignore' });
|
|
107
124
|
spinner.succeed('K3s already installed');
|
|
108
125
|
return true;
|
|
109
126
|
} catch {
|
|
110
127
|
// Not installed, continue
|
|
111
128
|
}
|
|
112
129
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
console.log(chalk.yellow('\n📦 Installing K3s - Lightweight Kubernetes'));
|
|
116
|
-
console.log(chalk.gray('This is a one-line installation that will:'));
|
|
117
|
-
console.log(chalk.gray(' • Download and install K3s'));
|
|
118
|
-
console.log(chalk.gray(' • Configure systemd service'));
|
|
119
|
-
console.log(chalk.gray(' • Set up kubectl access'));
|
|
120
|
-
console.log(chalk.gray(' • Disable Traefik (we use our own ingress)\n'));
|
|
130
|
+
// Check if we have permission to install
|
|
131
|
+
const needsSudo = process.getuid && process.getuid() !== 0;
|
|
121
132
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
// Check if we're in a container or CI environment
|
|
134
|
+
const isContainer = process.env.container === 'docker' ||
|
|
135
|
+
fs.existsSync('/.dockerenv') ||
|
|
136
|
+
!fs.existsSync('/run/systemd/system') ||
|
|
137
|
+
process.env.CI === 'true';
|
|
126
138
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
139
|
+
if (isContainer) {
|
|
140
|
+
spinner.warn('Running in container/CI - K3s installation skipped');
|
|
141
|
+
log.info('K3s requires systemd and privileged access');
|
|
142
|
+
log.info('For containers, consider using KIND or external cluster');
|
|
143
|
+
return true; // Don't fail in containers
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Download and install K3s
|
|
147
|
+
spinner.text = 'Downloading K3s installer...';
|
|
148
|
+
|
|
149
|
+
const installCommand = needsSudo ?
|
|
150
|
+
'curl -sfL https://get.k3s.io | sudo sh -s - --write-kubeconfig-mode 644' :
|
|
151
|
+
'curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644';
|
|
130
152
|
|
|
131
|
-
// Make kubectl available
|
|
132
153
|
try {
|
|
133
|
-
execSync(
|
|
134
|
-
|
|
135
|
-
|
|
154
|
+
execSync(installCommand, {
|
|
155
|
+
stdio: 'pipe',
|
|
156
|
+
env: { ...process.env, INSTALL_K3S_SKIP_START: 'false' }
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
spinner.succeed('K3s installed successfully');
|
|
160
|
+
|
|
161
|
+
// Wait for K3s to be ready
|
|
162
|
+
spinner.text = 'Waiting for K3s to start...';
|
|
163
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
164
|
+
|
|
165
|
+
// Configure kubectl to use K3s
|
|
166
|
+
const k3sConfig = '/etc/rancher/k3s/k3s.yaml';
|
|
167
|
+
const userConfig = path.join(os.homedir(), '.kube', 'config');
|
|
168
|
+
|
|
169
|
+
if (fs.existsSync(k3sConfig)) {
|
|
170
|
+
fs.mkdirSync(path.dirname(userConfig), { recursive: true });
|
|
171
|
+
|
|
172
|
+
if (needsSudo) {
|
|
173
|
+
execSync(`sudo cp ${k3sConfig} ${userConfig} && sudo chown $(id -u):$(id -g) ${userConfig}`, { stdio: 'ignore' });
|
|
174
|
+
} else {
|
|
175
|
+
fs.copyFileSync(k3sConfig, userConfig);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Update server address to use localhost
|
|
179
|
+
let configContent = fs.readFileSync(userConfig, 'utf8');
|
|
180
|
+
configContent = configContent.replace(/server: https:\/\/127\.0\.0\.1:6443/, 'server: https://localhost:6443');
|
|
181
|
+
fs.writeFileSync(userConfig, configContent);
|
|
182
|
+
|
|
183
|
+
spinner.succeed('K3s configured');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return true;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error.message.includes('permission denied') || error.message.includes('sudo')) {
|
|
189
|
+
spinner.fail('K3s installation requires sudo permission');
|
|
190
|
+
log.info('Please run the installer with sudo or install K3s manually:');
|
|
191
|
+
log.info(' curl -sfL https://get.k3s.io | sudo sh -');
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
136
195
|
}
|
|
137
196
|
|
|
138
|
-
// Set KUBECONFIG for current process
|
|
139
|
-
process.env.KUBECONFIG = '/etc/rancher/k3s/k3s.yaml';
|
|
140
|
-
|
|
141
|
-
spinner.succeed('K3s installed successfully');
|
|
142
|
-
return true;
|
|
143
197
|
} catch (error) {
|
|
144
|
-
spinner.fail(
|
|
145
|
-
log.
|
|
198
|
+
spinner.fail('K3s installation failed');
|
|
199
|
+
log.error(error.message);
|
|
200
|
+
log.info('Manual K3s installation:');
|
|
201
|
+
log.info(' curl -sfL https://get.k3s.io | sudo sh -');
|
|
202
|
+
log.info(' sudo systemctl enable --now k3s');
|
|
146
203
|
return false;
|
|
147
204
|
}
|
|
148
205
|
}
|
|
@@ -205,21 +262,21 @@ async function installColima() {
|
|
|
205
262
|
async function installKubectl() {
|
|
206
263
|
// Linux with K3s already has kubectl
|
|
207
264
|
if (platform === 'linux') {
|
|
208
|
-
|
|
265
|
+
try {
|
|
209
266
|
execSync('k3s kubectl version --client', { stdio: 'ignore' });
|
|
210
267
|
log.success('kubectl available via k3s');
|
|
211
268
|
// Create a symlink for convenience
|
|
212
|
-
|
|
269
|
+
try {
|
|
213
270
|
execSync('sudo ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl', { stdio: 'ignore' });
|
|
214
271
|
} catch {
|
|
215
272
|
// Link might already exist
|
|
216
273
|
}
|
|
217
|
-
|
|
274
|
+
return true;
|
|
218
275
|
} catch {
|
|
219
276
|
// Continue with regular kubectl installation
|
|
220
|
-
}
|
|
221
277
|
}
|
|
222
|
-
|
|
278
|
+
}
|
|
279
|
+
|
|
223
280
|
if (await checkCommand('kubectl')) {
|
|
224
281
|
log.success('kubectl already installed');
|
|
225
282
|
return true;
|
|
@@ -264,123 +321,237 @@ async function installTribeCLI() {
|
|
|
264
321
|
}
|
|
265
322
|
|
|
266
323
|
// Download pre-built binary from GitHub
|
|
267
|
-
|
|
324
|
+
// Proper architecture detection for all platforms
|
|
325
|
+
let arch;
|
|
326
|
+
if (process.arch === 'x64' || process.arch === 'x86_64') {
|
|
327
|
+
arch = 'amd64';
|
|
328
|
+
} else if (process.arch === 'arm64' || process.arch === 'aarch64') {
|
|
329
|
+
arch = 'arm64';
|
|
330
|
+
} else {
|
|
331
|
+
arch = process.arch; // fallback
|
|
332
|
+
}
|
|
333
|
+
|
|
268
334
|
const platform = os.platform();
|
|
269
335
|
|
|
270
336
|
// Multiple sources for reliability
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
337
|
+
// For testing, use a direct URL to our releases folder
|
|
338
|
+
const isTestEnv = process.env.TRIBE_TEST_BINARY_URL;
|
|
339
|
+
const githubRepo = process.env.TRIBE_INSTALLER_REPO || 'TRIBE-INC/releases';
|
|
340
|
+
|
|
341
|
+
let downloadUrl = '';
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
spinner.text = 'Fetching latest release information from GitHub...';
|
|
345
|
+
const response = await fetch(`https://api.github.com/repos/${githubRepo}/releases`);
|
|
346
|
+
if (!response.ok) {
|
|
347
|
+
throw new Error(`GitHub API returned ${response.status}`);
|
|
348
|
+
}
|
|
349
|
+
const releases = await response.json();
|
|
350
|
+
if (!releases || releases.length === 0) {
|
|
351
|
+
throw new Error('No releases found');
|
|
352
|
+
}
|
|
353
|
+
const latestRelease = releases[0];
|
|
354
|
+
const assetName = `tribe-${platform}-${arch}`;
|
|
355
|
+
const asset = latestRelease.assets.find(a => a.name === assetName);
|
|
356
|
+
|
|
357
|
+
if (!asset) {
|
|
358
|
+
throw new Error(`No binary found for ${platform}-${arch} in release ${latestRelease.tag_name}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
downloadUrl = asset.browser_download_url;
|
|
362
|
+
spinner.text = `Found latest release: ${latestRelease.tag_name}`;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
spinner.fail(`Failed to get release info: ${error.message}`);
|
|
365
|
+
// Fallback to other methods if the API fails
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const sources = isTestEnv ? [isTestEnv] : [
|
|
369
|
+
downloadUrl,
|
|
370
|
+
// Public releases repository (primary source)
|
|
371
|
+
`https://raw.githubusercontent.com/${githubRepo}/main/cli/latest-tribe-${platform}-${arch}`,
|
|
372
|
+
// GitHub releases (fallback)
|
|
373
|
+
`https://github.com/${githubRepo}/releases/latest/download/tribe-${platform}-${arch}`,
|
|
374
|
+
// Try jsDelivr CDN (for better availability)
|
|
375
|
+
`https://cdn.jsdelivr.net/gh/${githubRepo}@main/cli/latest-tribe-${platform}-${arch}`,
|
|
376
|
+
].filter(Boolean); // Filter out empty downloadUrl if API failed
|
|
279
377
|
|
|
280
378
|
let downloaded = false;
|
|
281
379
|
let lastError;
|
|
282
380
|
|
|
283
381
|
for (const url of sources) {
|
|
284
|
-
spinner.text = `Downloading TRIBE CLI from ${url.includes('jsdelivr') ? 'CDN' : 'GitHub'}...`;
|
|
285
382
|
try {
|
|
286
|
-
|
|
383
|
+
let downloadUrl = url;
|
|
384
|
+
|
|
385
|
+
// Check if this is the GitHub API endpoint
|
|
386
|
+
if (url.includes('/api.github.com/')) {
|
|
387
|
+
spinner.text = 'Fetching latest release information from GitHub...';
|
|
388
|
+
|
|
389
|
+
// Fetch release data
|
|
390
|
+
const response = await fetch(url);
|
|
391
|
+
if (!response.ok) {
|
|
392
|
+
throw new Error(`GitHub API returned ${response.status}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const releaseData = await response.json();
|
|
396
|
+
|
|
397
|
+
// Handle both single release and array of releases
|
|
398
|
+
const release = Array.isArray(releaseData) ? releaseData[0] : releaseData;
|
|
399
|
+
|
|
400
|
+
if (!release || !release.assets) {
|
|
401
|
+
throw new Error('No release found');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const assetName = `tribe-${platform}-${arch}`;
|
|
405
|
+
const asset = release.assets.find(a => a.name === assetName);
|
|
406
|
+
|
|
407
|
+
if (!asset) {
|
|
408
|
+
throw new Error(`No binary found for ${platform}-${arch}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
downloadUrl = asset.browser_download_url;
|
|
412
|
+
spinner.text = `Found ${release.prerelease ? 'pre-release' : 'release'}: ${release.tag_name}`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
spinner.text = `Downloading TRIBE CLI from ${new URL(downloadUrl).hostname}...`;
|
|
416
|
+
await downloadFile(downloadUrl, tribeDest);
|
|
417
|
+
|
|
418
|
+
// Check if file is not empty
|
|
419
|
+
const stats = fs.statSync(tribeDest);
|
|
420
|
+
if (stats.size === 0) {
|
|
421
|
+
throw new Error('Downloaded file is empty');
|
|
422
|
+
}
|
|
423
|
+
|
|
287
424
|
fs.chmodSync(tribeDest, '755');
|
|
288
425
|
|
|
289
426
|
// Verify the binary works
|
|
290
|
-
|
|
427
|
+
try {
|
|
428
|
+
execSync(`${tribeDest} version`, { stdio: 'ignore' });
|
|
429
|
+
} catch (versionError) {
|
|
430
|
+
// If version fails, still consider it downloaded if file is valid
|
|
431
|
+
log.warning('Binary downloaded but version check failed - may need different architecture');
|
|
432
|
+
}
|
|
433
|
+
|
|
291
434
|
spinner.succeed('TRIBE CLI installed successfully');
|
|
292
435
|
downloaded = true;
|
|
293
436
|
break;
|
|
294
437
|
} catch (error) {
|
|
295
438
|
lastError = error;
|
|
296
|
-
|
|
439
|
+
// Clean up failed download
|
|
440
|
+
if (fs.existsSync(tribeDest)) {
|
|
441
|
+
fs.unlinkSync(tribeDest);
|
|
442
|
+
}
|
|
443
|
+
log.warning(`Failed: ${error.message}. Trying next source...`);
|
|
297
444
|
}
|
|
298
445
|
}
|
|
299
|
-
|
|
446
|
+
|
|
300
447
|
if (!downloaded) {
|
|
301
|
-
throw new Error(`Failed to download TRIBE CLI
|
|
448
|
+
throw new Error(`Failed to download TRIBE CLI. Please check your internet connection and try again.
|
|
449
|
+
|
|
450
|
+
If this persists, the binaries may not be available for your platform (${platform}-${arch}).
|
|
451
|
+
Please visit https://tribecode.ai/support for assistance.`);
|
|
302
452
|
}
|
|
303
453
|
|
|
304
454
|
return true;
|
|
305
455
|
} catch (error) {
|
|
306
456
|
spinner.fail(`TRIBE CLI installation failed: ${error.message}`);
|
|
457
|
+
|
|
458
|
+
// Show helpful support information
|
|
459
|
+
console.log('');
|
|
460
|
+
console.log(chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
461
|
+
console.log(chalk.yellow('Need help? Visit:'));
|
|
462
|
+
console.log(chalk.cyan.bold(' https://tribecode.ai/support'));
|
|
463
|
+
console.log('');
|
|
464
|
+
console.log(chalk.gray('You can also:'));
|
|
465
|
+
console.log(chalk.gray(' • Check system requirements'));
|
|
466
|
+
console.log(chalk.gray(' • View troubleshooting guides'));
|
|
467
|
+
console.log(chalk.gray(' • Contact our support team'));
|
|
468
|
+
console.log(chalk.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
469
|
+
console.log('');
|
|
470
|
+
|
|
307
471
|
return false;
|
|
308
472
|
}
|
|
309
473
|
}
|
|
310
474
|
|
|
311
475
|
async function startContainerRuntime() {
|
|
312
476
|
const spinner = ora('Starting container runtime...').start();
|
|
313
|
-
|
|
477
|
+
|
|
314
478
|
try {
|
|
315
479
|
// Platform-specific container runtime handling
|
|
316
480
|
if (platform === 'darwin') {
|
|
317
|
-
// macOS - Check if
|
|
481
|
+
// macOS - Check if Colima is running
|
|
318
482
|
try {
|
|
319
|
-
execSync('
|
|
320
|
-
|
|
321
|
-
|
|
483
|
+
const colimaStatus = execSync('colima status 2>&1', { encoding: 'utf8' });
|
|
484
|
+
if (colimaStatus.includes('is running')) {
|
|
485
|
+
spinner.succeed('Colima is already running');
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
322
488
|
} catch {
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
const spinner = ora('Starting Colima container runtime...').start();
|
|
489
|
+
// Colima not running, need to start it
|
|
490
|
+
}
|
|
326
491
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
spinner
|
|
330
|
-
const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
|
|
331
|
-
execSync(`${colimaPath} start --cpu 2 --memory 4 --disk 10 --kubernetes --vm-type=vz`, {
|
|
332
|
-
stdio: 'pipe',
|
|
333
|
-
timeout: 60000 // 60 second timeout for K8s
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// Test if it worked
|
|
337
|
-
execSync('docker info', { stdio: 'ignore' });
|
|
338
|
-
spinner.succeed('Colima started successfully');
|
|
339
|
-
return true;
|
|
492
|
+
// Try to start Colima
|
|
493
|
+
if (await checkCommand('colima')) {
|
|
494
|
+
const spinner = ora('Starting Colima container runtime...').start();
|
|
340
495
|
|
|
341
|
-
} catch (error) {
|
|
342
|
-
// Strategy 2: Start in background with Kubernetes
|
|
343
496
|
try {
|
|
344
|
-
|
|
497
|
+
// Strategy 1: Quick start with minimal resources and Kubernetes
|
|
498
|
+
spinner.text = 'Starting Colima with Kubernetes...';
|
|
345
499
|
const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }
|
|
500
|
+
execSync(`${colimaPath} start --cpu 2 --memory 4 --disk 10 --kubernetes --vm-type=vz`, {
|
|
501
|
+
stdio: 'pipe',
|
|
502
|
+
timeout: 60000 // 60 second timeout for K8s
|
|
350
503
|
});
|
|
351
|
-
child.unref(); // Don't wait for completion
|
|
352
504
|
|
|
353
|
-
spinner.succeed('Colima
|
|
354
|
-
log.info('Colima is starting in the background');
|
|
355
|
-
log.info('Run "colima status" to check progress');
|
|
356
|
-
log.info('Run "docker info" to test when ready');
|
|
505
|
+
spinner.succeed('Colima started successfully');
|
|
357
506
|
return true;
|
|
358
507
|
|
|
359
|
-
} catch (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
508
|
+
} catch (error) {
|
|
509
|
+
// Strategy 2: Start in background with Kubernetes
|
|
510
|
+
try {
|
|
511
|
+
spinner.text = 'Starting Colima with Kubernetes in background...';
|
|
512
|
+
const colimaPath = await findCommand('colima') || path.join(binDir, 'colima');
|
|
513
|
+
const child = spawn(colimaPath, ['start', '--cpu', '2', '--memory', '4', '--disk', '10', '--kubernetes'], {
|
|
514
|
+
detached: true,
|
|
515
|
+
stdio: 'ignore',
|
|
516
|
+
env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }
|
|
517
|
+
});
|
|
518
|
+
child.unref(); // Don't wait for completion
|
|
519
|
+
|
|
520
|
+
spinner.succeed('Colima startup initiated (background)');
|
|
521
|
+
log.info('Colima is starting in the background');
|
|
522
|
+
log.info('Run "colima status" to check progress');
|
|
523
|
+
return true;
|
|
524
|
+
|
|
525
|
+
} catch (bgError) {
|
|
526
|
+
spinner.fail('Failed to start Colima');
|
|
527
|
+
log.warning('Container runtime startup failed (likely due to macOS system restrictions)');
|
|
528
|
+
log.info('Options to fix:');
|
|
529
|
+
log.info('');
|
|
530
|
+
log.info('Option 1 - Manual Colima start:');
|
|
531
|
+
log.info(' colima start --cpu 2 --memory 4 --kubernetes');
|
|
532
|
+
log.info(' # This downloads a 344MB disk image (may take time)');
|
|
533
|
+
log.info('');
|
|
534
|
+
log.info('Option 2 - Use Homebrew (recommended):');
|
|
535
|
+
log.info(' brew install colima');
|
|
536
|
+
log.info(' colima start --kubernetes');
|
|
537
|
+
log.info('');
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
377
540
|
}
|
|
378
541
|
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
542
|
} else if (platform === 'linux') {
|
|
382
543
|
// Linux - K3s should already be running
|
|
383
544
|
spinner.text = 'Checking K3s status...';
|
|
545
|
+
|
|
546
|
+
// Check if we're in a container (no systemd)
|
|
547
|
+
const isContainer = process.env.container === 'docker' || fs.existsSync('/.dockerenv') || !fs.existsSync('/run/systemd/system');
|
|
548
|
+
|
|
549
|
+
if (isContainer) {
|
|
550
|
+
spinner.info('Running in container - K3s requires systemd');
|
|
551
|
+
log.info('For containers, use KIND or connect to external cluster');
|
|
552
|
+
return true; // Don't fail
|
|
553
|
+
}
|
|
554
|
+
|
|
384
555
|
try {
|
|
385
556
|
execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore' });
|
|
386
557
|
spinner.succeed('K3s is running');
|
|
@@ -394,9 +565,9 @@ async function startContainerRuntime() {
|
|
|
394
565
|
spinner.succeed('K3s started');
|
|
395
566
|
return true;
|
|
396
567
|
} catch (error) {
|
|
397
|
-
spinner.
|
|
398
|
-
log.
|
|
399
|
-
return
|
|
568
|
+
spinner.warn('K3s not available - requires systemd');
|
|
569
|
+
log.info('K3s requires systemd. Install K3s manually or use KIND/external cluster');
|
|
570
|
+
return true; // Don't fail
|
|
400
571
|
}
|
|
401
572
|
}
|
|
402
573
|
} else if (platform === 'win32') {
|
|
@@ -405,7 +576,7 @@ async function startContainerRuntime() {
|
|
|
405
576
|
return false;
|
|
406
577
|
} else {
|
|
407
578
|
spinner.fail(`Unsupported platform: ${platform}`);
|
|
408
|
-
|
|
579
|
+
return false;
|
|
409
580
|
}
|
|
410
581
|
} catch (error) {
|
|
411
582
|
spinner.fail(`Container runtime error: ${error.message}`);
|
|
@@ -434,10 +605,98 @@ async function updatePath() {
|
|
|
434
605
|
process.env.PATH = `${binDir}:${process.env.PATH}`;
|
|
435
606
|
}
|
|
436
607
|
|
|
608
|
+
async function updateShellConfig() {
|
|
609
|
+
const homeDir = os.homedir();
|
|
610
|
+
const pathExport = `export PATH="$HOME/bin:$PATH"`;
|
|
611
|
+
|
|
612
|
+
// Get current shell
|
|
613
|
+
const currentShell = process.env.SHELL || '/bin/sh';
|
|
614
|
+
const shellName = path.basename(currentShell);
|
|
615
|
+
|
|
616
|
+
// Platform-specific shell config files
|
|
617
|
+
const shellConfigs = {
|
|
618
|
+
'zsh': ['.zshrc'],
|
|
619
|
+
'bash': ['.bashrc', '.bash_profile'],
|
|
620
|
+
'sh': ['.profile'],
|
|
621
|
+
'fish': ['.config/fish/config.fish'],
|
|
622
|
+
'ksh': ['.kshrc'],
|
|
623
|
+
'tcsh': ['.tcshrc', '.cshrc']
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// On Linux, also update .profile for login shells
|
|
627
|
+
if (platform === 'linux') {
|
|
628
|
+
shellConfigs['bash'].push('.profile');
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Get config files for current shell
|
|
632
|
+
const configFiles = shellConfigs[shellName] || ['.profile'];
|
|
633
|
+
|
|
634
|
+
let updated = false;
|
|
635
|
+
|
|
636
|
+
for (const configFile of configFiles) {
|
|
637
|
+
const configPath = path.join(homeDir, configFile);
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
// Create config file if it doesn't exist
|
|
641
|
+
if (!fs.existsSync(configPath)) {
|
|
642
|
+
// Handle fish shell special syntax
|
|
643
|
+
if (configFile.includes('fish')) {
|
|
644
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
645
|
+
fs.writeFileSync(configPath, `# Created by TRIBE installer\nset -gx PATH $HOME/bin $PATH\n`);
|
|
646
|
+
} else {
|
|
647
|
+
fs.writeFileSync(configPath, `# Created by TRIBE installer\n${pathExport}\n`);
|
|
648
|
+
}
|
|
649
|
+
updated = true;
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Check if PATH is already configured
|
|
654
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
655
|
+
if (content.includes('$HOME/bin') || content.includes('~/bin')) {
|
|
656
|
+
continue; // Already configured
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Add PATH export
|
|
660
|
+
if (configFile.includes('fish')) {
|
|
661
|
+
fs.appendFileSync(configPath, `\n# Added by TRIBE installer\nset -gx PATH $HOME/bin $PATH\n`);
|
|
662
|
+
} else {
|
|
663
|
+
fs.appendFileSync(configPath, `\n# Added by TRIBE installer\n${pathExport}\n`);
|
|
664
|
+
}
|
|
665
|
+
updated = true;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
log.warning(`Could not update ${configFile}: ${error.message}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (updated) {
|
|
672
|
+
log.success(`Updated shell configuration`);
|
|
673
|
+
|
|
674
|
+
// Linux-specific: also update /etc/environment if we have permission
|
|
675
|
+
if (platform === 'linux' && process.getuid && process.getuid() === 0) {
|
|
676
|
+
try {
|
|
677
|
+
const envPath = '/etc/environment';
|
|
678
|
+
if (fs.existsSync(envPath)) {
|
|
679
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
680
|
+
if (!envContent.includes('/usr/local/bin')) {
|
|
681
|
+
// Update system PATH
|
|
682
|
+
const newPath = envContent.replace(/PATH="([^"]*)"/, 'PATH="/usr/local/bin:$1"');
|
|
683
|
+
fs.writeFileSync(envPath, newPath);
|
|
684
|
+
log.success('Updated system PATH in /etc/environment');
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
} catch (error) {
|
|
688
|
+
// Ignore permission errors
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
log.info(`Run "source ~/${configFiles[0]}" or restart your terminal`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
437
696
|
async function verifyInstallation() {
|
|
438
697
|
const spinner = ora('Verifying installation...').start();
|
|
439
698
|
|
|
440
|
-
const tools = ['
|
|
699
|
+
const tools = ['kubectl', 'tribe', 'colima'];
|
|
441
700
|
const results = {};
|
|
442
701
|
|
|
443
702
|
for (const tool of tools) {
|
|
@@ -446,11 +705,27 @@ async function verifyInstallation() {
|
|
|
446
705
|
|
|
447
706
|
// Special check for container runtime
|
|
448
707
|
let containerWorking = false;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
708
|
+
if (platform === 'darwin') {
|
|
709
|
+
try {
|
|
710
|
+
const colimaStatus = execSync('colima status 2>&1', { encoding: 'utf8' });
|
|
711
|
+
containerWorking = colimaStatus.includes('is running');
|
|
712
|
+
} catch {
|
|
713
|
+
containerWorking = false;
|
|
714
|
+
}
|
|
715
|
+
} else if (platform === 'linux') {
|
|
716
|
+
try {
|
|
717
|
+
// Check if k3s is running
|
|
718
|
+
execSync('systemctl is-active k3s', { stdio: 'ignore' });
|
|
719
|
+
containerWorking = true;
|
|
720
|
+
} catch {
|
|
721
|
+
// Fallback to kubectl
|
|
722
|
+
try {
|
|
723
|
+
execSync('kubectl cluster-info', { stdio: 'ignore' });
|
|
724
|
+
containerWorking = true;
|
|
725
|
+
} catch {
|
|
726
|
+
containerWorking = false;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
454
729
|
}
|
|
455
730
|
|
|
456
731
|
spinner.stop();
|
|
@@ -514,7 +789,14 @@ async function startColimaWithKubernetes() {
|
|
|
514
789
|
// Check if it has Kubernetes enabled
|
|
515
790
|
if (await checkColimaHasKubernetes()) {
|
|
516
791
|
spinner.succeed('Colima is already running with Kubernetes');
|
|
517
|
-
|
|
792
|
+
|
|
793
|
+
// Verify kubectl works
|
|
794
|
+
try {
|
|
795
|
+
execSync('kubectl cluster-info', { stdio: 'ignore' });
|
|
796
|
+
return true;
|
|
797
|
+
} catch {
|
|
798
|
+
spinner.info('Colima is running but kubectl not connected, continuing...');
|
|
799
|
+
}
|
|
518
800
|
} else {
|
|
519
801
|
spinner.text = 'Colima is running without Kubernetes. Restarting with Kubernetes...';
|
|
520
802
|
|
|
@@ -754,19 +1036,26 @@ async function deployTribeCluster() {
|
|
|
754
1036
|
log.info(' colima start --kubernetes');
|
|
755
1037
|
log.info('3. Then run: tribe start');
|
|
756
1038
|
} else {
|
|
757
|
-
|
|
1039
|
+
log.info('You can manually deploy later with: tribe start');
|
|
758
1040
|
}
|
|
759
1041
|
return false;
|
|
760
1042
|
}
|
|
761
1043
|
}
|
|
762
1044
|
|
|
763
1045
|
async function promptForClusterSetup() {
|
|
1046
|
+
// Check for auto-approve in CI environments
|
|
1047
|
+
if (process.env.CI === 'true' || process.env.TRIBE_AUTO_APPROVE === 'true') {
|
|
1048
|
+
console.log('\n' + chalk.bold('🚀 TRIBE Cluster Setup'));
|
|
1049
|
+
console.log(chalk.green('Auto-approving cluster setup (CI mode)'));
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
764
1053
|
// Simple prompt without external dependencies
|
|
765
1054
|
return new Promise((resolve) => {
|
|
766
1055
|
console.log('\n' + chalk.bold('🚀 TRIBE Cluster Setup'));
|
|
767
1056
|
console.log('\nWould you like to set up the TRIBE cluster now?');
|
|
768
1057
|
console.log('This will:');
|
|
769
|
-
console.log(' • Start Colima with Kubernetes');
|
|
1058
|
+
console.log(' • Start ' + (platform === 'darwin' ? 'Colima' : 'K3s') + ' with Kubernetes');
|
|
770
1059
|
console.log(' • Deploy all TRIBE services');
|
|
771
1060
|
console.log(' • Set up port forwarding');
|
|
772
1061
|
console.log('\n' + chalk.yellow('Note: This requires ~2GB disk space and may take a few minutes'));
|
|
@@ -783,12 +1072,37 @@ async function promptForClusterSetup() {
|
|
|
783
1072
|
});
|
|
784
1073
|
}
|
|
785
1074
|
|
|
1075
|
+
async function cleanupOldInstallations() {
|
|
1076
|
+
// Clean up old aliases
|
|
1077
|
+
const rcFiles = ['.zshrc', '.bashrc'];
|
|
1078
|
+
rcFiles.forEach(file => {
|
|
1079
|
+
const rcPath = path.join(homeDir, file);
|
|
1080
|
+
if (fs.existsSync(rcPath)) {
|
|
1081
|
+
try {
|
|
1082
|
+
let content = fs.readFileSync(rcPath, 'utf8');
|
|
1083
|
+
const originalContent = content;
|
|
1084
|
+
|
|
1085
|
+
// Remove old tribe-cli aliases
|
|
1086
|
+
content = content.replace(/^alias tribe=['"]tribe-cli['"]\s*$/gm, '# $& # Removed by TRIBE installer');
|
|
1087
|
+
content = content.replace(/^alias t=['"]tribe-cli['"]\s*$/gm, '# $& # Removed by TRIBE installer');
|
|
1088
|
+
|
|
1089
|
+
if (content !== originalContent) {
|
|
1090
|
+
fs.writeFileSync(rcPath, content);
|
|
1091
|
+
log.info(`Cleaned up old aliases in ${file}`);
|
|
1092
|
+
}
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
log.warning(`Could not clean up ${file}: ${error.message}`);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
786
1100
|
async function main() {
|
|
787
1101
|
console.log(chalk.bold.blue('\n🚀 TRIBE CLI Complete Installer\n'));
|
|
788
1102
|
|
|
789
1103
|
// Detect and display platform info
|
|
790
|
-
const
|
|
791
|
-
console.log(chalk.cyan(`Platform: ${platform} (${
|
|
1104
|
+
const displayArch = process.arch === 'x64' ? 'amd64' : process.arch;
|
|
1105
|
+
console.log(chalk.cyan(`Platform: ${platform} (${displayArch})`));
|
|
792
1106
|
console.log(chalk.cyan(`Node: ${process.version}`));
|
|
793
1107
|
console.log('');
|
|
794
1108
|
|
|
@@ -800,12 +1114,68 @@ async function main() {
|
|
|
800
1114
|
process.exit(1);
|
|
801
1115
|
}
|
|
802
1116
|
|
|
1117
|
+
// Edge case detection
|
|
1118
|
+
// 1. Check for unsupported architectures
|
|
1119
|
+
const supportedArchitectures = ['x64', 'x86_64', 'arm64', 'aarch64'];
|
|
1120
|
+
if (!supportedArchitectures.includes(nodeArch)) {
|
|
1121
|
+
console.log(chalk.red('\n❌ Unsupported Architecture'));
|
|
1122
|
+
console.log(chalk.red(`Your system architecture (${nodeArch}) is not supported.`));
|
|
1123
|
+
console.log(chalk.yellow('TRIBE only provides binaries for x64 (AMD64) and ARM64.\n'));
|
|
1124
|
+
process.exit(1);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// 2. WSL2 detection
|
|
1128
|
+
if (fs.existsSync('/proc/sys/fs/binfmt_misc/WSLInterop') || process.env.WSL_DISTRO_NAME) {
|
|
1129
|
+
console.log(chalk.yellow('\n⚠️ WSL2 Environment Detected'));
|
|
1130
|
+
console.log(chalk.yellow('K3s may have networking limitations in WSL2.'));
|
|
1131
|
+
console.log(chalk.yellow('Consider using Docker Desktop with WSL2 backend instead.\n'));
|
|
1132
|
+
// Don't exit, just warn
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// 3. Proxy detection
|
|
1136
|
+
const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
|
|
1137
|
+
if (proxyUrl) {
|
|
1138
|
+
console.log(chalk.blue('ℹ️ Proxy detected: ' + proxyUrl));
|
|
1139
|
+
console.log(chalk.blue('Downloads will use system proxy settings.\n'));
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// 4. Check for existing Kubernetes installations
|
|
1143
|
+
const k8sTools = [];
|
|
1144
|
+
const checkTools = ['minikube', 'microk8s', 'kind', 'k3d', 'kubectl'];
|
|
1145
|
+
for (const tool of checkTools) {
|
|
1146
|
+
try {
|
|
1147
|
+
execSync(`which ${tool}`, { stdio: 'ignore' });
|
|
1148
|
+
k8sTools.push(tool);
|
|
1149
|
+
} catch {}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (k8sTools.length > 0) {
|
|
1153
|
+
console.log(chalk.blue('\n📦 Existing Kubernetes tools detected: ' + k8sTools.join(', ')));
|
|
1154
|
+
console.log(chalk.blue('You can use --skip-cluster to skip K3s installation.\n'));
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// 5. Disk space check
|
|
1158
|
+
try {
|
|
1159
|
+
const stats = fs.statfsSync(homeDir);
|
|
1160
|
+
const freeGB = (stats.bavail * stats.bsize) / (1024 ** 3);
|
|
1161
|
+
if (freeGB < 3) {
|
|
1162
|
+
console.log(chalk.yellow(`\n⚠️ Low disk space: ${freeGB.toFixed(1)}GB free`));
|
|
1163
|
+
console.log(chalk.yellow('K3s installation requires at least 3GB of free space.\n'));
|
|
1164
|
+
}
|
|
1165
|
+
} catch {}
|
|
1166
|
+
|
|
1167
|
+
// Clean up old installations
|
|
1168
|
+
await cleanupOldInstallations();
|
|
1169
|
+
|
|
803
1170
|
log.info(`Detected: ${platform} (${arch})`);
|
|
804
1171
|
log.info(`Installing to: ${binDir}`);
|
|
805
1172
|
|
|
806
1173
|
// Update PATH first
|
|
807
1174
|
await updatePath();
|
|
808
1175
|
|
|
1176
|
+
// Update shell configuration files
|
|
1177
|
+
await updateShellConfig();
|
|
1178
|
+
|
|
809
1179
|
const tasks = [];
|
|
810
1180
|
|
|
811
1181
|
// Platform-specific Kubernetes installation
|
|
@@ -915,17 +1285,30 @@ async function main() {
|
|
|
915
1285
|
console.log('');
|
|
916
1286
|
// Check container runtime for guidance
|
|
917
1287
|
let runtimeWorking = false;
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1288
|
+
if (platform === 'darwin') {
|
|
1289
|
+
try {
|
|
1290
|
+
const colimaStatus = execSync('colima status 2>&1', { encoding: 'utf8' });
|
|
1291
|
+
runtimeWorking = colimaStatus.includes('is running');
|
|
1292
|
+
} catch {
|
|
1293
|
+
runtimeWorking = false;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
if (!runtimeWorking) {
|
|
1297
|
+
log.info('Start container runtime:');
|
|
1298
|
+
console.log(' colima start --kubernetes # Start Colima with Kubernetes');
|
|
1299
|
+
}
|
|
1300
|
+
} else if (platform === 'linux') {
|
|
1301
|
+
try {
|
|
1302
|
+
execSync('systemctl is-active k3s', { stdio: 'ignore' });
|
|
1303
|
+
runtimeWorking = true;
|
|
1304
|
+
} catch {
|
|
1305
|
+
runtimeWorking = false;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
if (!runtimeWorking) {
|
|
1309
|
+
log.info('Start container runtime:');
|
|
1310
|
+
console.log(' sudo systemctl start k3s # Start k3s');
|
|
1311
|
+
}
|
|
929
1312
|
}
|
|
930
1313
|
console.log('');
|
|
931
1314
|
log.info('Restart your shell or run: source ~/.zshrc');
|
|
@@ -949,7 +1332,7 @@ if (require.main === module) {
|
|
|
949
1332
|
console.log(' --verify Only verify existing installation');
|
|
950
1333
|
console.log(' --dry-run Show what would be installed');
|
|
951
1334
|
console.log(' --skip-cluster Skip cluster deployment (for testing)');
|
|
952
|
-
|
|
1335
|
+
console.log('\nThis installer sets up the complete TRIBE development environment:');
|
|
953
1336
|
|
|
954
1337
|
if (platform === 'darwin') {
|
|
955
1338
|
console.log('• Colima (Container runtime with Kubernetes)');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_xtribe/cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.25",
|
|
4
4
|
"description": "TRIBE multi-agent development system - Zero to productive with one command",
|
|
5
5
|
"main": "install-tribe.js",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"ora": "^5.4.1",
|
|
44
44
|
"node-fetch": "^2.6.7",
|
|
45
45
|
"tar": "^6.1.15",
|
|
46
|
-
"which": "^2.0.2"
|
|
46
|
+
"which": "^2.0.2",
|
|
47
|
+
"adm-zip": "^0.5.9"
|
|
47
48
|
},
|
|
48
49
|
"repository": {
|
|
49
50
|
"type": "git",
|