@_xtribe/cli 1.0.0-beta.3
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 +177 -0
- package/install-tribe.js +686 -0
- package/lima-guestagent.Linux-aarch64.gz +0 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @_xtribe/cli
|
|
2
|
+
|
|
3
|
+
Complete TRIBE multi-agent development environment installer.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @_xtribe/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This single command installs:
|
|
12
|
+
- ✅ Docker CLI
|
|
13
|
+
- ✅ Colima (lightweight container runtime for macOS)
|
|
14
|
+
- ✅ Lima (virtualization layer)
|
|
15
|
+
- ✅ KIND (Kubernetes in Docker)
|
|
16
|
+
- ✅ kubectl (Kubernetes CLI)
|
|
17
|
+
- ✅ TRIBE CLI (multi-agent orchestration)
|
|
18
|
+
|
|
19
|
+
## After Installation
|
|
20
|
+
|
|
21
|
+
Start the container runtime:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Option 1: Use installed Colima
|
|
25
|
+
colima start --cpu 2 --memory 4
|
|
26
|
+
|
|
27
|
+
# Option 2: Use Docker Desktop
|
|
28
|
+
# Download from: https://docs.docker.com/desktop/install/mac-install/
|
|
29
|
+
|
|
30
|
+
# Option 3: Use Homebrew (recommended)
|
|
31
|
+
brew install colima docker
|
|
32
|
+
colima start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then start your TRIBE development environment:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
tribe start
|
|
39
|
+
tribe status
|
|
40
|
+
tribe deploy-task --prompt "Create REST API" --repo "https://github.com/your/repo"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What is TRIBE?
|
|
44
|
+
|
|
45
|
+
TRIBE (Task-Review-Iterate-Branch-Execute) is a multi-agent AI development system that automates software development workflows using multiple Claude AI agents working in parallel.
|
|
46
|
+
|
|
47
|
+
## System Requirements
|
|
48
|
+
|
|
49
|
+
- macOS or Linux
|
|
50
|
+
- Node.js 16+
|
|
51
|
+
- Internet connection for downloads
|
|
52
|
+
|
|
53
|
+
## Support
|
|
54
|
+
|
|
55
|
+
- Issues: https://github.com/0zen/0zen/issues
|
|
56
|
+
- Documentation: https://github.com/0zen/0zen
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
|
61
|
+
|
|
62
|
+
## Overview
|
|
63
|
+
|
|
64
|
+
The TRIBE CLI GUI provides both interactive and non-interactive modes for:
|
|
65
|
+
- Listing and viewing tasks
|
|
66
|
+
- Managing projects
|
|
67
|
+
- Monitoring agents
|
|
68
|
+
- Creating new tasks (interactive mode)
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
cd sdk/cmd/cli-gui
|
|
74
|
+
go build -o cli-gui .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### Interactive Mode
|
|
80
|
+
|
|
81
|
+
Simply run the CLI without arguments:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
./cli-gui
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This will present a menu-driven interface where you can:
|
|
88
|
+
1. List all tasks
|
|
89
|
+
2. List all projects
|
|
90
|
+
3. List all agents
|
|
91
|
+
4. Create a new task
|
|
92
|
+
5. View task details
|
|
93
|
+
|
|
94
|
+
### Non-Interactive Mode
|
|
95
|
+
|
|
96
|
+
Perfect for scripting and automation:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# List all tasks
|
|
100
|
+
./cli-gui --non-interactive --cmd list-tasks
|
|
101
|
+
|
|
102
|
+
# List all projects
|
|
103
|
+
./cli-gui --non-interactive --cmd list-projects
|
|
104
|
+
|
|
105
|
+
# List all agents
|
|
106
|
+
./cli-gui --non-interactive --cmd list-agents
|
|
107
|
+
|
|
108
|
+
# View task details
|
|
109
|
+
./cli-gui --non-interactive --cmd task-details --task-id task-8e4999d6
|
|
110
|
+
|
|
111
|
+
# Get JSON output
|
|
112
|
+
./cli-gui --non-interactive --cmd list-tasks --format json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Environment Variables
|
|
116
|
+
|
|
117
|
+
Configure API endpoints using environment variables:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
export TASKMASTER_URL=http://localhost:8080
|
|
121
|
+
export BRIDGE_URL=http://localhost:3456
|
|
122
|
+
export GITEA_URL=http://localhost:3000
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Command-Line Options
|
|
126
|
+
|
|
127
|
+
- `--non-interactive`: Run in non-interactive mode
|
|
128
|
+
- `--cmd <command>`: Command to execute (required in non-interactive mode)
|
|
129
|
+
- `list-tasks`: List all tasks
|
|
130
|
+
- `list-projects`: List all projects
|
|
131
|
+
- `list-agents`: List all agents
|
|
132
|
+
- `task-details`: View details of a specific task
|
|
133
|
+
- `--task-id <id>`: Task ID (required for task-details command)
|
|
134
|
+
- `--format <format>`: Output format (text or json, default: text)
|
|
135
|
+
|
|
136
|
+
## Examples
|
|
137
|
+
|
|
138
|
+
### List tasks in JSON format
|
|
139
|
+
```bash
|
|
140
|
+
./cli-gui --non-interactive --cmd list-tasks --format json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### View specific task details
|
|
144
|
+
```bash
|
|
145
|
+
./cli-gui --non-interactive --cmd task-details --task-id task-123456
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Use with custom API endpoints
|
|
149
|
+
```bash
|
|
150
|
+
TASKMASTER_URL=http://api.example.com:8080 ./cli-gui --non-interactive --cmd list-tasks
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Testing with Mock Data
|
|
154
|
+
|
|
155
|
+
To test without a running TRIBE cluster, you can use mock endpoints or run against a local instance.
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
### Adding New Commands
|
|
160
|
+
|
|
161
|
+
1. Add the command to the switch statement in `runNonInteractive()`
|
|
162
|
+
2. Implement the corresponding function
|
|
163
|
+
3. Add any necessary API methods to `api.go`
|
|
164
|
+
4. Update this README with the new command
|
|
165
|
+
|
|
166
|
+
### Building for Different Platforms
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Linux
|
|
170
|
+
GOOS=linux GOARCH=amd64 go build -o cli-gui-linux .
|
|
171
|
+
|
|
172
|
+
# macOS
|
|
173
|
+
GOOS=darwin GOARCH=amd64 go build -o cli-gui-macos .
|
|
174
|
+
|
|
175
|
+
# Windows
|
|
176
|
+
GOOS=windows GOARCH=amd64 go build -o cli-gui.exe .
|
|
177
|
+
```
|
package/install-tribe.js
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const ora = require('ora');
|
|
10
|
+
const which = require('which');
|
|
11
|
+
|
|
12
|
+
const platform = os.platform();
|
|
13
|
+
const arch = os.arch();
|
|
14
|
+
const homeDir = os.homedir();
|
|
15
|
+
const binDir = path.join(homeDir, 'bin');
|
|
16
|
+
|
|
17
|
+
// Ensure local bin directory exists
|
|
18
|
+
if (!fs.existsSync(binDir)) {
|
|
19
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const log = {
|
|
23
|
+
success: (msg) => console.log(chalk.green('✓'), msg),
|
|
24
|
+
error: (msg) => console.log(chalk.red('✗'), msg),
|
|
25
|
+
warning: (msg) => console.log(chalk.yellow('⚠'), msg),
|
|
26
|
+
info: (msg) => console.log(chalk.blue('ℹ'), msg),
|
|
27
|
+
step: (msg) => console.log(chalk.cyan('→'), msg)
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
async function checkCommand(cmd) {
|
|
31
|
+
try {
|
|
32
|
+
await which(cmd);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function downloadFile(url, dest) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const file = fs.createWriteStream(dest);
|
|
42
|
+
https.get(url, (response) => {
|
|
43
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
44
|
+
// Handle redirects
|
|
45
|
+
return downloadFile(response.headers.location, dest).then(resolve, reject);
|
|
46
|
+
}
|
|
47
|
+
if (response.statusCode !== 200) {
|
|
48
|
+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
response.pipe(file);
|
|
52
|
+
file.on('finish', () => {
|
|
53
|
+
file.close();
|
|
54
|
+
resolve();
|
|
55
|
+
});
|
|
56
|
+
}).on('error', reject);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function installDocker() {
|
|
61
|
+
if (await checkCommand('docker')) {
|
|
62
|
+
// Check if Docker daemon is running
|
|
63
|
+
try {
|
|
64
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
65
|
+
log.success('Docker is already working');
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
log.warning('Docker CLI found but daemon not running');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const spinner = ora('Installing Docker CLI...').start();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
let dockerUrl;
|
|
76
|
+
if (platform === 'darwin') {
|
|
77
|
+
// Docker uses aarch64 instead of arm64 for macOS
|
|
78
|
+
const dockerArch = arch === 'arm64' ? 'aarch64' : 'x86_64';
|
|
79
|
+
dockerUrl = `https://download.docker.com/mac/static/stable/${dockerArch}/docker-24.0.7.tgz`;
|
|
80
|
+
} else if (platform === 'linux') {
|
|
81
|
+
dockerUrl = `https://download.docker.com/linux/static/stable/${arch}/docker-24.0.7.tgz`;
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const tempFile = path.join(os.tmpdir(), 'docker.tgz');
|
|
87
|
+
await downloadFile(dockerUrl, tempFile);
|
|
88
|
+
|
|
89
|
+
// Extract Docker CLI
|
|
90
|
+
execSync(`tar -xzf ${tempFile} -C ${os.tmpdir()}`);
|
|
91
|
+
const dockerBinary = path.join(os.tmpdir(), 'docker', 'docker');
|
|
92
|
+
const dockerDest = path.join(binDir, 'docker');
|
|
93
|
+
|
|
94
|
+
fs.copyFileSync(dockerBinary, dockerDest);
|
|
95
|
+
fs.chmodSync(dockerDest, '755');
|
|
96
|
+
|
|
97
|
+
// Cleanup
|
|
98
|
+
fs.rmSync(tempFile);
|
|
99
|
+
fs.rmSync(path.join(os.tmpdir(), 'docker'), { recursive: true });
|
|
100
|
+
|
|
101
|
+
spinner.succeed('Docker CLI installed');
|
|
102
|
+
return true;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
spinner.fail(`Docker CLI installation failed: ${error.message}`);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function installColima() {
|
|
110
|
+
if (platform !== 'darwin') {
|
|
111
|
+
log.info('Colima is only needed on macOS - skipping');
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (await checkCommand('colima')) {
|
|
116
|
+
log.success('Colima already installed');
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const spinner = ora('Installing Colima...').start();
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
// Strategy 1: Try Homebrew if available (better signing)
|
|
124
|
+
try {
|
|
125
|
+
execSync('brew --version', { stdio: 'ignore' });
|
|
126
|
+
spinner.text = 'Installing Colima via Homebrew...';
|
|
127
|
+
execSync('brew install colima', { stdio: 'ignore' });
|
|
128
|
+
spinner.succeed('Colima installed via Homebrew');
|
|
129
|
+
return true;
|
|
130
|
+
} catch (brewError) {
|
|
131
|
+
// Homebrew not available, continue with direct download
|
|
132
|
+
spinner.text = 'Installing Colima (direct download)...';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Strategy 2: Direct download with Gatekeeper approval
|
|
136
|
+
const colimaUrl = `https://github.com/abiosoft/colima/releases/latest/download/colima-${platform}-${arch}`;
|
|
137
|
+
const colimaDest = path.join(binDir, 'colima');
|
|
138
|
+
|
|
139
|
+
await downloadFile(colimaUrl, colimaDest);
|
|
140
|
+
fs.chmodSync(colimaDest, '755');
|
|
141
|
+
|
|
142
|
+
// Try to remove quarantine attribute (macOS Sequoia workaround)
|
|
143
|
+
try {
|
|
144
|
+
execSync(`xattr -d com.apple.quarantine ${colimaDest}`, { stdio: 'ignore' });
|
|
145
|
+
log.info('Removed quarantine attribute from Colima');
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// Quarantine attribute may not exist, which is fine
|
|
148
|
+
log.info('Colima installed (quarantine handling not needed)');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
spinner.succeed('Colima installed');
|
|
152
|
+
return true;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
spinner.fail(`Colima installation failed: ${error.message}`);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function installLima() {
|
|
160
|
+
if (platform !== 'darwin') {
|
|
161
|
+
return true; // Lima only needed on macOS
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (await checkCommand('limactl')) {
|
|
165
|
+
log.success('Lima already installed');
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const spinner = ora('Installing Lima...').start();
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
// Strategy 1: Try Homebrew if available (better signing)
|
|
173
|
+
try {
|
|
174
|
+
execSync('brew --version', { stdio: 'ignore' });
|
|
175
|
+
spinner.text = 'Installing Lima via Homebrew...';
|
|
176
|
+
execSync('brew install lima', { stdio: 'ignore' });
|
|
177
|
+
spinner.succeed('Lima installed via Homebrew');
|
|
178
|
+
return true;
|
|
179
|
+
} catch (brewError) {
|
|
180
|
+
// Homebrew not available, continue with direct download
|
|
181
|
+
spinner.text = 'Installing Lima (direct download)...';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Strategy 2: Direct download
|
|
185
|
+
// Get latest Lima release
|
|
186
|
+
const response = await fetch('https://api.github.com/repos/lima-vm/lima/releases/latest');
|
|
187
|
+
const release = await response.json();
|
|
188
|
+
const version = release.tag_name;
|
|
189
|
+
|
|
190
|
+
const archName = arch === 'arm64' ? 'arm64' : 'x86_64';
|
|
191
|
+
const limaUrl = `https://github.com/lima-vm/lima/releases/download/${version}/lima-${version.replace('v', '')}-Darwin-${archName}.tar.gz`;
|
|
192
|
+
|
|
193
|
+
const tempFile = path.join(os.tmpdir(), 'lima.tar.gz');
|
|
194
|
+
await downloadFile(limaUrl, tempFile);
|
|
195
|
+
|
|
196
|
+
// Extract Lima
|
|
197
|
+
const extractDir = path.join(os.tmpdir(), 'lima-extract');
|
|
198
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
199
|
+
execSync(`tar -xzf ${tempFile} -C ${extractDir}`);
|
|
200
|
+
|
|
201
|
+
// Lima tarball extracts directly to bin/ directory
|
|
202
|
+
const limaBinDir = path.join(extractDir, 'bin');
|
|
203
|
+
if (fs.existsSync(limaBinDir)) {
|
|
204
|
+
// Copy all Lima binaries
|
|
205
|
+
const binaries = fs.readdirSync(limaBinDir);
|
|
206
|
+
binaries.forEach(binary => {
|
|
207
|
+
const src = path.join(limaBinDir, binary);
|
|
208
|
+
const dest = path.join(binDir, binary);
|
|
209
|
+
fs.copyFileSync(src, dest);
|
|
210
|
+
fs.chmodSync(dest, '755');
|
|
211
|
+
|
|
212
|
+
// Remove quarantine attribute
|
|
213
|
+
try {
|
|
214
|
+
execSync(`xattr -d com.apple.quarantine ${dest}`, { stdio: 'ignore' });
|
|
215
|
+
} catch (error) {
|
|
216
|
+
// Quarantine attribute may not exist, which is fine
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
throw new Error('Lima binaries not found in expected location');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Copy share directory (required for guest agents)
|
|
224
|
+
const limaShareDir = path.join(extractDir, 'share');
|
|
225
|
+
const destShareDir = path.join(homeDir, 'share');
|
|
226
|
+
if (fs.existsSync(limaShareDir)) {
|
|
227
|
+
if (!fs.existsSync(destShareDir)) {
|
|
228
|
+
fs.mkdirSync(destShareDir, { recursive: true });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
// Create lima directory in share
|
|
233
|
+
const limaDir = path.join(destShareDir, 'lima');
|
|
234
|
+
if (!fs.existsSync(limaDir)) {
|
|
235
|
+
fs.mkdirSync(limaDir, { recursive: true });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Strategy 1: Try bundled guest agent (packaged with NPX)
|
|
239
|
+
const bundledGuestAgent = path.join(__dirname, 'lima-guestagent.Linux-aarch64.gz');
|
|
240
|
+
if (fs.existsSync(bundledGuestAgent)) {
|
|
241
|
+
log.info('Using bundled Lima guest agent');
|
|
242
|
+
const guestAgentDest = path.join(limaDir, 'lima-guestagent.Linux-aarch64.gz');
|
|
243
|
+
fs.copyFileSync(bundledGuestAgent, guestAgentDest);
|
|
244
|
+
|
|
245
|
+
// Extract the guest agent (Colima needs it uncompressed)
|
|
246
|
+
try {
|
|
247
|
+
execSync(`gunzip -f "${guestAgentDest}"`);
|
|
248
|
+
} catch (gunzipError) {
|
|
249
|
+
// If gunzip fails, try manual extraction
|
|
250
|
+
const zlib = require('zlib');
|
|
251
|
+
const compressed = fs.readFileSync(guestAgentDest);
|
|
252
|
+
const decompressed = zlib.gunzipSync(compressed);
|
|
253
|
+
const extractedPath = guestAgentDest.replace('.gz', '');
|
|
254
|
+
fs.writeFileSync(extractedPath, decompressed);
|
|
255
|
+
fs.unlinkSync(guestAgentDest); // Remove compressed version
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Create basic default template for Colima
|
|
259
|
+
const templatesDir = path.join(limaDir, 'templates');
|
|
260
|
+
if (!fs.existsSync(templatesDir)) {
|
|
261
|
+
fs.mkdirSync(templatesDir, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const defaultTemplate = `# Basic default template for Colima
|
|
265
|
+
images:
|
|
266
|
+
- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
|
267
|
+
arch: "x86_64"
|
|
268
|
+
- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img"
|
|
269
|
+
arch: "aarch64"
|
|
270
|
+
|
|
271
|
+
mounts:
|
|
272
|
+
- location: "~"
|
|
273
|
+
writable: false
|
|
274
|
+
- location: "/tmp/lima"
|
|
275
|
+
writable: true
|
|
276
|
+
|
|
277
|
+
containerd:
|
|
278
|
+
system: false
|
|
279
|
+
user: false
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
fs.writeFileSync(path.join(templatesDir, 'default.yaml'), defaultTemplate);
|
|
283
|
+
log.info('Created basic Lima template');
|
|
284
|
+
|
|
285
|
+
} else {
|
|
286
|
+
// Strategy 2: Try from tarball (fallback)
|
|
287
|
+
const guestAgentSrc = path.join(limaShareDir, 'lima-guestagent.Linux-aarch64.gz');
|
|
288
|
+
const templatesSrc = path.join(limaShareDir, 'templates');
|
|
289
|
+
|
|
290
|
+
if (fs.existsSync(guestAgentSrc)) {
|
|
291
|
+
// Copy and extract guest agent
|
|
292
|
+
const guestAgentDest = path.join(limaDir, 'lima-guestagent.Linux-aarch64.gz');
|
|
293
|
+
fs.copyFileSync(guestAgentSrc, guestAgentDest);
|
|
294
|
+
|
|
295
|
+
// Extract the guest agent (Colima needs it uncompressed)
|
|
296
|
+
try {
|
|
297
|
+
execSync(`gunzip -f "${guestAgentDest}"`);
|
|
298
|
+
} catch (gunzipError) {
|
|
299
|
+
// If gunzip fails, try manual extraction
|
|
300
|
+
const zlib = require('zlib');
|
|
301
|
+
const compressed = fs.readFileSync(guestAgentDest);
|
|
302
|
+
const decompressed = zlib.gunzipSync(compressed);
|
|
303
|
+
const extractedPath = guestAgentDest.replace('.gz', '');
|
|
304
|
+
fs.writeFileSync(extractedPath, decompressed);
|
|
305
|
+
fs.unlinkSync(guestAgentDest); // Remove compressed version
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
throw new Error('Lima guest agent not found in tarball or bundled');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (fs.existsSync(templatesSrc)) {
|
|
312
|
+
// Copy templates directory
|
|
313
|
+
execSync(`cp -R "${templatesSrc}" "${limaDir}/templates"`);
|
|
314
|
+
} else {
|
|
315
|
+
// Create minimal template as fallback
|
|
316
|
+
const templatesDir = path.join(limaDir, 'templates');
|
|
317
|
+
fs.mkdirSync(templatesDir, { recursive: true });
|
|
318
|
+
fs.writeFileSync(path.join(templatesDir, 'default.yaml'), defaultTemplate);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
} catch (error) {
|
|
323
|
+
log.warning(`Lima share installation failed: ${error.message}`);
|
|
324
|
+
log.warning('You may need to install Lima manually: brew install lima');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Cleanup
|
|
329
|
+
fs.rmSync(tempFile);
|
|
330
|
+
fs.rmSync(extractDir, { recursive: true });
|
|
331
|
+
|
|
332
|
+
spinner.succeed('Lima installed');
|
|
333
|
+
return true;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
spinner.fail(`Lima installation failed: ${error.message}`);
|
|
336
|
+
log.warning('You may need to install Lima manually: brew install lima');
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function installKind() {
|
|
342
|
+
if (await checkCommand('kind')) {
|
|
343
|
+
log.success('KIND already installed');
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const spinner = ora('Installing KIND...').start();
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const kindUrl = `https://kind.sigs.k8s.io/dl/v0.20.0/kind-${platform}-${arch}`;
|
|
351
|
+
const kindDest = path.join(binDir, 'kind');
|
|
352
|
+
|
|
353
|
+
await downloadFile(kindUrl, kindDest);
|
|
354
|
+
fs.chmodSync(kindDest, '755');
|
|
355
|
+
|
|
356
|
+
spinner.succeed('KIND installed');
|
|
357
|
+
return true;
|
|
358
|
+
} catch (error) {
|
|
359
|
+
spinner.fail(`KIND installation failed: ${error.message}`);
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function installKubectl() {
|
|
365
|
+
if (await checkCommand('kubectl')) {
|
|
366
|
+
log.success('kubectl already installed');
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const spinner = ora('Installing kubectl...').start();
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
// Get latest stable version
|
|
374
|
+
const versionResponse = await fetch('https://dl.k8s.io/release/stable.txt');
|
|
375
|
+
const version = await versionResponse.text();
|
|
376
|
+
const kubectlUrl = `https://dl.k8s.io/release/${version.trim()}/bin/${platform}/${arch}/kubectl`;
|
|
377
|
+
const kubectlDest = path.join(binDir, 'kubectl');
|
|
378
|
+
|
|
379
|
+
await downloadFile(kubectlUrl, kubectlDest);
|
|
380
|
+
fs.chmodSync(kubectlDest, '755');
|
|
381
|
+
|
|
382
|
+
spinner.succeed('kubectl installed');
|
|
383
|
+
return true;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
spinner.fail(`kubectl installation failed: ${error.message}`);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function installTribeCli() {
|
|
391
|
+
const spinner = ora('Installing TRIBE CLI...').start();
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
// Check if we have local source
|
|
395
|
+
const sourceFile = path.join(__dirname, 'cluster-cli.go');
|
|
396
|
+
if (fs.existsSync(sourceFile)) {
|
|
397
|
+
// Build from source
|
|
398
|
+
try {
|
|
399
|
+
execSync('go version', { stdio: 'ignore' });
|
|
400
|
+
execSync(`cd ${__dirname} && go build -o tribe cluster-cli.go client.go`);
|
|
401
|
+
const tribeDest = path.join(binDir, 'tribe');
|
|
402
|
+
fs.copyFileSync(path.join(__dirname, 'tribe'), tribeDest);
|
|
403
|
+
fs.chmodSync(tribeDest, '755');
|
|
404
|
+
spinner.succeed('TRIBE CLI built from source');
|
|
405
|
+
return true;
|
|
406
|
+
} catch {
|
|
407
|
+
spinner.warn('Go not available, trying pre-built binary...');
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Try pre-built binary
|
|
412
|
+
const tribeUrl = `https://github.com/0zen/0zen/releases/latest/download/tribe-${platform}-${arch}`;
|
|
413
|
+
const tribeDest = path.join(binDir, 'tribe');
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
await downloadFile(tribeUrl, tribeDest);
|
|
417
|
+
fs.chmodSync(tribeDest, '755');
|
|
418
|
+
spinner.succeed('TRIBE CLI installed');
|
|
419
|
+
return true;
|
|
420
|
+
} catch {
|
|
421
|
+
// Fallback: copy local binary if it exists
|
|
422
|
+
const localBinary = path.join(__dirname, 'tribe');
|
|
423
|
+
if (fs.existsSync(localBinary)) {
|
|
424
|
+
fs.copyFileSync(localBinary, tribeDest);
|
|
425
|
+
fs.chmodSync(tribeDest, '755');
|
|
426
|
+
spinner.succeed('TRIBE CLI installed from local binary');
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
throw new Error('No TRIBE CLI binary available');
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
spinner.fail(`TRIBE CLI installation failed: ${error.message}`);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function startContainerRuntime() {
|
|
438
|
+
if (platform !== 'darwin') {
|
|
439
|
+
return true; // Linux uses Docker daemon directly
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Check if container runtime is already working
|
|
443
|
+
try {
|
|
444
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
445
|
+
log.success('Container runtime is already working');
|
|
446
|
+
return true;
|
|
447
|
+
} catch {
|
|
448
|
+
// Try to start Colima with different approaches
|
|
449
|
+
if (await checkCommand('colima')) {
|
|
450
|
+
const spinner = ora('Starting Colima container runtime...').start();
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
// Strategy 1: Quick start with minimal resources
|
|
454
|
+
spinner.text = 'Starting Colima (minimal setup)...';
|
|
455
|
+
execSync('colima start --cpu 1 --memory 2 --disk 5 --vm-type=vz', {
|
|
456
|
+
stdio: 'pipe',
|
|
457
|
+
timeout: 30000 // 30 second timeout
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Test if it worked
|
|
461
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
462
|
+
spinner.succeed('Colima started successfully');
|
|
463
|
+
return true;
|
|
464
|
+
|
|
465
|
+
} catch (error) {
|
|
466
|
+
// Strategy 2: Start in background and don't wait
|
|
467
|
+
try {
|
|
468
|
+
spinner.text = 'Starting Colima in background...';
|
|
469
|
+
const child = spawn(path.join(binDir, 'colima'), ['start', '--cpu', '2', '--memory', '4'], {
|
|
470
|
+
detached: true,
|
|
471
|
+
stdio: 'ignore',
|
|
472
|
+
env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }
|
|
473
|
+
});
|
|
474
|
+
child.unref(); // Don't wait for completion
|
|
475
|
+
|
|
476
|
+
spinner.succeed('Colima startup initiated (background)');
|
|
477
|
+
log.info('Colima is starting in the background');
|
|
478
|
+
log.info('Run "colima status" to check progress');
|
|
479
|
+
log.info('Run "docker info" to test when ready');
|
|
480
|
+
return true;
|
|
481
|
+
|
|
482
|
+
} catch (bgError) {
|
|
483
|
+
spinner.fail('Failed to start Colima');
|
|
484
|
+
log.warning('Container runtime startup failed (likely due to macOS system restrictions)');
|
|
485
|
+
log.info('Options to fix:');
|
|
486
|
+
log.info('');
|
|
487
|
+
log.info('Option 1 - Manual Colima start:');
|
|
488
|
+
log.info(' colima start --cpu 2 --memory 4 # Start container runtime');
|
|
489
|
+
log.info(' # This downloads a 344MB disk image (may take time)');
|
|
490
|
+
log.info('');
|
|
491
|
+
log.info('Option 2 - Use Docker Desktop (easier):');
|
|
492
|
+
log.info(' Download from: https://docs.docker.com/desktop/install/mac-install/');
|
|
493
|
+
log.info(' # Docker Desktop handles all container runtime setup');
|
|
494
|
+
log.info('');
|
|
495
|
+
log.info('Option 3 - Use Homebrew (recommended):');
|
|
496
|
+
log.info(' brew install colima docker');
|
|
497
|
+
log.info(' colima start');
|
|
498
|
+
log.info('');
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async function updatePath() {
|
|
508
|
+
const shell = process.env.SHELL || '/bin/zsh';
|
|
509
|
+
const rcFile = shell.includes('zsh') ? '.zshrc' : '.bashrc';
|
|
510
|
+
const rcPath = path.join(homeDir, rcFile);
|
|
511
|
+
|
|
512
|
+
const pathExport = `export PATH="${binDir}:$PATH"`;
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
const rcContent = fs.existsSync(rcPath) ? fs.readFileSync(rcPath, 'utf8') : '';
|
|
516
|
+
if (!rcContent.includes(pathExport)) {
|
|
517
|
+
fs.appendFileSync(rcPath, `\n# Added by TRIBE CLI installer\n${pathExport}\n`);
|
|
518
|
+
log.success(`Updated ${rcFile} with PATH`);
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
log.warning(`Failed to update ${rcFile}: ${error.message}`);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Update current process PATH
|
|
525
|
+
process.env.PATH = `${binDir}:${process.env.PATH}`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async function verifyInstallation() {
|
|
529
|
+
const spinner = ora('Verifying installation...').start();
|
|
530
|
+
|
|
531
|
+
const tools = ['docker', 'kind', 'kubectl', 'tribe'];
|
|
532
|
+
const results = {};
|
|
533
|
+
|
|
534
|
+
for (const tool of tools) {
|
|
535
|
+
results[tool] = await checkCommand(tool);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Special check for container runtime
|
|
539
|
+
let containerWorking = false;
|
|
540
|
+
try {
|
|
541
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
542
|
+
containerWorking = true;
|
|
543
|
+
} catch (error) {
|
|
544
|
+
containerWorking = false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
spinner.stop();
|
|
548
|
+
|
|
549
|
+
console.log('\n' + chalk.bold('Installation Summary:'));
|
|
550
|
+
tools.forEach(tool => {
|
|
551
|
+
const status = results[tool] ? chalk.green('✓') : chalk.red('✗');
|
|
552
|
+
console.log(`${status} ${tool}`);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const extraStatus = containerWorking ? chalk.green('✓') : chalk.yellow('⚠');
|
|
556
|
+
console.log(`${extraStatus} Container runtime`);
|
|
557
|
+
|
|
558
|
+
if (platform === 'darwin') {
|
|
559
|
+
const colimaInstalled = await checkCommand('colima');
|
|
560
|
+
const limaInstalled = await checkCommand('limactl');
|
|
561
|
+
console.log(`${colimaInstalled ? chalk.green('✓') : chalk.yellow('⚠')} Colima`);
|
|
562
|
+
console.log(`${limaInstalled ? chalk.green('✓') : chalk.yellow('⚠')} Lima`);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return Object.values(results).every(r => r) && containerWorking;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async function main() {
|
|
569
|
+
console.log(chalk.bold.blue('\n🚀 TRIBE CLI Complete Installer\n'));
|
|
570
|
+
|
|
571
|
+
log.info(`Detected: ${platform} (${arch})`);
|
|
572
|
+
log.info(`Installing to: ${binDir}`);
|
|
573
|
+
|
|
574
|
+
// Update PATH first
|
|
575
|
+
await updatePath();
|
|
576
|
+
|
|
577
|
+
const tasks = [
|
|
578
|
+
{ name: 'Docker CLI', fn: installDocker },
|
|
579
|
+
{ name: 'Colima', fn: installColima },
|
|
580
|
+
{ name: 'Lima', fn: installLima },
|
|
581
|
+
{ name: 'KIND', fn: installKind },
|
|
582
|
+
{ name: 'kubectl', fn: installKubectl },
|
|
583
|
+
{ name: 'TRIBE CLI', fn: installTribeCli }
|
|
584
|
+
];
|
|
585
|
+
|
|
586
|
+
let allSuccess = true;
|
|
587
|
+
|
|
588
|
+
for (const task of tasks) {
|
|
589
|
+
log.step(`Installing ${task.name}...`);
|
|
590
|
+
const success = await task.fn();
|
|
591
|
+
if (!success) allSuccess = false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Try to start container runtime
|
|
595
|
+
await startContainerRuntime();
|
|
596
|
+
|
|
597
|
+
// Verify everything
|
|
598
|
+
const verified = await verifyInstallation();
|
|
599
|
+
|
|
600
|
+
console.log('\n' + chalk.bold('Next Steps:'));
|
|
601
|
+
if (verified) {
|
|
602
|
+
log.success('All components installed successfully! 🎉');
|
|
603
|
+
console.log('');
|
|
604
|
+
log.info('Quick start:');
|
|
605
|
+
console.log(' tribe start # Start TRIBE cluster');
|
|
606
|
+
console.log(' tribe status # Check cluster status');
|
|
607
|
+
console.log(' tribe deploy-task # Deploy tasks to agents');
|
|
608
|
+
console.log('');
|
|
609
|
+
log.info('For help: tribe --help');
|
|
610
|
+
} else {
|
|
611
|
+
log.warning('Some components need attention:');
|
|
612
|
+
console.log('');
|
|
613
|
+
// Check container runtime for guidance
|
|
614
|
+
let runtimeWorking = false;
|
|
615
|
+
try {
|
|
616
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
617
|
+
runtimeWorking = true;
|
|
618
|
+
} catch {
|
|
619
|
+
runtimeWorking = false;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (!runtimeWorking) {
|
|
623
|
+
log.info('Start container runtime:');
|
|
624
|
+
console.log(' colima start # macOS');
|
|
625
|
+
console.log(' sudo systemctl start docker # Linux');
|
|
626
|
+
}
|
|
627
|
+
console.log('');
|
|
628
|
+
log.info('Restart your shell or run: source ~/.zshrc');
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Handle fetch polyfill for older Node versions
|
|
633
|
+
if (!global.fetch) {
|
|
634
|
+
global.fetch = require('node-fetch');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (require.main === module) {
|
|
638
|
+
const args = process.argv.slice(2);
|
|
639
|
+
|
|
640
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
641
|
+
console.log(chalk.bold.blue('TRIBE CLI Installer\n'));
|
|
642
|
+
console.log('Usage: npx tribe-cli-local [options]\n');
|
|
643
|
+
console.log('Options:');
|
|
644
|
+
console.log(' --help, -h Show this help message');
|
|
645
|
+
console.log(' --verify Only verify existing installation');
|
|
646
|
+
console.log(' --dry-run Show what would be installed');
|
|
647
|
+
console.log('\nThis installer sets up the complete TRIBE development environment:');
|
|
648
|
+
console.log('• Docker CLI + Colima (macOS container runtime)');
|
|
649
|
+
console.log('• KIND (Kubernetes in Docker)');
|
|
650
|
+
console.log('• kubectl (Kubernetes CLI)');
|
|
651
|
+
console.log('• TRIBE CLI (Multi-agent orchestration)');
|
|
652
|
+
console.log('\nAfter installation:');
|
|
653
|
+
console.log(' tribe start # Start TRIBE cluster');
|
|
654
|
+
console.log(' tribe status # Check cluster status');
|
|
655
|
+
process.exit(0);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (args.includes('--verify')) {
|
|
659
|
+
console.log(chalk.bold.blue('🔍 Verifying TRIBE Installation\n'));
|
|
660
|
+
verifyInstallation().then(success => {
|
|
661
|
+
process.exit(success ? 0 : 1);
|
|
662
|
+
});
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (args.includes('--dry-run')) {
|
|
667
|
+
console.log(chalk.bold.blue('🔍 TRIBE CLI Installation Preview\n'));
|
|
668
|
+
log.info(`Platform: ${platform} (${arch})`);
|
|
669
|
+
log.info(`Install directory: ${binDir}`);
|
|
670
|
+
console.log('\nWould install:');
|
|
671
|
+
console.log('• Docker CLI (if not present)');
|
|
672
|
+
console.log('• Colima container runtime (macOS only)');
|
|
673
|
+
console.log('• Lima virtualization (macOS only)');
|
|
674
|
+
console.log('• KIND - Kubernetes in Docker');
|
|
675
|
+
console.log('• kubectl - Kubernetes CLI');
|
|
676
|
+
console.log('• TRIBE CLI - Multi-agent system');
|
|
677
|
+
process.exit(0);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
main().catch(error => {
|
|
681
|
+
console.error(chalk.red('Installation failed:'), error.message);
|
|
682
|
+
process.exit(1);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
module.exports = { main };
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@_xtribe/cli",
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
|
+
"description": "TRIBE multi-agent development system CLI installer",
|
|
5
|
+
"main": "install-tribe.js",
|
|
6
|
+
"bin": "./install-tribe.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"tribe",
|
|
12
|
+
"cli",
|
|
13
|
+
"kubernetes",
|
|
14
|
+
"docker",
|
|
15
|
+
"colima",
|
|
16
|
+
"multi-agent",
|
|
17
|
+
"ai",
|
|
18
|
+
"development"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=16.0.0"
|
|
22
|
+
},
|
|
23
|
+
"os": [
|
|
24
|
+
"darwin",
|
|
25
|
+
"linux"
|
|
26
|
+
],
|
|
27
|
+
"preferGlobal": true,
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org/"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"install-tribe.js",
|
|
34
|
+
"lima-guestagent.Linux-aarch64.gz",
|
|
35
|
+
"README.md",
|
|
36
|
+
"package.json"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"chalk": "^4.1.2",
|
|
40
|
+
"ora": "^5.4.1",
|
|
41
|
+
"node-fetch": "^2.6.7",
|
|
42
|
+
"tar": "^6.1.15",
|
|
43
|
+
"which": "^2.0.2"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/0zen/0zen.git"
|
|
48
|
+
},
|
|
49
|
+
"author": "0zen",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"homepage": "https://github.com/0zen/0zen",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/0zen/0zen/issues"
|
|
54
|
+
}
|
|
55
|
+
}
|