@_xtribe/cli 1.0.19 → 1.0.21
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 +142 -33
- package/package.json +2 -1
- package/tribe-deployment.yaml +455 -0
package/install-tribe.js
CHANGED
|
@@ -821,16 +821,107 @@ async function startContainerRuntime() {
|
|
|
821
821
|
// Colima not running, need to start it
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
-
//
|
|
824
|
+
// Start Colima automatically
|
|
825
825
|
if (await checkCommand('colima')) {
|
|
826
|
-
spinner.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
826
|
+
spinner.text = 'Starting Colima with Kubernetes (this may take a few minutes on first run)...';
|
|
827
|
+
|
|
828
|
+
// Start Colima in the background
|
|
829
|
+
const { spawn } = require('child_process');
|
|
830
|
+
const colimaProcess = spawn('colima', ['start', '--kubernetes'], {
|
|
831
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
832
|
+
detached: false
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
let output = '';
|
|
836
|
+
let errorOutput = '';
|
|
837
|
+
let startupComplete = false;
|
|
838
|
+
|
|
839
|
+
// Monitor output
|
|
840
|
+
colimaProcess.stdout.on('data', (data) => {
|
|
841
|
+
output += data.toString();
|
|
842
|
+
// Update spinner with progress
|
|
843
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
844
|
+
for (const line of lines) {
|
|
845
|
+
if (line.includes('starting ...')) {
|
|
846
|
+
const contextMatch = line.match(/context=(\w+)/);
|
|
847
|
+
if (contextMatch) {
|
|
848
|
+
spinner.text = `Starting Colima: ${contextMatch[1]}...`;
|
|
849
|
+
}
|
|
850
|
+
} else if (line.includes('provisioning ...')) {
|
|
851
|
+
const contextMatch = line.match(/context=(\w+)/);
|
|
852
|
+
if (contextMatch) {
|
|
853
|
+
spinner.text = `Provisioning ${contextMatch[1]}...`;
|
|
854
|
+
}
|
|
855
|
+
} else if (line.includes('done')) {
|
|
856
|
+
startupComplete = true;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
colimaProcess.stderr.on('data', (data) => {
|
|
862
|
+
errorOutput += data.toString();
|
|
863
|
+
// Colima outputs progress to stderr, not stdout
|
|
864
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
865
|
+
for (const line of lines) {
|
|
866
|
+
if (line.includes('starting ...')) {
|
|
867
|
+
const contextMatch = line.match(/context=(\w+)/);
|
|
868
|
+
if (contextMatch) {
|
|
869
|
+
spinner.text = `Starting Colima: ${contextMatch[1]}...`;
|
|
870
|
+
}
|
|
871
|
+
} else if (line.includes('provisioning ...')) {
|
|
872
|
+
const contextMatch = line.match(/context=(\w+)/);
|
|
873
|
+
if (contextMatch) {
|
|
874
|
+
spinner.text = `Provisioning ${contextMatch[1]}...`;
|
|
875
|
+
}
|
|
876
|
+
} else if (line.includes('done')) {
|
|
877
|
+
startupComplete = true;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
// Wait for process to complete
|
|
883
|
+
const exitCode = await new Promise((resolve) => {
|
|
884
|
+
colimaProcess.on('exit', (code) => {
|
|
885
|
+
resolve(code);
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
if (exitCode === 0 && startupComplete) {
|
|
890
|
+
// Verify Colima is actually running
|
|
891
|
+
try {
|
|
892
|
+
execSync('colima status', { stdio: 'ignore' });
|
|
893
|
+
spinner.succeed('Colima started with Kubernetes');
|
|
894
|
+
|
|
895
|
+
// Give k3s a moment to initialize
|
|
896
|
+
spinner.text = 'Waiting for Kubernetes to initialize...';
|
|
897
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
898
|
+
|
|
899
|
+
return true;
|
|
900
|
+
} catch {
|
|
901
|
+
spinner.fail('Colima started but is not responding');
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
spinner.fail('Failed to start Colima');
|
|
906
|
+
// Only show actual error messages, not progress
|
|
907
|
+
const actualErrors = errorOutput.split('\n')
|
|
908
|
+
.filter(line => line.trim() &&
|
|
909
|
+
!line.includes('level=info') &&
|
|
910
|
+
!line.includes('starting ...') &&
|
|
911
|
+
!line.includes('provisioning ...') &&
|
|
912
|
+
!line.includes('done'))
|
|
913
|
+
.join('\n');
|
|
914
|
+
|
|
915
|
+
if (actualErrors.trim()) {
|
|
916
|
+
console.log(chalk.red('Error output:'));
|
|
917
|
+
console.log(actualErrors);
|
|
918
|
+
}
|
|
919
|
+
console.log('');
|
|
920
|
+
console.log(chalk.yellow(' Please start Colima manually:'));
|
|
921
|
+
console.log(chalk.cyan(' colima start --kubernetes'));
|
|
922
|
+
console.log('');
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
834
925
|
} else {
|
|
835
926
|
spinner.warn('Colima not found');
|
|
836
927
|
return false;
|
|
@@ -956,9 +1047,28 @@ async function verifyInstallation() {
|
|
|
956
1047
|
|
|
957
1048
|
async function checkClusterExists() {
|
|
958
1049
|
try {
|
|
959
|
-
// Check if TRIBE namespace exists
|
|
1050
|
+
// Check if TRIBE namespace exists
|
|
960
1051
|
execSync('kubectl get namespace tribe-system', { stdio: 'ignore' });
|
|
961
|
-
|
|
1052
|
+
|
|
1053
|
+
// Also check if key TRIBE pods are running
|
|
1054
|
+
const podsOutput = execSync('kubectl get pods -n tribe-system -o json', {
|
|
1055
|
+
encoding: 'utf8',
|
|
1056
|
+
stdio: 'pipe'
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
const pods = JSON.parse(podsOutput);
|
|
1060
|
+
if (!pods.items || pods.items.length === 0) {
|
|
1061
|
+
return false; // Namespace exists but no pods
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// Check if essential pods exist (gitea, postgres, taskmaster)
|
|
1065
|
+
const essentialPods = ['gitea', 'postgres', 'taskmaster'];
|
|
1066
|
+
const runningPods = pods.items.filter(pod => {
|
|
1067
|
+
const podName = pod.metadata.name;
|
|
1068
|
+
return essentialPods.some(essential => podName.includes(essential));
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
return runningPods.length >= essentialPods.length;
|
|
962
1072
|
} catch {
|
|
963
1073
|
return false;
|
|
964
1074
|
}
|
|
@@ -1320,6 +1430,17 @@ async function cleanupOldInstallations() {
|
|
|
1320
1430
|
async function forceCleanInstallation() {
|
|
1321
1431
|
console.log(chalk.yellow('\n🧹 Performing force clean installation...\n'));
|
|
1322
1432
|
|
|
1433
|
+
// Clean up Kubernetes resources first (while cluster is running)
|
|
1434
|
+
try {
|
|
1435
|
+
execSync('kubectl delete namespace tribe-system --ignore-not-found=true', {
|
|
1436
|
+
stdio: 'ignore',
|
|
1437
|
+
timeout: 30000
|
|
1438
|
+
});
|
|
1439
|
+
log.success('Removed TRIBE namespace from cluster');
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
// Cluster might not be running or namespace doesn't exist
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1323
1444
|
// Uninstall global npm package if exists
|
|
1324
1445
|
try {
|
|
1325
1446
|
execSync('npm uninstall -g tribe-cli-global', {
|
|
@@ -1531,21 +1652,23 @@ async function main() {
|
|
|
1531
1652
|
const verified = await verifyInstallation();
|
|
1532
1653
|
|
|
1533
1654
|
// Try to start container runtime (after showing results)
|
|
1534
|
-
await startContainerRuntime();
|
|
1655
|
+
const runtimeStarted = await startContainerRuntime();
|
|
1535
1656
|
|
|
1536
|
-
|
|
1657
|
+
// If we successfully started the runtime, we should continue with deployment
|
|
1658
|
+
if (verified || runtimeStarted) {
|
|
1537
1659
|
// Check if cluster already exists
|
|
1538
1660
|
const clusterExists = await checkClusterExists();
|
|
1539
1661
|
|
|
1540
1662
|
if (!clusterExists && !global.skipCluster) {
|
|
1541
|
-
//
|
|
1542
|
-
|
|
1663
|
+
// If we just started Colima, automatically deploy the cluster
|
|
1664
|
+
// Don't prompt if we just started the runtime - deploy automatically
|
|
1665
|
+
const shouldSetup = runtimeStarted ? true : await promptForClusterSetup();
|
|
1543
1666
|
|
|
1544
1667
|
if (shouldSetup) {
|
|
1545
1668
|
console.log('');
|
|
1546
1669
|
|
|
1547
|
-
// Start Colima with Kubernetes if on macOS
|
|
1548
|
-
if (platform === 'darwin') {
|
|
1670
|
+
// Start Colima with Kubernetes if on macOS (skip if we just started it)
|
|
1671
|
+
if (platform === 'darwin' && !runtimeStarted) {
|
|
1549
1672
|
const colimaStarted = await startColimaWithKubernetes();
|
|
1550
1673
|
if (!colimaStarted) {
|
|
1551
1674
|
log.error('Failed to start Colima. Please run manually:');
|
|
@@ -1562,22 +1685,8 @@ async function main() {
|
|
|
1562
1685
|
console.log('\n' + chalk.bold.green('✨ TRIBE is ready!'));
|
|
1563
1686
|
console.log('');
|
|
1564
1687
|
|
|
1565
|
-
//
|
|
1566
|
-
|
|
1567
|
-
const shouldSetupPath = await promptForPathSetup();
|
|
1568
|
-
if (shouldSetupPath) {
|
|
1569
|
-
// Run the setup-path logic inline
|
|
1570
|
-
try {
|
|
1571
|
-
const setupPath = require('./setup-path.js');
|
|
1572
|
-
setupPath();
|
|
1573
|
-
console.log('');
|
|
1574
|
-
// PATH is now set up, update instructions
|
|
1575
|
-
global.pathSetupComplete = true;
|
|
1576
|
-
} catch (error) {
|
|
1577
|
-
log.warning('Could not automatically set up PATH');
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1688
|
+
// PATH was already set up automatically in setupPathEnvironment
|
|
1689
|
+
// No need to prompt again
|
|
1581
1690
|
|
|
1582
1691
|
// Provide immediate access to tribe command
|
|
1583
1692
|
if (global.pathSetupComplete) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_xtribe/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"description": "TRIBE multi-agent development system - Zero to productive with one command",
|
|
5
5
|
"main": "install-tribe.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"install-tribe.js",
|
|
41
41
|
"setup-path.js",
|
|
42
42
|
"install.sh",
|
|
43
|
+
"tribe-deployment.yaml",
|
|
43
44
|
"README.md",
|
|
44
45
|
"package.json"
|
|
45
46
|
],
|
|
@@ -0,0 +1,455 @@
|
|
|
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!"
|