@_xtribe/cli 1.0.0-beta.18 ā 1.0.0-beta.22
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/install-tribe.js +255 -74
- package/package.json +1 -4
- package/lima-guestagent.Linux-aarch64.gz +0 -0
- package/tribe +0 -0
- package/tribe-deployment.yaml +0 -455
package/install-tribe.js
CHANGED
|
@@ -93,6 +93,60 @@ async function downloadFile(url, dest) {
|
|
|
93
93
|
|
|
94
94
|
// Docker installation removed - Colima provides Docker runtime
|
|
95
95
|
|
|
96
|
+
async function installK3s() {
|
|
97
|
+
if (platform !== 'linux') {
|
|
98
|
+
return true; // K3s is Linux only
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const spinner = ora('Installing K3s (lightweight Kubernetes)...').start();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Check if already installed
|
|
105
|
+
try {
|
|
106
|
+
execSync('k3s --version', { stdio: 'ignore' });
|
|
107
|
+
spinner.succeed('K3s already installed');
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
// Not installed, continue
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// One-line K3s installation
|
|
114
|
+
spinner.text = 'Installing K3s (this may take a few minutes)...';
|
|
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'));
|
|
121
|
+
|
|
122
|
+
execSync('curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode 644', {
|
|
123
|
+
stdio: 'inherit',
|
|
124
|
+
shell: '/bin/bash'
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Wait for K3s to be ready
|
|
128
|
+
spinner.text = 'Waiting for K3s to be ready...';
|
|
129
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
130
|
+
|
|
131
|
+
// Make kubectl available
|
|
132
|
+
try {
|
|
133
|
+
execSync('sudo ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl', { stdio: 'ignore' });
|
|
134
|
+
} catch {
|
|
135
|
+
// Link might already exist
|
|
136
|
+
}
|
|
137
|
+
|
|
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
|
+
} catch (error) {
|
|
144
|
+
spinner.fail(`K3s installation failed: ${error.message}`);
|
|
145
|
+
log.info('You can install K3s manually with: curl -sfL https://get.k3s.io | sh -');
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
96
150
|
async function installColima() {
|
|
97
151
|
if (platform !== 'darwin') {
|
|
98
152
|
log.info('Colima is only needed on macOS - skipping');
|
|
@@ -149,6 +203,23 @@ async function installColima() {
|
|
|
149
203
|
// KIND installation removed - using Colima's built-in Kubernetes
|
|
150
204
|
|
|
151
205
|
async function installKubectl() {
|
|
206
|
+
// Linux with K3s already has kubectl
|
|
207
|
+
if (platform === 'linux') {
|
|
208
|
+
try {
|
|
209
|
+
execSync('k3s kubectl version --client', { stdio: 'ignore' });
|
|
210
|
+
log.success('kubectl available via k3s');
|
|
211
|
+
// Create a symlink for convenience
|
|
212
|
+
try {
|
|
213
|
+
execSync('sudo ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl', { stdio: 'ignore' });
|
|
214
|
+
} catch {
|
|
215
|
+
// Link might already exist
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
} catch {
|
|
219
|
+
// Continue with regular kubectl installation
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
152
223
|
if (await checkCommand('kubectl')) {
|
|
153
224
|
log.success('kubectl already installed');
|
|
154
225
|
return true;
|
|
@@ -174,65 +245,63 @@ async function installKubectl() {
|
|
|
174
245
|
}
|
|
175
246
|
}
|
|
176
247
|
|
|
177
|
-
async function
|
|
248
|
+
async function installTribeCLI() {
|
|
178
249
|
const spinner = ora('Installing TRIBE CLI...').start();
|
|
179
250
|
|
|
180
251
|
try {
|
|
181
252
|
const tribeDest = path.join(binDir, 'tribe');
|
|
182
253
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
if (fs.existsSync(bundledBinary)) {
|
|
186
|
-
fs.copyFileSync(bundledBinary, tribeDest);
|
|
187
|
-
fs.chmodSync(tribeDest, '755');
|
|
188
|
-
spinner.succeed('TRIBE CLI installed from bundled binary');
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Check if we have local source
|
|
193
|
-
const sourceFile = path.join(__dirname, 'cluster-cli.go');
|
|
194
|
-
if (fs.existsSync(sourceFile)) {
|
|
195
|
-
// Build from source
|
|
254
|
+
// Skip if already installed and working
|
|
255
|
+
if (fs.existsSync(tribeDest)) {
|
|
196
256
|
try {
|
|
197
|
-
execSync(
|
|
198
|
-
|
|
199
|
-
fs.copyFileSync(path.join(__dirname, 'tribe'), tribeDest);
|
|
200
|
-
fs.chmodSync(tribeDest, '755');
|
|
201
|
-
spinner.succeed('TRIBE CLI built from source');
|
|
257
|
+
execSync(`${tribeDest} version`, { stdio: 'ignore' });
|
|
258
|
+
spinner.succeed('TRIBE CLI already installed');
|
|
202
259
|
return true;
|
|
203
260
|
} catch {
|
|
204
|
-
|
|
261
|
+
// Binary exists but doesn't work, remove it
|
|
262
|
+
fs.unlinkSync(tribeDest);
|
|
205
263
|
}
|
|
206
264
|
}
|
|
207
265
|
|
|
208
|
-
//
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
266
|
+
// Download pre-built binary from GitHub
|
|
267
|
+
const arch = process.arch === 'x64' ? 'amd64' : 'arm64';
|
|
268
|
+
const platform = os.platform();
|
|
269
|
+
|
|
270
|
+
// Multiple sources for reliability
|
|
271
|
+
const sources = [
|
|
272
|
+
// GitHub releases (primary source)
|
|
273
|
+
`https://github.com/TRIBE-INC/0zen/releases/latest/download/tribe-${platform}-${arch}`,
|
|
274
|
+
// Try jsDelivr CDN as fallback
|
|
275
|
+
`https://cdn.jsdelivr.net/gh/TRIBE-INC/0zen@latest/releases/tribe-${platform}-${arch}`,
|
|
276
|
+
// Direct raw GitHub as last resort
|
|
277
|
+
`https://raw.githubusercontent.com/TRIBE-INC/0zen/main/releases/tribe-${platform}-${arch}`
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
let downloaded = false;
|
|
281
|
+
let lastError;
|
|
282
|
+
|
|
283
|
+
for (const url of sources) {
|
|
284
|
+
spinner.text = `Downloading TRIBE CLI from ${url.includes('jsdelivr') ? 'CDN' : 'GitHub'}...`;
|
|
285
|
+
try {
|
|
286
|
+
await downloadFile(url, tribeDest);
|
|
287
|
+
fs.chmodSync(tribeDest, '755');
|
|
288
|
+
|
|
289
|
+
// Verify the binary works
|
|
290
|
+
execSync(`${tribeDest} version`, { stdio: 'ignore' });
|
|
291
|
+
spinner.succeed('TRIBE CLI installed successfully');
|
|
292
|
+
downloaded = true;
|
|
293
|
+
break;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
lastError = error;
|
|
296
|
+
log.warning(`Failed to download from ${url.split('/').slice(2, 3).join('/')}, trying next source...`);
|
|
232
297
|
}
|
|
233
|
-
|
|
234
|
-
throw new Error('No TRIBE CLI binary available');
|
|
235
298
|
}
|
|
299
|
+
|
|
300
|
+
if (!downloaded) {
|
|
301
|
+
throw new Error(`Failed to download TRIBE CLI: ${lastError?.message || 'All sources failed'}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return true;
|
|
236
305
|
} catch (error) {
|
|
237
306
|
spinner.fail(`TRIBE CLI installation failed: ${error.message}`);
|
|
238
307
|
return false;
|
|
@@ -240,17 +309,18 @@ async function installTribeCli() {
|
|
|
240
309
|
}
|
|
241
310
|
|
|
242
311
|
async function startContainerRuntime() {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Check if container runtime is already working
|
|
312
|
+
const spinner = ora('Starting container runtime...').start();
|
|
313
|
+
|
|
248
314
|
try {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
315
|
+
// Platform-specific container runtime handling
|
|
316
|
+
if (platform === 'darwin') {
|
|
317
|
+
// macOS - Check if container runtime is already working via Colima
|
|
318
|
+
try {
|
|
319
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
320
|
+
log.success('Container runtime is already working');
|
|
321
|
+
return true;
|
|
322
|
+
} catch {
|
|
323
|
+
// Try to start Colima with different approaches
|
|
254
324
|
if (await checkCommand('colima')) {
|
|
255
325
|
const spinner = ora('Starting Colima container runtime...').start();
|
|
256
326
|
|
|
@@ -307,8 +377,40 @@ async function startContainerRuntime() {
|
|
|
307
377
|
}
|
|
308
378
|
}
|
|
309
379
|
}
|
|
380
|
+
}
|
|
381
|
+
} else if (platform === 'linux') {
|
|
382
|
+
// Linux - K3s should already be running
|
|
383
|
+
spinner.text = 'Checking K3s status...';
|
|
384
|
+
try {
|
|
385
|
+
execSync('sudo systemctl is-active --quiet k3s', { stdio: 'ignore' });
|
|
386
|
+
spinner.succeed('K3s is running');
|
|
387
|
+
return true;
|
|
388
|
+
} catch {
|
|
389
|
+
// Try to start K3s
|
|
390
|
+
spinner.text = 'Starting K3s...';
|
|
391
|
+
try {
|
|
392
|
+
execSync('sudo systemctl start k3s', { stdio: 'ignore' });
|
|
393
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
394
|
+
spinner.succeed('K3s started');
|
|
395
|
+
return true;
|
|
396
|
+
} catch (error) {
|
|
397
|
+
spinner.fail('Failed to start K3s');
|
|
398
|
+
log.error('Please ensure K3s is installed and you have sudo access');
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} else if (platform === 'win32') {
|
|
403
|
+
spinner.fail('Windows support coming soon!');
|
|
404
|
+
log.info('For Windows, please use WSL2 with Ubuntu and run this installer inside WSL2');
|
|
405
|
+
return false;
|
|
406
|
+
} else {
|
|
407
|
+
spinner.fail(`Unsupported platform: ${platform}`);
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
} catch (error) {
|
|
411
|
+
spinner.fail(`Container runtime error: ${error.message}`);
|
|
412
|
+
return false;
|
|
310
413
|
}
|
|
311
|
-
return false;
|
|
312
414
|
}
|
|
313
415
|
|
|
314
416
|
async function updatePath() {
|
|
@@ -488,6 +590,32 @@ async function deployTribeCluster() {
|
|
|
488
590
|
if (fs.existsSync(sourceYaml)) {
|
|
489
591
|
fs.copyFileSync(sourceYaml, destYaml);
|
|
490
592
|
log.info('Copied deployment configuration');
|
|
593
|
+
} else {
|
|
594
|
+
// Download deployment YAML if not bundled
|
|
595
|
+
spinner.text = 'Downloading deployment configuration...';
|
|
596
|
+
const yamlSources = [
|
|
597
|
+
// Direct from GitHub (primary)
|
|
598
|
+
'https://raw.githubusercontent.com/TRIBE-INC/0zen/main/sdk/cmd/cli-gui/package/tribe-deployment.yaml',
|
|
599
|
+
// jsDelivr CDN as fallback
|
|
600
|
+
'https://cdn.jsdelivr.net/gh/TRIBE-INC/0zen@main/sdk/cmd/cli-gui/package/tribe-deployment.yaml'
|
|
601
|
+
];
|
|
602
|
+
|
|
603
|
+
let yamlDownloaded = false;
|
|
604
|
+
for (const yamlUrl of yamlSources) {
|
|
605
|
+
try {
|
|
606
|
+
await downloadFile(yamlUrl, destYaml);
|
|
607
|
+
log.info('Downloaded deployment configuration');
|
|
608
|
+
yamlDownloaded = true;
|
|
609
|
+
break;
|
|
610
|
+
} catch (downloadError) {
|
|
611
|
+
// Try next source
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (!yamlDownloaded) {
|
|
616
|
+
spinner.fail('Failed to get deployment configuration');
|
|
617
|
+
throw new Error('Could not download deployment YAML from any source');
|
|
618
|
+
}
|
|
491
619
|
}
|
|
492
620
|
|
|
493
621
|
// Run tribe start command with deployment YAML path
|
|
@@ -558,20 +686,36 @@ async function deployTribeCluster() {
|
|
|
558
686
|
let nodeReady = false;
|
|
559
687
|
for (let i = 0; i < 20; i++) {
|
|
560
688
|
try {
|
|
561
|
-
|
|
689
|
+
// First check if any nodes exist
|
|
690
|
+
const nodeList = execSync(`${kubectlPath} get nodes -o json`, {
|
|
562
691
|
encoding: 'utf8',
|
|
563
|
-
env: env
|
|
564
|
-
|
|
692
|
+
env: env,
|
|
693
|
+
stdio: 'pipe'
|
|
694
|
+
});
|
|
565
695
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
696
|
+
const nodes = JSON.parse(nodeList);
|
|
697
|
+
if (nodes.items && nodes.items.length > 0) {
|
|
698
|
+
// Now check if the node is ready
|
|
699
|
+
const nodeStatus = execSync(`${kubectlPath} get nodes -o jsonpath='{.items[0].status.conditions[?(@.type=="Ready")].status}'`, {
|
|
700
|
+
encoding: 'utf8',
|
|
701
|
+
env: env,
|
|
702
|
+
stdio: 'pipe'
|
|
703
|
+
}).trim();
|
|
704
|
+
|
|
705
|
+
if (nodeStatus === 'True') {
|
|
706
|
+
nodeReady = true;
|
|
707
|
+
spinner.text = 'Kubernetes node is ready';
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
} catch (error) {
|
|
712
|
+
// Ignore errors - node might not be registered yet or connection issues
|
|
713
|
+
if (error.message && error.message.includes('connection reset')) {
|
|
714
|
+
spinner.text = `Waiting for Kubernetes node... (${i+1}/20) - reconnecting...`;
|
|
715
|
+
} else {
|
|
716
|
+
spinner.text = `Waiting for Kubernetes node... (${i+1}/20)`;
|
|
570
717
|
}
|
|
571
|
-
} catch {
|
|
572
|
-
// Node might not be registered yet
|
|
573
718
|
}
|
|
574
|
-
spinner.text = `Waiting for Kubernetes node... (${i+1}/20)`;
|
|
575
719
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
576
720
|
}
|
|
577
721
|
|
|
@@ -642,17 +786,40 @@ async function promptForClusterSetup() {
|
|
|
642
786
|
async function main() {
|
|
643
787
|
console.log(chalk.bold.blue('\nš TRIBE CLI Complete Installer\n'));
|
|
644
788
|
|
|
789
|
+
// Detect and display platform info
|
|
790
|
+
const arch = process.arch === 'x64' ? 'amd64' : process.arch;
|
|
791
|
+
console.log(chalk.cyan(`Platform: ${platform} (${arch})`));
|
|
792
|
+
console.log(chalk.cyan(`Node: ${process.version}`));
|
|
793
|
+
console.log('');
|
|
794
|
+
|
|
795
|
+
// Check for unsupported platforms early
|
|
796
|
+
if (platform === 'win32') {
|
|
797
|
+
console.log(chalk.yellow('\nā ļø Windows is not yet supported'));
|
|
798
|
+
console.log(chalk.yellow('Please use WSL2 with Ubuntu and run this installer inside WSL2'));
|
|
799
|
+
console.log(chalk.yellow('Instructions: https://docs.microsoft.com/en-us/windows/wsl/install\n'));
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
|
|
645
803
|
log.info(`Detected: ${platform} (${arch})`);
|
|
646
804
|
log.info(`Installing to: ${binDir}`);
|
|
647
805
|
|
|
648
806
|
// Update PATH first
|
|
649
807
|
await updatePath();
|
|
650
808
|
|
|
651
|
-
const tasks = [
|
|
652
|
-
|
|
809
|
+
const tasks = [];
|
|
810
|
+
|
|
811
|
+
// Platform-specific Kubernetes installation
|
|
812
|
+
if (platform === 'darwin') {
|
|
813
|
+
tasks.push({ name: 'Colima', fn: installColima });
|
|
814
|
+
} else if (platform === 'linux') {
|
|
815
|
+
tasks.push({ name: 'K3s', fn: installK3s });
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Common tools for all platforms
|
|
819
|
+
tasks.push(
|
|
653
820
|
{ name: 'kubectl', fn: installKubectl },
|
|
654
|
-
{ name: 'TRIBE CLI', fn:
|
|
655
|
-
|
|
821
|
+
{ name: 'TRIBE CLI', fn: installTribeCLI }
|
|
822
|
+
);
|
|
656
823
|
|
|
657
824
|
let allSuccess = true;
|
|
658
825
|
|
|
@@ -672,7 +839,7 @@ async function main() {
|
|
|
672
839
|
// Check if cluster already exists
|
|
673
840
|
const clusterExists = await checkClusterExists();
|
|
674
841
|
|
|
675
|
-
if (!clusterExists) {
|
|
842
|
+
if (!clusterExists && !global.skipCluster) {
|
|
676
843
|
// Prompt for cluster setup
|
|
677
844
|
const shouldSetup = await promptForClusterSetup();
|
|
678
845
|
|
|
@@ -776,13 +943,24 @@ if (require.main === module) {
|
|
|
776
943
|
|
|
777
944
|
if (args.includes('--help') || args.includes('-h')) {
|
|
778
945
|
console.log(chalk.bold.blue('TRIBE CLI Installer\n'));
|
|
779
|
-
console.log('Usage: npx
|
|
946
|
+
console.log('Usage: npx @_xtribe/cli [options]\n');
|
|
780
947
|
console.log('Options:');
|
|
781
948
|
console.log(' --help, -h Show this help message');
|
|
782
949
|
console.log(' --verify Only verify existing installation');
|
|
783
950
|
console.log(' --dry-run Show what would be installed');
|
|
951
|
+
console.log(' --skip-cluster Skip cluster deployment (for testing)');
|
|
784
952
|
console.log('\nThis installer sets up the complete TRIBE development environment:');
|
|
785
|
-
|
|
953
|
+
|
|
954
|
+
if (platform === 'darwin') {
|
|
955
|
+
console.log('⢠Colima (Container runtime with Kubernetes)');
|
|
956
|
+
} else if (platform === 'linux') {
|
|
957
|
+
console.log('⢠K3s (Lightweight Kubernetes, one-line install)');
|
|
958
|
+
} else if (platform === 'win32') {
|
|
959
|
+
console.log('\nā ļø Windows support coming soon!');
|
|
960
|
+
console.log('Please use WSL2 with Ubuntu and run this installer inside WSL2');
|
|
961
|
+
process.exit(1);
|
|
962
|
+
}
|
|
963
|
+
|
|
786
964
|
console.log('⢠kubectl (Kubernetes CLI)');
|
|
787
965
|
console.log('⢠TRIBE CLI (Multi-agent orchestration)');
|
|
788
966
|
console.log('\nAfter installation:');
|
|
@@ -810,6 +988,9 @@ if (require.main === module) {
|
|
|
810
988
|
process.exit(0);
|
|
811
989
|
}
|
|
812
990
|
|
|
991
|
+
// Store skip-cluster flag
|
|
992
|
+
global.skipCluster = args.includes('--skip-cluster');
|
|
993
|
+
|
|
813
994
|
main().catch(error => {
|
|
814
995
|
console.error(chalk.red('Installation failed:'), error.message);
|
|
815
996
|
process.exit(1);
|
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.22",
|
|
4
4
|
"description": "TRIBE multi-agent development system - Zero to productive with one command",
|
|
5
5
|
"main": "install-tribe.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,9 +35,6 @@
|
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"install-tribe.js",
|
|
38
|
-
"lima-guestagent.Linux-aarch64.gz",
|
|
39
|
-
"tribe-deployment.yaml",
|
|
40
|
-
"tribe",
|
|
41
38
|
"README.md",
|
|
42
39
|
"package.json"
|
|
43
40
|
],
|
|
Binary file
|
package/tribe
DELETED
|
Binary file
|
package/tribe-deployment.yaml
DELETED
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
# TRIBE Complete Deployment - Bundled with NPM Package
|
|
3
|
-
# This file is automatically deployed by 'npx @_xtribe/cli'
|
|
4
|
-
#
|
|
5
|
-
# IMPORTANT: This deployment uses PUBLIC Docker Hub images from tribexal/tribe
|
|
6
|
-
# - tribexal/tribe:latest-taskmaster
|
|
7
|
-
# - tribexal/tribe:latest-bridge
|
|
8
|
-
# - tribexal/tribe:latest-claude-agent
|
|
9
|
-
#
|
|
10
|
-
# DO NOT change these to local image names for the npm package!
|
|
11
|
-
---
|
|
12
|
-
apiVersion: v1
|
|
13
|
-
kind: Namespace
|
|
14
|
-
metadata:
|
|
15
|
-
name: tribe-system
|
|
16
|
-
---
|
|
17
|
-
# ServiceAccount for Bridge
|
|
18
|
-
apiVersion: v1
|
|
19
|
-
kind: ServiceAccount
|
|
20
|
-
metadata:
|
|
21
|
-
name: bridge
|
|
22
|
-
namespace: tribe-system
|
|
23
|
-
---
|
|
24
|
-
# Role for Bridge
|
|
25
|
-
apiVersion: rbac.authorization.k8s.io/v1
|
|
26
|
-
kind: Role
|
|
27
|
-
metadata:
|
|
28
|
-
name: bridge-role
|
|
29
|
-
namespace: tribe-system
|
|
30
|
-
rules:
|
|
31
|
-
- apiGroups: [""]
|
|
32
|
-
resources: ["pods", "services", "pods/exec", "pods/log", "configmaps", "secrets", "namespaces"]
|
|
33
|
-
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
|
34
|
-
- apiGroups: ["apps"]
|
|
35
|
-
resources: ["deployments", "replicasets"]
|
|
36
|
-
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
|
37
|
-
- apiGroups: ["batch"]
|
|
38
|
-
resources: ["jobs"]
|
|
39
|
-
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
|
40
|
-
---
|
|
41
|
-
# RoleBinding for Bridge
|
|
42
|
-
apiVersion: rbac.authorization.k8s.io/v1
|
|
43
|
-
kind: RoleBinding
|
|
44
|
-
metadata:
|
|
45
|
-
name: bridge-rolebinding
|
|
46
|
-
namespace: tribe-system
|
|
47
|
-
subjects:
|
|
48
|
-
- kind: ServiceAccount
|
|
49
|
-
name: bridge
|
|
50
|
-
namespace: tribe-system
|
|
51
|
-
roleRef:
|
|
52
|
-
kind: Role
|
|
53
|
-
name: bridge-role
|
|
54
|
-
apiGroup: rbac.authorization.k8s.io
|
|
55
|
-
---
|
|
56
|
-
# PostgreSQL
|
|
57
|
-
apiVersion: apps/v1
|
|
58
|
-
kind: Deployment
|
|
59
|
-
metadata:
|
|
60
|
-
name: postgres
|
|
61
|
-
namespace: tribe-system
|
|
62
|
-
spec:
|
|
63
|
-
replicas: 1
|
|
64
|
-
selector:
|
|
65
|
-
matchLabels:
|
|
66
|
-
app: postgres
|
|
67
|
-
template:
|
|
68
|
-
metadata:
|
|
69
|
-
labels:
|
|
70
|
-
app: postgres
|
|
71
|
-
spec:
|
|
72
|
-
containers:
|
|
73
|
-
- name: postgres
|
|
74
|
-
image: postgres:15
|
|
75
|
-
env:
|
|
76
|
-
- name: POSTGRES_DB
|
|
77
|
-
value: gitea
|
|
78
|
-
- name: POSTGRES_USER
|
|
79
|
-
value: gitea
|
|
80
|
-
- name: POSTGRES_PASSWORD
|
|
81
|
-
value: gitea
|
|
82
|
-
ports:
|
|
83
|
-
- containerPort: 5432
|
|
84
|
-
readinessProbe:
|
|
85
|
-
exec:
|
|
86
|
-
command:
|
|
87
|
-
- pg_isready
|
|
88
|
-
- -U
|
|
89
|
-
- gitea
|
|
90
|
-
initialDelaySeconds: 5
|
|
91
|
-
periodSeconds: 5
|
|
92
|
-
---
|
|
93
|
-
apiVersion: v1
|
|
94
|
-
kind: Service
|
|
95
|
-
metadata:
|
|
96
|
-
name: postgres
|
|
97
|
-
namespace: tribe-system
|
|
98
|
-
spec:
|
|
99
|
-
selector:
|
|
100
|
-
app: postgres
|
|
101
|
-
ports:
|
|
102
|
-
- port: 5432
|
|
103
|
-
---
|
|
104
|
-
# Gitea
|
|
105
|
-
apiVersion: apps/v1
|
|
106
|
-
kind: Deployment
|
|
107
|
-
metadata:
|
|
108
|
-
name: gitea
|
|
109
|
-
namespace: tribe-system
|
|
110
|
-
spec:
|
|
111
|
-
replicas: 1
|
|
112
|
-
selector:
|
|
113
|
-
matchLabels:
|
|
114
|
-
app: gitea
|
|
115
|
-
template:
|
|
116
|
-
metadata:
|
|
117
|
-
labels:
|
|
118
|
-
app: gitea
|
|
119
|
-
spec:
|
|
120
|
-
initContainers:
|
|
121
|
-
- name: wait-for-db
|
|
122
|
-
image: busybox:1.35
|
|
123
|
-
command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for db; sleep 2; done']
|
|
124
|
-
- name: init-gitea
|
|
125
|
-
image: gitea/gitea:1.20.5
|
|
126
|
-
command: ['/bin/bash', '-c']
|
|
127
|
-
args:
|
|
128
|
-
- |
|
|
129
|
-
# Create app.ini
|
|
130
|
-
mkdir -p /data/gitea/conf
|
|
131
|
-
cat > /data/gitea/conf/app.ini << 'EOF'
|
|
132
|
-
APP_NAME = Gitea
|
|
133
|
-
RUN_MODE = prod
|
|
134
|
-
|
|
135
|
-
[database]
|
|
136
|
-
DB_TYPE = postgres
|
|
137
|
-
HOST = postgres:5432
|
|
138
|
-
NAME = gitea
|
|
139
|
-
USER = gitea
|
|
140
|
-
PASSWD = gitea
|
|
141
|
-
|
|
142
|
-
[server]
|
|
143
|
-
DOMAIN = gitea
|
|
144
|
-
ROOT_URL = http://gitea:3000/
|
|
145
|
-
HTTP_PORT = 3000
|
|
146
|
-
|
|
147
|
-
[service]
|
|
148
|
-
DISABLE_REGISTRATION = true
|
|
149
|
-
|
|
150
|
-
[security]
|
|
151
|
-
INSTALL_LOCK = true
|
|
152
|
-
SECRET_KEY = changeme
|
|
153
|
-
EOF
|
|
154
|
-
|
|
155
|
-
# Run migrations
|
|
156
|
-
gitea migrate
|
|
157
|
-
|
|
158
|
-
# Create admin user
|
|
159
|
-
gitea admin user create --admin --username gitea_admin --password admin123 --email admin@example.com || true
|
|
160
|
-
env:
|
|
161
|
-
- name: GITEA_WORK_DIR
|
|
162
|
-
value: /data
|
|
163
|
-
- name: GITEA_CUSTOM
|
|
164
|
-
value: /data/gitea
|
|
165
|
-
volumeMounts:
|
|
166
|
-
- name: gitea-data
|
|
167
|
-
mountPath: /data
|
|
168
|
-
containers:
|
|
169
|
-
- name: gitea
|
|
170
|
-
image: gitea/gitea:1.20.5
|
|
171
|
-
ports:
|
|
172
|
-
- containerPort: 3000
|
|
173
|
-
env:
|
|
174
|
-
- name: GITEA_WORK_DIR
|
|
175
|
-
value: /data
|
|
176
|
-
- name: GITEA_CUSTOM
|
|
177
|
-
value: /data/gitea
|
|
178
|
-
volumeMounts:
|
|
179
|
-
- name: gitea-data
|
|
180
|
-
mountPath: /data
|
|
181
|
-
readinessProbe:
|
|
182
|
-
httpGet:
|
|
183
|
-
path: /
|
|
184
|
-
port: 3000
|
|
185
|
-
initialDelaySeconds: 30
|
|
186
|
-
periodSeconds: 10
|
|
187
|
-
volumes:
|
|
188
|
-
- name: gitea-data
|
|
189
|
-
emptyDir: {}
|
|
190
|
-
---
|
|
191
|
-
apiVersion: v1
|
|
192
|
-
kind: Service
|
|
193
|
-
metadata:
|
|
194
|
-
name: gitea
|
|
195
|
-
namespace: tribe-system
|
|
196
|
-
spec:
|
|
197
|
-
selector:
|
|
198
|
-
app: gitea
|
|
199
|
-
ports:
|
|
200
|
-
- port: 3000
|
|
201
|
-
---
|
|
202
|
-
# TaskMaster
|
|
203
|
-
apiVersion: apps/v1
|
|
204
|
-
kind: Deployment
|
|
205
|
-
metadata:
|
|
206
|
-
name: taskmaster
|
|
207
|
-
namespace: tribe-system
|
|
208
|
-
spec:
|
|
209
|
-
replicas: 1
|
|
210
|
-
selector:
|
|
211
|
-
matchLabels:
|
|
212
|
-
app: taskmaster
|
|
213
|
-
template:
|
|
214
|
-
metadata:
|
|
215
|
-
labels:
|
|
216
|
-
app: taskmaster
|
|
217
|
-
spec:
|
|
218
|
-
initContainers:
|
|
219
|
-
- name: wait-for-db
|
|
220
|
-
image: busybox:1.35
|
|
221
|
-
command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for db; sleep 2; done']
|
|
222
|
-
containers:
|
|
223
|
-
- name: taskmaster
|
|
224
|
-
image: tribexal/tribe:latest-taskmaster
|
|
225
|
-
imagePullPolicy: IfNotPresent
|
|
226
|
-
ports:
|
|
227
|
-
- containerPort: 5000
|
|
228
|
-
env:
|
|
229
|
-
- name: FLASK_ENV
|
|
230
|
-
value: development
|
|
231
|
-
- name: DATABASE_URL
|
|
232
|
-
value: postgresql://gitea:gitea@postgres:5432/gitea
|
|
233
|
-
- name: GITEA_URL
|
|
234
|
-
value: http://gitea:3000
|
|
235
|
-
- name: GITEA_TOKEN
|
|
236
|
-
value: will-be-set-by-init-job
|
|
237
|
-
readinessProbe:
|
|
238
|
-
httpGet:
|
|
239
|
-
path: /
|
|
240
|
-
port: 5000
|
|
241
|
-
initialDelaySeconds: 10
|
|
242
|
-
periodSeconds: 5
|
|
243
|
-
---
|
|
244
|
-
apiVersion: v1
|
|
245
|
-
kind: Service
|
|
246
|
-
metadata:
|
|
247
|
-
name: taskmaster
|
|
248
|
-
namespace: tribe-system
|
|
249
|
-
spec:
|
|
250
|
-
selector:
|
|
251
|
-
app: taskmaster
|
|
252
|
-
ports:
|
|
253
|
-
- port: 5000
|
|
254
|
-
---
|
|
255
|
-
# Bridge
|
|
256
|
-
apiVersion: apps/v1
|
|
257
|
-
kind: Deployment
|
|
258
|
-
metadata:
|
|
259
|
-
name: bridge
|
|
260
|
-
namespace: tribe-system
|
|
261
|
-
spec:
|
|
262
|
-
replicas: 1
|
|
263
|
-
selector:
|
|
264
|
-
matchLabels:
|
|
265
|
-
app: bridge
|
|
266
|
-
template:
|
|
267
|
-
metadata:
|
|
268
|
-
labels:
|
|
269
|
-
app: bridge
|
|
270
|
-
spec:
|
|
271
|
-
serviceAccountName: bridge
|
|
272
|
-
initContainers:
|
|
273
|
-
- name: wait-for-services
|
|
274
|
-
image: busybox:1.35
|
|
275
|
-
command: ['sh', '-c']
|
|
276
|
-
args:
|
|
277
|
-
- |
|
|
278
|
-
echo "Waiting for services..."
|
|
279
|
-
until nc -z taskmaster 5000; do echo waiting for taskmaster; sleep 2; done
|
|
280
|
-
until nc -z gitea 3000; do echo waiting for gitea; sleep 2; done
|
|
281
|
-
echo "All services ready!"
|
|
282
|
-
containers:
|
|
283
|
-
- name: bridge
|
|
284
|
-
image: tribexal/tribe:latest-bridge
|
|
285
|
-
imagePullPolicy: IfNotPresent
|
|
286
|
-
ports:
|
|
287
|
-
- containerPort: 8080
|
|
288
|
-
- containerPort: 3456
|
|
289
|
-
env:
|
|
290
|
-
- name: TASKMASTER_URL
|
|
291
|
-
value: http://taskmaster:5000
|
|
292
|
-
- name: GITEA_URL
|
|
293
|
-
value: http://gitea:3000
|
|
294
|
-
- name: GITEA_ADMIN_USER
|
|
295
|
-
value: gitea_admin
|
|
296
|
-
- name: GITEA_ADMIN_PASSWORD
|
|
297
|
-
value: admin123
|
|
298
|
-
- name: NAMESPACE
|
|
299
|
-
value: tribe-system
|
|
300
|
-
readinessProbe:
|
|
301
|
-
httpGet:
|
|
302
|
-
path: /health
|
|
303
|
-
port: 8080
|
|
304
|
-
initialDelaySeconds: 10
|
|
305
|
-
periodSeconds: 5
|
|
306
|
-
---
|
|
307
|
-
apiVersion: v1
|
|
308
|
-
kind: Service
|
|
309
|
-
metadata:
|
|
310
|
-
name: bridge
|
|
311
|
-
namespace: tribe-system
|
|
312
|
-
spec:
|
|
313
|
-
selector:
|
|
314
|
-
app: bridge
|
|
315
|
-
ports:
|
|
316
|
-
- name: http
|
|
317
|
-
port: 8080
|
|
318
|
-
- name: websocket
|
|
319
|
-
port: 3456
|
|
320
|
-
---
|
|
321
|
-
# Claude Worker Deployment (starts with 0 replicas)
|
|
322
|
-
apiVersion: apps/v1
|
|
323
|
-
kind: Deployment
|
|
324
|
-
metadata:
|
|
325
|
-
name: claude-worker-deployment
|
|
326
|
-
namespace: tribe-system
|
|
327
|
-
spec:
|
|
328
|
-
replicas: 0
|
|
329
|
-
selector:
|
|
330
|
-
matchLabels:
|
|
331
|
-
app: claude-worker
|
|
332
|
-
template:
|
|
333
|
-
metadata:
|
|
334
|
-
labels:
|
|
335
|
-
app: claude-worker
|
|
336
|
-
spec:
|
|
337
|
-
containers:
|
|
338
|
-
- name: claude-agent
|
|
339
|
-
image: tribexal/tribe:latest-claude-agent
|
|
340
|
-
imagePullPolicy: IfNotPresent
|
|
341
|
-
env:
|
|
342
|
-
- name: ROLE
|
|
343
|
-
value: worker
|
|
344
|
-
- name: TASKMASTER_URL
|
|
345
|
-
value: http://taskmaster:5000
|
|
346
|
-
- name: GITEA_URL
|
|
347
|
-
value: http://gitea:3000
|
|
348
|
-
- name: NAMESPACE
|
|
349
|
-
value: tribe-system
|
|
350
|
-
- name: ANTHROPIC_API_KEY
|
|
351
|
-
valueFrom:
|
|
352
|
-
secretKeyRef:
|
|
353
|
-
name: claude-api-key
|
|
354
|
-
key: api-key
|
|
355
|
-
optional: true
|
|
356
|
-
volumeMounts:
|
|
357
|
-
- name: workspace
|
|
358
|
-
mountPath: /workspace
|
|
359
|
-
- name: config
|
|
360
|
-
mountPath: /app/minimal-config
|
|
361
|
-
readinessProbe:
|
|
362
|
-
exec:
|
|
363
|
-
command:
|
|
364
|
-
- /bin/sh
|
|
365
|
-
- -c
|
|
366
|
-
- test -f /tmp/worker-ready
|
|
367
|
-
initialDelaySeconds: 30
|
|
368
|
-
periodSeconds: 10
|
|
369
|
-
volumes:
|
|
370
|
-
- name: workspace
|
|
371
|
-
emptyDir: {}
|
|
372
|
-
- name: config
|
|
373
|
-
configMap:
|
|
374
|
-
name: claude-config
|
|
375
|
-
optional: true
|
|
376
|
-
---
|
|
377
|
-
# NodePort service for easy access
|
|
378
|
-
apiVersion: v1
|
|
379
|
-
kind: Service
|
|
380
|
-
metadata:
|
|
381
|
-
name: bridge-nodeport
|
|
382
|
-
namespace: tribe-system
|
|
383
|
-
spec:
|
|
384
|
-
type: NodePort
|
|
385
|
-
selector:
|
|
386
|
-
app: bridge
|
|
387
|
-
ports:
|
|
388
|
-
- name: http
|
|
389
|
-
port: 8080
|
|
390
|
-
nodePort: 30080
|
|
391
|
-
- name: websocket
|
|
392
|
-
port: 3456
|
|
393
|
-
nodePort: 30456
|
|
394
|
-
---
|
|
395
|
-
# ConfigMap for Claude agent configuration
|
|
396
|
-
apiVersion: v1
|
|
397
|
-
kind: ConfigMap
|
|
398
|
-
metadata:
|
|
399
|
-
name: claude-config
|
|
400
|
-
namespace: tribe-system
|
|
401
|
-
data:
|
|
402
|
-
config.yaml: |
|
|
403
|
-
mcp_servers:
|
|
404
|
-
filesystem:
|
|
405
|
-
command: npx
|
|
406
|
-
args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
|
|
407
|
-
git:
|
|
408
|
-
command: npx
|
|
409
|
-
args: ["-y", "@modelcontextprotocol/server-git"]
|
|
410
|
-
env:
|
|
411
|
-
PATH: /usr/local/bin:/usr/bin:/bin
|
|
412
|
-
---
|
|
413
|
-
# Initialization Job
|
|
414
|
-
apiVersion: batch/v1
|
|
415
|
-
kind: Job
|
|
416
|
-
metadata:
|
|
417
|
-
name: tribe-init
|
|
418
|
-
namespace: tribe-system
|
|
419
|
-
spec:
|
|
420
|
-
template:
|
|
421
|
-
spec:
|
|
422
|
-
restartPolicy: OnFailure
|
|
423
|
-
containers:
|
|
424
|
-
- name: init
|
|
425
|
-
image: curlimages/curl:8.4.0
|
|
426
|
-
command: ['/bin/sh', '-c']
|
|
427
|
-
args:
|
|
428
|
-
- |
|
|
429
|
-
echo "Waiting for Gitea to be ready..."
|
|
430
|
-
until curl -s http://gitea:3000 > /dev/null; do
|
|
431
|
-
echo "Waiting for Gitea..."
|
|
432
|
-
sleep 5
|
|
433
|
-
done
|
|
434
|
-
|
|
435
|
-
echo "Creating Gitea access token..."
|
|
436
|
-
TOKEN=$(curl -s -X POST http://gitea:3000/api/v1/users/gitea_admin/tokens \
|
|
437
|
-
-u gitea_admin:admin123 \
|
|
438
|
-
-H "Content-Type: application/json" \
|
|
439
|
-
-d '{"name":"taskmaster-'$(date +%s)'","scopes":["write:repository","write:user","write:issue","write:organization","read:repository"]}' \
|
|
440
|
-
| grep -o '"sha1":"[^"]*' | cut -d'"' -f4)
|
|
441
|
-
|
|
442
|
-
if [ -z "$TOKEN" ]; then
|
|
443
|
-
echo "Failed to create token!"
|
|
444
|
-
exit 1
|
|
445
|
-
fi
|
|
446
|
-
|
|
447
|
-
echo "Token created successfully"
|
|
448
|
-
|
|
449
|
-
# Update TaskMaster with the token
|
|
450
|
-
echo "Updating TaskMaster configuration..."
|
|
451
|
-
curl -X POST http://taskmaster:5000/api/config \
|
|
452
|
-
-H "Content-Type: application/json" \
|
|
453
|
-
-d "{\"gitea_token\":\"$TOKEN\"}"
|
|
454
|
-
|
|
455
|
-
echo "Initialization complete!"
|