crewos 0.1.0
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/app/.env.example +1 -0
- package/app/index.html +50 -0
- package/app/package.json +25 -0
- package/app/public/favicon.svg +1 -0
- package/app/public/images/cursor-ide-guiiding.png +0 -0
- package/app/public/images/gpt.jpg +0 -0
- package/app/src/app.jsx +22 -0
- package/app/src/components/ConfirmModal.jsx +50 -0
- package/app/src/components/Icons.jsx +377 -0
- package/app/src/components/RedirectRoute.jsx +14 -0
- package/app/src/components/SplashScreen.jsx +15 -0
- package/app/src/hooks/useAuth.js +28 -0
- package/app/src/index.css +268 -0
- package/app/src/main.jsx +5 -0
- package/app/src/navigations/AuthRoutes.jsx +15 -0
- package/app/src/navigations/MainRoutes.jsx +15 -0
- package/app/src/navigations/OnboardingRoutes.jsx +15 -0
- package/app/src/navigations/index.jsx +37 -0
- package/app/src/pages/Home/index.jsx +2095 -0
- package/app/src/pages/Login/index.jsx +118 -0
- package/app/src/pages/Onboarding/index.jsx +550 -0
- package/app/src/services/api.js +46 -0
- package/app/src/services/auth.service.js +3 -0
- package/app/src/services/config.service.js +13 -0
- package/app/src/services/member.service.js +7 -0
- package/app/src/services/onboarding.service.js +17 -0
- package/app/src/services/role.service.js +6 -0
- package/app/src/services/task.service.js +22 -0
- package/app/src/stores/auth.store.js +7 -0
- package/app/src/utils/environments.js +5 -0
- package/app/vite.config.js +10 -0
- package/app/yarn.lock +1337 -0
- package/backend/package-lock.json +918 -0
- package/backend/package.json +18 -0
- package/backend/src/configs/db.config.js +40 -0
- package/backend/src/controllers/auth.controller.js +19 -0
- package/backend/src/controllers/config.controller.js +23 -0
- package/backend/src/controllers/member.controller.js +30 -0
- package/backend/src/controllers/models.controller.js +25 -0
- package/backend/src/controllers/onboarding.controller.js +49 -0
- package/backend/src/controllers/role.controller.js +17 -0
- package/backend/src/controllers/task.controller.js +63 -0
- package/backend/src/index.js +36 -0
- package/backend/src/middlewares/onboarding.guard.js +14 -0
- package/backend/src/routes/auth.route.js +8 -0
- package/backend/src/routes/config.route.js +11 -0
- package/backend/src/routes/index.js +22 -0
- package/backend/src/routes/member.route.js +11 -0
- package/backend/src/routes/models.route.js +8 -0
- package/backend/src/routes/onboarding.route.js +13 -0
- package/backend/src/routes/role.route.js +9 -0
- package/backend/src/routes/task.route.js +20 -0
- package/backend/src/services/auth.service.js +14 -0
- package/backend/src/services/config.service.js +176 -0
- package/backend/src/services/data/roles.json +474 -0
- package/backend/src/services/member.service.js +77 -0
- package/backend/src/services/onboarding.service.js +328 -0
- package/backend/src/services/role.service.js +23 -0
- package/backend/src/services/task.service.js +665 -0
- package/backend/src/utils/catcher.js +9 -0
- package/backend/src/utils/sanitize.js +13 -0
- package/backend/yarn.lock +513 -0
- package/bin/crewos.js +307 -0
- package/package.json +11 -0
package/bin/crewos.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
13
|
+
const BACKEND_DIR = path.join(REPO_ROOT, 'backend');
|
|
14
|
+
const APP_DIR = path.join(REPO_ROOT, 'app');
|
|
15
|
+
|
|
16
|
+
const CONFIG_DIR = path.join(os.homedir(), '.crewos');
|
|
17
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
18
|
+
|
|
19
|
+
const cmd = process.argv[2];
|
|
20
|
+
|
|
21
|
+
function generatePassword(length = 20) {
|
|
22
|
+
const chars =
|
|
23
|
+
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+';
|
|
24
|
+
return Array.from(crypto.randomBytes(length))
|
|
25
|
+
.map((b) => chars[b % chars.length])
|
|
26
|
+
.join('');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ask(rl, question, defaultValue = '') {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const display = defaultValue
|
|
32
|
+
? `${question} (${defaultValue}): `
|
|
33
|
+
: `${question}: `;
|
|
34
|
+
rl.question(display, (answer) => {
|
|
35
|
+
resolve(answer.trim() || defaultValue);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validatePort(raw) {
|
|
41
|
+
const port = parseInt(raw, 10);
|
|
42
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
43
|
+
return { valid: false, value: raw };
|
|
44
|
+
}
|
|
45
|
+
return { valid: true, value: port };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function checkOpenCode() {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const check = spawn('opencode', ['--version'], {
|
|
51
|
+
stdio: 'pipe',
|
|
52
|
+
shell: true,
|
|
53
|
+
});
|
|
54
|
+
let output = '';
|
|
55
|
+
check.stdout.on('data', (d) => (output += d.toString()));
|
|
56
|
+
check.stderr.on('data', (d) => (output += d.toString()));
|
|
57
|
+
check.on('close', (code) => {
|
|
58
|
+
resolve(code === 0 ? output.trim() : null);
|
|
59
|
+
});
|
|
60
|
+
check.on('error', () => {
|
|
61
|
+
resolve(null);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function installOpenCode() {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const install = spawn('npm', ['install', '-g', 'opencode-ai'], {
|
|
69
|
+
stdio: 'inherit',
|
|
70
|
+
shell: true,
|
|
71
|
+
});
|
|
72
|
+
install.on('close', (code) => {
|
|
73
|
+
resolve(code === 0);
|
|
74
|
+
});
|
|
75
|
+
install.on('error', () => {
|
|
76
|
+
resolve(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function initCommand() {
|
|
82
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
83
|
+
console.log('ā crewOS is already initialized.\n');
|
|
84
|
+
console.log(
|
|
85
|
+
' To reconfigure, delete ~/.crewos/config.json and run init again.',
|
|
86
|
+
);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const rl = readline.createInterface({
|
|
91
|
+
input: process.stdin,
|
|
92
|
+
output: process.stdout,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log('ā” crewOS initialization\n');
|
|
96
|
+
console.log('Press Enter to accept default values shown in parentheses.\n');
|
|
97
|
+
|
|
98
|
+
const backendPortRaw = await ask(rl, 'Backend port', '24101');
|
|
99
|
+
const backendPortResult = validatePort(backendPortRaw);
|
|
100
|
+
if (!backendPortResult.valid) {
|
|
101
|
+
console.log(`\nā Invalid backend port: ${backendPortResult.value}`);
|
|
102
|
+
rl.close();
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const frontendPortRaw = await ask(rl, 'Frontend port', '24102');
|
|
107
|
+
const frontendPortResult = validatePort(frontendPortRaw);
|
|
108
|
+
if (!frontendPortResult.valid) {
|
|
109
|
+
console.log(`\nā Invalid frontend port: ${frontendPortResult.value}`);
|
|
110
|
+
rl.close();
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const opencodePortRaw = await ask(rl, 'Opencode port', '24103');
|
|
115
|
+
const opencodePortResult = validatePort(opencodePortRaw);
|
|
116
|
+
if (!opencodePortResult.valid) {
|
|
117
|
+
console.log(`\nā Invalid opencode port: ${opencodePortResult.value}`);
|
|
118
|
+
rl.close();
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
backendPortResult.value === frontendPortResult.value ||
|
|
124
|
+
backendPortResult.value === opencodePortResult.value ||
|
|
125
|
+
frontendPortResult.value === opencodePortResult.value
|
|
126
|
+
) {
|
|
127
|
+
console.log('\nā Ports must be different.');
|
|
128
|
+
rl.close();
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const password = await ask(rl, 'App password', generatePassword());
|
|
133
|
+
if (!password) {
|
|
134
|
+
console.log('\nā Password is required.');
|
|
135
|
+
rl.close();
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
rl.close();
|
|
140
|
+
|
|
141
|
+
const config = {
|
|
142
|
+
backendPort: backendPortResult.value,
|
|
143
|
+
frontendPort: frontendPortResult.value,
|
|
144
|
+
opencodePort: opencodePortResult.value,
|
|
145
|
+
workingDir: process.cwd(),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
149
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
150
|
+
|
|
151
|
+
const DB_PATH = path.join(CONFIG_DIR, 'db.json');
|
|
152
|
+
const dbData = {
|
|
153
|
+
members: [],
|
|
154
|
+
tasks: [],
|
|
155
|
+
meta: {
|
|
156
|
+
password,
|
|
157
|
+
baseURL: '',
|
|
158
|
+
apiKey: '',
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
fs.writeFileSync(DB_PATH, JSON.stringify(dbData, null, 2));
|
|
162
|
+
|
|
163
|
+
console.log('\nā
crewOS initialized successfully!');
|
|
164
|
+
console.log(` Config saved to ${CONFIG_FILE}\n`);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const installed = await checkOpenCode();
|
|
168
|
+
if (installed) {
|
|
169
|
+
console.log(` opencode: ${installed}`);
|
|
170
|
+
} else {
|
|
171
|
+
console.log(' opencode not found. Installing...');
|
|
172
|
+
const installSuccess = await installOpenCode();
|
|
173
|
+
if (installSuccess) {
|
|
174
|
+
console.log(' ā
opencode installed successfully.');
|
|
175
|
+
} else {
|
|
176
|
+
console.log(' ā Failed to install opencode. Install it manually:');
|
|
177
|
+
console.log(' npm install -g opencode-ai');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
console.log(' ā opencode not found. Install it to process tasks.');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.log('\nAvailable commands:');
|
|
185
|
+
console.log(' crewos init Initialize crewOS');
|
|
186
|
+
console.log(' crewos start Start backend and application\n');
|
|
187
|
+
console.log('Run `crewos start` to launch the application.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function startCommand() {
|
|
191
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
192
|
+
console.log('ā crewOS is not initialized. Run `crewos init` first.');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
197
|
+
|
|
198
|
+
console.log('šØ Building frontend...');
|
|
199
|
+
try {
|
|
200
|
+
await new Promise((resolve, reject) => {
|
|
201
|
+
const env = {
|
|
202
|
+
...process.env,
|
|
203
|
+
VITE_BACKEND_URL: `http://localhost:${config.backendPort}`,
|
|
204
|
+
};
|
|
205
|
+
const build = spawn('npx', ['vite', 'build'], {
|
|
206
|
+
cwd: APP_DIR,
|
|
207
|
+
stdio: 'inherit',
|
|
208
|
+
env,
|
|
209
|
+
shell: true,
|
|
210
|
+
});
|
|
211
|
+
build.on('close', (code) => {
|
|
212
|
+
if (code === 0) resolve();
|
|
213
|
+
else reject(new Error(`vite build exited with code ${code}`));
|
|
214
|
+
});
|
|
215
|
+
build.on('error', reject);
|
|
216
|
+
});
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.log(`\nā Frontend build failed: ${err.message}`);
|
|
219
|
+
console.log(' Make sure dependencies are installed. Run:');
|
|
220
|
+
console.log(` cd ${APP_DIR} && npm install`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log('\nš Starting crewOS...');
|
|
225
|
+
console.log(` Backend: http://localhost:${config.backendPort}`);
|
|
226
|
+
console.log(` Frontend: http://localhost:${config.frontendPort}`);
|
|
227
|
+
console.log(` OpenCode: http://localhost:${config.opencodePort}`);
|
|
228
|
+
|
|
229
|
+
const children = [];
|
|
230
|
+
|
|
231
|
+
const backend = spawn('node', ['src/index.js'], {
|
|
232
|
+
cwd: BACKEND_DIR,
|
|
233
|
+
stdio: 'inherit',
|
|
234
|
+
env: {
|
|
235
|
+
...process.env,
|
|
236
|
+
PORT: String(config.backendPort),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
children.push(backend);
|
|
240
|
+
|
|
241
|
+
const frontend = spawn(
|
|
242
|
+
'npx',
|
|
243
|
+
['vite', 'preview', '--port', String(config.frontendPort), '--strictPort'],
|
|
244
|
+
{
|
|
245
|
+
cwd: APP_DIR,
|
|
246
|
+
stdio: 'inherit',
|
|
247
|
+
env: {
|
|
248
|
+
...process.env,
|
|
249
|
+
},
|
|
250
|
+
shell: true,
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
children.push(frontend);
|
|
254
|
+
|
|
255
|
+
const opencode = spawn(
|
|
256
|
+
'opencode',
|
|
257
|
+
['serve', '--port', String(opencodePort)],
|
|
258
|
+
{
|
|
259
|
+
stdio: 'inherit',
|
|
260
|
+
},
|
|
261
|
+
);
|
|
262
|
+
children.push(opencode);
|
|
263
|
+
|
|
264
|
+
backend.on('close', (code) => {
|
|
265
|
+
console.log(`Backend exited with code ${code}`);
|
|
266
|
+
children.forEach((c) => c.kill('SIGTERM'));
|
|
267
|
+
process.exit(code);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
frontend.on('close', (code) => {
|
|
271
|
+
console.log(`Frontend exited with code ${code}`);
|
|
272
|
+
children.forEach((c) => c.kill('SIGTERM'));
|
|
273
|
+
process.exit(code);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
opencode.on('close', (code) => {
|
|
277
|
+
console.log(`OpenCode serve exited with code ${code}`);
|
|
278
|
+
children.forEach((c) => c.kill('SIGTERM'));
|
|
279
|
+
process.exit(code);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const shutdown = () => {
|
|
283
|
+
children.forEach((c) => c.kill('SIGTERM'));
|
|
284
|
+
process.exit(0);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
process.on('SIGINT', shutdown);
|
|
288
|
+
process.on('SIGTERM', shutdown);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
switch (cmd) {
|
|
292
|
+
case 'init':
|
|
293
|
+
initCommand();
|
|
294
|
+
break;
|
|
295
|
+
case 'start':
|
|
296
|
+
startCommand();
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
console.log(`
|
|
300
|
+
crewOS v0.1.0
|
|
301
|
+
|
|
302
|
+
Commands:
|
|
303
|
+
crewos init Initialize crewOS
|
|
304
|
+
crewos start Start backend and application
|
|
305
|
+
`);
|
|
306
|
+
break;
|
|
307
|
+
}
|