crewx 0.8.5 → 0.8.6-rc.1

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/bin/crewx-lib.js CHANGED
@@ -164,7 +164,9 @@ export function ensureCrewxDirs() {
164
164
  const crewxHome = join(homedir(), '.crewx'); // config/DB
165
165
  const crewxWorkspaces = join(homedir(), 'crewx'); // workspaces collection
166
166
 
167
- for (const dir of [crewxHome, crewxWorkspaces]) {
167
+ const crewxTls = join(crewxHome, 'tls');
168
+
169
+ for (const dir of [crewxHome, crewxWorkspaces, crewxTls]) {
168
170
  try {
169
171
  mkdirSync(dir, { recursive: true });
170
172
  } catch (err) {
package/bin/crewx.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * crewx q|x|agent|... <args> → CLI subcommand (forwarded to packages/cli)
15
15
  */
16
16
  import { spawn } from 'child_process';
17
- import { readFileSync } from 'fs';
17
+ import { existsSync, readFileSync } from 'fs';
18
18
  import { fileURLToPath } from 'url';
19
19
  import { dirname, join } from 'path';
20
20
  import { homedir } from 'os';
@@ -139,6 +139,11 @@ function launchWeb(serveArgs = [], { openBrowser = false } = {}) {
139
139
  env.MCP_BEARER_TOKEN = content;
140
140
  }
141
141
 
142
+ const certPath = join(homedir(), '.crewx', 'tls', 'cert.pem');
143
+ if (existsSync(certPath)) {
144
+ env.NODE_EXTRA_CA_CERTS = certPath;
145
+ }
146
+
142
147
  if (openBrowser && !noOpen) {
143
148
  env.CREWX_OPEN_BROWSER = '1';
144
149
  }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CERT_PATH = void 0;
4
+ exports.resolveHttpsOptions = resolveHttpsOptions;
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const selfsigned_1 = require("selfsigned");
9
+ const TLS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'tls');
10
+ const CERT_PATH = (0, path_1.join)(TLS_DIR, 'cert.pem');
11
+ exports.CERT_PATH = CERT_PATH;
12
+ const KEY_PATH = (0, path_1.join)(TLS_DIR, 'key.pem');
13
+ async function resolveHttpsOptions() {
14
+ if (process.env.NODE_ENV !== 'production') {
15
+ return null;
16
+ }
17
+ if (process.env.TLS_CERT && process.env.TLS_KEY) {
18
+ return {
19
+ cert: (0, fs_1.readFileSync)(process.env.TLS_CERT),
20
+ key: (0, fs_1.readFileSync)(process.env.TLS_KEY),
21
+ };
22
+ }
23
+ if ((0, fs_1.existsSync)(CERT_PATH) && (0, fs_1.existsSync)(KEY_PATH)) {
24
+ return {
25
+ cert: (0, fs_1.readFileSync)(CERT_PATH),
26
+ key: (0, fs_1.readFileSync)(KEY_PATH),
27
+ };
28
+ }
29
+ return generateSelfSignedCert();
30
+ }
31
+ async function generateSelfSignedCert() {
32
+ const host = (0, os_1.hostname)();
33
+ const attrs = [{ name: 'commonName', value: 'CrewX Local Server' }];
34
+ const sanExt = {
35
+ name: 'subjectAltName',
36
+ altNames: [
37
+ { type: 2, value: 'localhost' },
38
+ { type: 2, value: host },
39
+ { type: 7, ip: '127.0.0.1' },
40
+ { type: 7, ip: '::1' },
41
+ ],
42
+ };
43
+ const pems = await (0, selfsigned_1.generate)(attrs, {
44
+ keySize: 2048,
45
+ extensions: [sanExt],
46
+ });
47
+ (0, fs_1.mkdirSync)(TLS_DIR, { recursive: true });
48
+ (0, fs_1.writeFileSync)(CERT_PATH, pems.cert, { mode: 0o644 });
49
+ (0, fs_1.writeFileSync)(KEY_PATH, pems.private, { mode: 0o600 });
50
+ console.log(`🔒 Self-signed TLS certificate generated: ~/.crewx/tls/`);
51
+ console.log(`⚠️ Browser will show a security warning on first access.`);
52
+ console.log(`💡 Install mkcert for warning-free local HTTPS.`);
53
+ return {
54
+ cert: Buffer.from(pems.cert),
55
+ key: Buffer.from(pems.private),
56
+ };
57
+ }
@@ -56,7 +56,7 @@ let AuthController = AuthController_1 = class AuthController {
56
56
  return { success: true, data: { authenticated: true, method } };
57
57
  }
58
58
  // AUTH.LOGIN: brute-force protected, @Public() skips BaseAuthGuard
59
- login(body, res) {
59
+ login(body, req, res) {
60
60
  const usernameOk = (0, ip_utils_js_1.safeCompare)(body.username, this.credentials.username);
61
61
  const passwordOk = (0, ip_utils_js_1.safeCompare)(body.password, this.credentials.password);
62
62
  if (!usernameOk || !passwordOk) {
@@ -69,7 +69,7 @@ let AuthController = AuthController_1 = class AuthController {
69
69
  res.cookie(COOKIE_NAME, this.mcpToken.token, {
70
70
  httpOnly: true,
71
71
  sameSite: 'strict',
72
- secure: process.env.NODE_ENV === 'production',
72
+ secure: req.secure,
73
73
  maxAge: 7 * 24 * 60 * 60 * 1000,
74
74
  path: '/',
75
75
  });
@@ -97,9 +97,10 @@ __decorate([
97
97
  (0, common_1.HttpCode)(200),
98
98
  (0, throttler_1.Throttle)({ default: { limit: 5, ttl: 60000 } }),
99
99
  __param(0, (0, common_1.Body)()),
100
- __param(1, (0, common_1.Res)({ passthrough: true })),
100
+ __param(1, (0, common_1.Req)()),
101
+ __param(2, (0, common_1.Res)({ passthrough: true })),
101
102
  __metadata("design:type", Function),
102
- __metadata("design:paramtypes", [LoginDto, Object]),
103
+ __metadata("design:paramtypes", [LoginDto, Object, Object]),
103
104
  __metadata("design:returntype", Object)
104
105
  ], AuthController.prototype, "login", null);
105
106
  __decorate([
@@ -429,24 +429,26 @@ let McpService = McpService_1 = class McpService {
429
429
  }
430
430
  }
431
431
  }
432
- const duration = Date.now() - startTime;
433
- const taskId = response.result?._meta?.taskId ??
434
- (sessionId ? this.activeTaskMap.get(sessionId) : undefined) ??
435
- null;
436
- try {
437
- this.toolCallRepo.insertToolCall({
438
- id: (0, sdk_1.generateId)('tcl'),
439
- task_id: taskId,
440
- session_id: sessionId ?? null,
441
- tool_name: `mcp_${params.name}`,
442
- input: JSON.stringify(params.arguments ?? {}),
443
- output: JSON.stringify(response.result?.content ?? response.error ?? null),
444
- duration_ms: duration,
445
- timestamp: new Date().toISOString(),
446
- });
447
- }
448
- catch (err) {
449
- this.logger.warn(`tool_calls insert failed: ${err?.message}`);
432
+ // Record only browser tool calls (agent calling a tool during execution).
433
+ // Server tools (queryAgent, executeAgent, etc.) are task triggers, not tool calls.
434
+ if (this.toolRouter.isBrowserTool(params.name, sessionId)) {
435
+ const duration = Date.now() - startTime;
436
+ const taskId = (sessionId ? this.activeTaskMap.get(sessionId) : undefined) ?? null;
437
+ try {
438
+ this.toolCallRepo.insertToolCall({
439
+ id: (0, sdk_1.generateId)('tcl'),
440
+ task_id: taskId,
441
+ session_id: sessionId ?? null,
442
+ tool_name: `mcp_${params.name}`,
443
+ input: JSON.stringify(params.arguments ?? {}),
444
+ output: JSON.stringify(response.result?.content ?? response.error ?? null),
445
+ duration_ms: duration,
446
+ timestamp: new Date().toISOString(),
447
+ });
448
+ }
449
+ catch (err) {
450
+ this.logger.warn(`tool_calls insert failed: ${err?.message}`);
451
+ }
450
452
  }
451
453
  return response;
452
454
  }
@@ -45,15 +45,20 @@ const cookie_parser_1 = __importDefault(require("cookie-parser"));
45
45
  const fs_1 = __importDefault(require("fs"));
46
46
  const path_1 = require("path");
47
47
  const app_module_js_1 = require("./app.module.js");
48
+ const tls_js_1 = require("./bootstrap/tls.js");
48
49
  const cors_js_1 = require("./common/cors.js");
49
50
  const mcp_constants_js_1 = require("./domain/mcp/mcp.constants.js");
50
51
  const auth_constants_js_1 = require("./domain/auth/auth.constants.js");
51
52
  const ip_utils_js_1 = require("./domain/auth/utils/ip.utils.js");
52
53
  const project_repository_js_1 = require("./repository/project.repository.js");
53
54
  async function bootstrap() {
55
+ const httpsOptions = await (0, tls_js_1.resolveHttpsOptions)();
54
56
  // RT-6: bodyParser disabled to preserve raw body for adapter webhook signature verification.
55
57
  // Adapter routes (/adapters/*) need raw Buffer body for signature verification.
56
- const app = await core_1.NestFactory.create(app_module_js_1.AppModule, { bodyParser: false });
58
+ const app = await core_1.NestFactory.create(app_module_js_1.AppModule, {
59
+ bodyParser: false,
60
+ ...(httpsOptions ? { httpsOptions } : {}),
61
+ });
57
62
  // trust proxy — set before other middleware so req.ip is correct behind nginx/Caddy
58
63
  if (process.env.TRUST_PROXY) {
59
64
  app.set('trust proxy', process.env.TRUST_PROXY);
@@ -142,8 +147,9 @@ async function bootstrap() {
142
147
  const server = app.getHttpServer();
143
148
  const address = server.address();
144
149
  const actualPort = typeof address === 'object' ? address?.port : port;
145
- console.log(`🚀 CrewX UI Server running on http://localhost:${actualPort}`);
146
- console.log(`📖 Swagger UI: http://localhost:${actualPort}/api/docs`);
150
+ const protocol = httpsOptions ? 'https' : 'http';
151
+ console.log(`🚀 CrewX UI Server running on ${protocol}://localhost:${actualPort}`);
152
+ console.log(`📖 Swagger UI: ${protocol}://localhost:${actualPort}/api/docs`);
147
153
  // Print MCP token info and clickable URLs
148
154
  const tokenInfo = app.get(mcp_constants_js_1.MCP_TOKEN);
149
155
  const tokenSource = tokenInfo.source === 'env' ? 'MCP_BEARER_TOKEN env' : 'auto-generated';
@@ -158,14 +164,14 @@ async function bootstrap() {
158
164
  console.log(`🔐 Web UI auth: ${authCreds.username} / ${masked} (source: ${authCreds.source})`);
159
165
  console.log(` Set AUTH_USERNAME / AUTH_PASSWORD in .env to customize.`);
160
166
  }
161
- console.log(`🏥 MCP Health: http://localhost:${actualPort}/mcp/health?token=${tokenInfo.token}`);
162
- console.log(`📡 MCP Endpoint: POST http://localhost:${actualPort}/mcp`);
167
+ console.log(`🏥 MCP Health: ${protocol}://localhost:${actualPort}/mcp/health?token=${tokenInfo.token}`);
168
+ console.log(`📡 MCP Endpoint: POST ${protocol}://localhost:${actualPort}/mcp`);
163
169
  // Auto-open browser when CREWX_OPEN_BROWSER=1 (set by bin/crewx.js for web-default route)
164
170
  if (process.env.CREWX_OPEN_BROWSER === '1') {
165
171
  try {
166
172
  const open = (await Promise.resolve().then(() => __importStar(require('open')))).default;
167
- await open(`http://localhost:${actualPort}`);
168
- console.log(`🌐 Browser opened: http://localhost:${actualPort}`);
173
+ await open(`${protocol}://localhost:${actualPort}`);
174
+ console.log(`🌐 Browser opened: ${protocol}://localhost:${actualPort}`);
169
175
  }
170
176
  catch {
171
177
  console.warn('⚠️ Could not open browser automatically. Please open manually.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewx",
3
- "version": "0.8.5",
3
+ "version": "0.8.6-rc.1",
4
4
  "description": "CrewX — AI agent team dashboard with Electron UI and CLI (Web + Electron + Global CLI)",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -60,6 +60,7 @@
60
60
  "remark-parse": "^11.0.0",
61
61
  "remark-stringify": "^11.0.0",
62
62
  "rxjs": "^7.8.1",
63
+ "selfsigned": "^5.5.0",
63
64
  "swagger-ui-express": "^5.0.1",
64
65
  "unified": "^11.0.5",
65
66
  "unist-util-visit": "^5.0.0",
@@ -67,12 +68,12 @@
67
68
  "wink-nlp-utils": "^2.1.0",
68
69
  "yargs": "^17.7.0",
69
70
  "zod": "^3.22.0",
70
- "@crewx/cli": "0.8.5",
71
- "@crewx/cron": "0.1.8",
71
+ "@crewx/cli": "0.8.6-rc.1",
72
72
  "@crewx/doc": "0.1.8",
73
- "@crewx/knowledge-core": "0.1.6",
73
+ "@crewx/cron": "0.1.8",
74
74
  "@crewx/memory": "0.1.10",
75
- "@crewx/sdk": "0.8.5",
75
+ "@crewx/knowledge-core": "0.1.6",
76
+ "@crewx/sdk": "0.8.6-rc.2",
76
77
  "@crewx/shared": "0.0.5",
77
78
  "@crewx/wbs": "0.1.9",
78
79
  "@crewx/search": "0.1.9",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.8.5",
3
+ "version": "0.8.6-rc.1",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }