crewx 0.8.6-rc.2 → 0.8.6-rc.4

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
@@ -19,6 +19,8 @@ export function parseServeFlags(args) {
19
19
  let token = undefined;
20
20
  let tokenFile = undefined;
21
21
  let noOpen = false;
22
+ let https = false;
23
+ let trust = false;
22
24
 
23
25
  const consumed = new Set();
24
26
 
@@ -60,6 +62,12 @@ export function parseServeFlags(args) {
60
62
  } else if (arg === '--no-open') {
61
63
  noOpen = true;
62
64
  consumed.add(i);
65
+ } else if (arg === '--https') {
66
+ https = true;
67
+ consumed.add(i);
68
+ } else if (arg === '--trust') {
69
+ trust = true;
70
+ consumed.add(i);
63
71
  } else if (arg === '--help' || arg === '-h') {
64
72
  return { help: true };
65
73
  }
@@ -74,7 +82,7 @@ export function parseServeFlags(args) {
74
82
  }
75
83
  }
76
84
 
77
- return { port, token, tokenFile, noOpen };
85
+ return { port, token, tokenFile, noOpen, https, trust };
78
86
  }
79
87
 
80
88
  /**
package/bin/crewx.js CHANGED
@@ -94,6 +94,8 @@ function launchWeb(serveArgs = [], { openBrowser = false } = {}) {
94
94
  ' --port <N> Port to listen on (default: auto)\n' +
95
95
  ' --token <T> MCP bearer token\n' +
96
96
  ' --token-file <path> Read token from file\n' +
97
+ ' --https Enable HTTPS with auto-generated certificate\n' +
98
+ ' --trust Install CA to system trust store (requires --https)\n' +
97
99
  ' --no-open Do not open browser automatically\n' +
98
100
  ' --help, -h Show this help\n',
99
101
  );
@@ -110,10 +112,21 @@ function launchWeb(serveArgs = [], { openBrowser = false } = {}) {
110
112
  process.exit(flagsResult.error.startsWith('Unknown option') ? 2 : 1);
111
113
  }
112
114
 
113
- const { port, token, tokenFile, noOpen } = flagsResult;
115
+ const { port, token, tokenFile, noOpen, https, trust } = flagsResult;
114
116
 
115
117
  const env = { ...process.env, NODE_ENV: 'production' }; // Force production — prevents users from bypassing FREE limits via env
116
118
 
119
+ if (https) {
120
+ env.CREWX_HTTPS = 'true';
121
+ }
122
+ if (trust) {
123
+ if (!https) {
124
+ console.error('❌ --trust requires --https');
125
+ process.exit(1);
126
+ }
127
+ env.CREWX_TRUST_CA = 'true';
128
+ }
129
+
117
130
  if (port) {
118
131
  env.PORT = String(port);
119
132
  }
@@ -144,6 +157,14 @@ function launchWeb(serveArgs = [], { openBrowser = false } = {}) {
144
157
  env.NODE_EXTRA_CA_CERTS = certPath;
145
158
  }
146
159
 
160
+ if (https) {
161
+ console.log('🔒 HTTPS enabled with SowonLabs CA');
162
+ if (!trust) {
163
+ console.log('⚠️ Browser may show a certificate warning.');
164
+ console.log(' To remove warning: npx crewx serve --https --trust');
165
+ }
166
+ }
167
+
147
168
  if (openBrowser && !noOpen) {
148
169
  env.CREWX_OPEN_BROWSER = '1';
149
170
  }
@@ -17,7 +17,7 @@ const CERT_PATH = (0, path_1.join)(TLS_DIR, 'cert.pem');
17
17
  exports.CERT_PATH = CERT_PATH;
18
18
  const KEY_PATH = (0, path_1.join)(TLS_DIR, 'key.pem');
19
19
  async function resolveHttpsOptions() {
20
- if (process.env.NODE_ENV !== 'production') {
20
+ if (process.env.CREWX_HTTPS !== 'true') {
21
21
  return null;
22
22
  }
23
23
  if (process.env.TLS_CERT && process.env.TLS_KEY) {
@@ -37,7 +37,7 @@ async function resolveHttpsOptions() {
37
37
  const server = generateServerCert(ca.cert, ca.key);
38
38
  (0, fs_1.writeFileSync)(CERT_PATH, server.certPem, { mode: 0o644 });
39
39
  (0, fs_1.writeFileSync)(KEY_PATH, server.keyPem, { mode: 0o600 });
40
- if (ca.isNew) {
40
+ if (ca.isNew && process.env.CREWX_TRUST_CA === 'true') {
41
41
  installCA(CA_CERT_PATH);
42
42
  }
43
43
  return {
@@ -109,13 +109,9 @@ function generateServerCert(caCert, caKey) {
109
109
  };
110
110
  }
111
111
  function installCA(caCertPath) {
112
- if (process.env.CREWX_TRUST_CA === '0') {
113
- return;
114
- }
115
112
  const platform = process.platform;
116
113
  console.log('');
117
- console.log('🔐 브라우저 경고 없이 HTTPS를 사용하려면 CA 인증서를 등록해야 합니다.');
118
- console.log(' 등록하지 않아도 HTTPS는 동작하지만, 브라우저에서 "주의 요함" 경고가 표시됩니다.');
114
+ console.log('🔐 Installing CA certificate to system trust store...');
119
115
  console.log('');
120
116
  try {
121
117
  if (platform === 'win32') {
@@ -125,16 +121,16 @@ function installCA(caCertPath) {
125
121
  (0, child_process_1.execSync)(`security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db "${caCertPath}"`, { stdio: 'inherit' });
126
122
  }
127
123
  else {
128
- console.log(' Linux에서는 수동 등록이 필요합니다:');
124
+ console.log(' Manual installation required on Linux:');
129
125
  console.log(` sudo cp ${caCertPath} /usr/local/share/ca-certificates/crewx-ca.crt`);
130
126
  console.log(' sudo update-ca-certificates');
131
127
  return;
132
128
  }
133
- console.log('✅ SowonLabs CA 시스템에 등록되었습니다. 브라우저 경고 없이 HTTPS를 사용할 수 있습니다.');
129
+ console.log('✅ SowonLabs CA installed. HTTPS will work without browser warnings.');
134
130
  }
135
131
  catch {
136
- console.log('⚠️ CA 등록에 실패했습니다. HTTPS 동작하지만 브라우저에서 경고가 표시됩니다.');
137
- console.log(` 수동 등록: ${caCertPath}`);
132
+ console.log('⚠️ CA installation failed. HTTPS still works, but browser will show a warning.');
133
+ console.log(` Manual install: ${caCertPath}`);
138
134
  }
139
135
  }
140
136
  function randomSerialNumber() {
@@ -18,10 +18,10 @@ const common_1 = require("@nestjs/common");
18
18
  const swagger_1 = require("@nestjs/swagger");
19
19
  const class_validator_1 = require("class-validator");
20
20
  const throttler_1 = require("@nestjs/throttler");
21
- const mcp_constants_js_1 = require("../mcp/mcp.constants.js");
22
21
  const auth_constants_js_1 = require("./auth.constants.js");
23
22
  const public_decorator_js_1 = require("./decorators/public.decorator.js");
24
23
  const ip_utils_js_1 = require("./utils/ip.utils.js");
24
+ const session_store_js_1 = require("./session-store.js");
25
25
  class LoginDto {
26
26
  username;
27
27
  password;
@@ -38,11 +38,9 @@ __decorate([
38
38
  ], LoginDto.prototype, "password", void 0);
39
39
  const COOKIE_NAME = 'crewx_token';
40
40
  let AuthController = AuthController_1 = class AuthController {
41
- mcpToken;
42
41
  credentials;
43
42
  logger = new common_1.Logger(AuthController_1.name);
44
- constructor(mcpToken, credentials) {
45
- this.mcpToken = mcpToken;
43
+ constructor(credentials) {
46
44
  this.credentials = credentials;
47
45
  }
48
46
  // AUTH.CHECK: Guard already passed at this point — always returns 200
@@ -66,7 +64,8 @@ let AuthController = AuthController_1 = class AuthController {
66
64
  error: { code: 'INVALID_CREDENTIALS', message: 'Invalid username or password' },
67
65
  });
68
66
  }
69
- res.cookie(COOKIE_NAME, this.mcpToken.token, {
67
+ const sessionId = (0, session_store_js_1.createSession)();
68
+ res.cookie(COOKIE_NAME, sessionId, {
70
69
  httpOnly: true,
71
70
  sameSite: 'strict',
72
71
  secure: req.secure,
@@ -76,7 +75,10 @@ let AuthController = AuthController_1 = class AuthController {
76
75
  return { success: true, data: { authenticated: true } };
77
76
  }
78
77
  // AUTH.LOGOUT: clear session cookie (BaseAuthGuard applied — not @Public)
79
- logout(res) {
78
+ logout(req, res) {
79
+ const cookieSessionId = req.cookies?.[COOKIE_NAME];
80
+ if (cookieSessionId)
81
+ (0, session_store_js_1.destroySession)(cookieSessionId);
80
82
  res.cookie(COOKIE_NAME, '', { httpOnly: true, maxAge: 0, path: '/' });
81
83
  return { success: true, data: { authenticated: false } };
82
84
  }
@@ -107,15 +109,15 @@ __decorate([
107
109
  (0, swagger_1.ApiOperation)({ operationId: 'AUTH.LOGOUT', summary: 'Logout and clear session cookie' }),
108
110
  (0, common_1.Post)('logout'),
109
111
  (0, common_1.HttpCode)(200),
110
- __param(0, (0, common_1.Res)({ passthrough: true })),
112
+ __param(0, (0, common_1.Req)()),
113
+ __param(1, (0, common_1.Res)({ passthrough: true })),
111
114
  __metadata("design:type", Function),
112
- __metadata("design:paramtypes", [Object]),
115
+ __metadata("design:paramtypes", [Object, Object]),
113
116
  __metadata("design:returntype", Object)
114
117
  ], AuthController.prototype, "logout", null);
115
118
  exports.AuthController = AuthController = AuthController_1 = __decorate([
116
119
  (0, swagger_1.ApiTags)('auth'),
117
120
  (0, common_1.Controller)('auth'),
118
- __param(0, (0, common_1.Inject)(mcp_constants_js_1.MCP_TOKEN)),
119
- __param(1, (0, common_1.Inject)(auth_constants_js_1.AUTH_CREDENTIALS)),
120
- __metadata("design:paramtypes", [Object, Object])
121
+ __param(0, (0, common_1.Inject)(auth_constants_js_1.AUTH_CREDENTIALS)),
122
+ __metadata("design:paramtypes", [Object])
121
123
  ], AuthController);
@@ -18,6 +18,7 @@ const core_1 = require("@nestjs/core");
18
18
  const mcp_constants_js_1 = require("../../mcp/mcp.constants.js");
19
19
  const public_decorator_js_1 = require("../decorators/public.decorator.js");
20
20
  const ip_utils_js_1 = require("../utils/ip.utils.js");
21
+ const session_store_js_1 = require("../session-store.js");
21
22
  let BaseAuthGuard = class BaseAuthGuard {
22
23
  reflector;
23
24
  mcpToken;
@@ -43,15 +44,15 @@ let BaseAuthGuard = class BaseAuthGuard {
43
44
  return (0, ip_utils_js_1.isLoopback)(ip);
44
45
  }
45
46
  validateToken(req) {
46
- const cookieToken = req.cookies?.['crewx_token'];
47
+ const cookieSessionId = req.cookies?.['crewx_token'];
47
48
  const bearerToken = (0, ip_utils_js_1.extractBearerToken)(req);
48
- const token = cookieToken || bearerToken;
49
- if (!token)
50
- throw new common_1.UnauthorizedException('Authentication required');
51
- if (!(0, ip_utils_js_1.safeCompare)(token, this.mcpToken.token)) {
52
- throw new common_1.UnauthorizedException('Invalid token');
49
+ if (cookieSessionId && (0, session_store_js_1.validateSession)(cookieSessionId)) {
50
+ return true;
51
+ }
52
+ if (bearerToken && (0, ip_utils_js_1.safeCompare)(bearerToken, this.mcpToken.token)) {
53
+ return true;
53
54
  }
54
- return true;
55
+ throw new common_1.UnauthorizedException('Authentication required');
55
56
  }
56
57
  };
57
58
  exports.BaseAuthGuard = BaseAuthGuard;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSession = createSession;
4
+ exports.validateSession = validateSession;
5
+ exports.destroySession = destroySession;
6
+ const crypto_1 = require("crypto");
7
+ const sessions = new Map();
8
+ const SESSION_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days (matches cookie maxAge)
9
+ function createSession() {
10
+ const sessionId = (0, crypto_1.randomBytes)(32).toString('hex');
11
+ sessions.set(sessionId, { createdAt: Date.now() });
12
+ return sessionId;
13
+ }
14
+ function validateSession(sessionId) {
15
+ const session = sessions.get(sessionId);
16
+ if (!session)
17
+ return false;
18
+ if (Date.now() - session.createdAt > SESSION_MAX_AGE_MS) {
19
+ sessions.delete(sessionId);
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ function destroySession(sessionId) {
25
+ sessions.delete(sessionId);
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewx",
3
- "version": "0.8.6-rc.2",
3
+ "version": "0.8.6-rc.4",
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": {
@@ -68,17 +68,17 @@
68
68
  "wink-nlp-utils": "^2.1.0",
69
69
  "yargs": "^17.7.0",
70
70
  "zod": "^3.22.0",
71
- "@crewx/cli": "0.8.6-rc.2",
72
- "@crewx/cron": "0.1.8",
71
+ "@crewx/cli": "0.8.6-rc.4",
73
72
  "@crewx/doc": "0.1.8",
74
- "@crewx/memory": "0.1.10",
73
+ "@crewx/cron": "0.1.8",
75
74
  "@crewx/knowledge-core": "0.1.6",
76
- "@crewx/sdk": "0.8.6-rc.3",
77
- "@crewx/shared": "0.0.5",
78
- "@crewx/wbs": "0.1.9",
75
+ "@crewx/memory": "0.1.10",
76
+ "@crewx/sdk": "0.8.6-rc.5",
79
77
  "@crewx/search": "0.1.9",
80
- "@crewx/workflow": "0.3.18",
81
- "@crewx/skill": "0.1.8"
78
+ "@crewx/skill": "0.1.8",
79
+ "@crewx/wbs": "0.1.9",
80
+ "@crewx/shared": "0.0.5",
81
+ "@crewx/workflow": "0.3.18"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@ccusage/codex": "^0.0.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewx/cli",
3
- "version": "0.8.6-rc.2",
3
+ "version": "0.8.6-rc.4",
4
4
  "license": "UNLICENSED",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"