crewx 0.8.8-rc.2 → 0.8.8-rc.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/bin/cli-commands.js +1 -1
- package/dist/assets/{MarketPage-Bh1UfiC2.js → MarketPage-B5JbqK4T.js} +9 -14
- package/dist/assets/{PromptTab-DYmYVBKo.js → PromptTab-_97mSbLk.js} +7 -7
- package/dist/assets/{arc-C14FbhzP.js → arc-3FDm7TxG.js} +1 -1
- package/dist/assets/architectureDiagram-UYN6MBPD-D8X56UlC.js +36 -0
- package/dist/assets/blockDiagram-ZHA2E4KO-B6jUnEVc.js +121 -0
- package/dist/assets/{c4Diagram-6F5ED5ID-DLSfQS-Z.js → c4Diagram-6F5ED5ID-Cy8x6Ims.js} +1 -1
- package/dist/assets/channel-Bs0J8jjN.js +1 -0
- package/dist/assets/{chunk-5HRBRIJM-j7YcJ7Bx.js → chunk-5HRBRIJM-Bh819Q5A.js} +1 -1
- package/dist/assets/{chunk-7U56Z5CX-Baqt1cj3.js → chunk-7U56Z5CX-CfNtPGa8.js} +1 -1
- package/dist/assets/{chunk-ASOPGD6M-F1YJ2mzH.js → chunk-ASOPGD6M-CMR1ElQ6.js} +1 -1
- package/dist/assets/{chunk-KFBOBJHC-Dvhzl6G9.js → chunk-KFBOBJHC-Dm2I1dHM.js} +1 -1
- package/dist/assets/{chunk-T2TOU4HS-BDczCXP6.js → chunk-T2TOU4HS-0tf-aNtk.js} +1 -1
- package/dist/assets/{chunk-TMUBEWPD-4BhwBH_F.js → chunk-TMUBEWPD-b5ZrpKJw.js} +1 -1
- package/dist/assets/classDiagram-LNE6IOMH-B1-6qC07.js +1 -0
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-B1-6qC07.js +1 -0
- package/dist/assets/dagre-4EVJKHTY-zYmFBlwR.js +4 -0
- package/dist/assets/diagram-QW4FP2JN-DkZngH-Z.js +24 -0
- package/dist/assets/{erDiagram-6RL3IURR-DRhNKkH0.js → erDiagram-6RL3IURR-8TfuIQ6D.js} +2 -2
- package/dist/assets/{flowDiagram-7ASYPVHJ-BDJZsUJS.js → flowDiagram-7ASYPVHJ-BnhSLZTo.js} +1 -1
- package/dist/assets/{ganttDiagram-NTVNEXSI-BYBeyULV.js → ganttDiagram-NTVNEXSI--jDGPnYB.js} +3 -3
- package/dist/assets/gitGraph-YCYPL57B-DkCOt6bF.js +133 -0
- package/dist/assets/gitGraphDiagram-NRZ2UAAF-D4p17Lzw.js +65 -0
- package/dist/assets/graph-BlwPajkw.js +1 -0
- package/dist/assets/infoDiagram-A4XQUW5V-COnYi0Q8.js +2 -0
- package/dist/assets/{journeyDiagram-G5WM74LC-BhSSQ4Od.js → journeyDiagram-G5WM74LC-CTZcqDJf.js} +1 -1
- package/dist/assets/{kanban-definition-QRCXZQQD-CZOkitn-.js → kanban-definition-QRCXZQQD-CezLYsK3.js} +1 -1
- package/dist/assets/layout-Bvwu4dCi.js +1 -0
- package/dist/assets/{linear-BRR4YPhd.js → linear-BcYBjD0V.js} +1 -1
- package/dist/assets/main-CgT_Atre.js +1210 -0
- package/dist/assets/main-kJ_DZpZU.css +10 -0
- package/dist/assets/{mindmap-definition-GWI6TPTV-CiMg1rSL.js → mindmap-definition-GWI6TPTV-NM7qIPkp.js} +1 -1
- package/dist/assets/pieDiagram-YF2LJOPJ-Ck2UBMrZ.js +30 -0
- package/dist/assets/{quadrantDiagram-OS5C2QUG-cYMjsA0L.js → quadrantDiagram-OS5C2QUG-0OKnDbeW.js} +1 -1
- package/dist/assets/{requirementDiagram-MIRIMTAZ-D8piGWQV.js → requirementDiagram-MIRIMTAZ-Nq_mStSk.js} +2 -2
- package/dist/assets/{sankeyDiagram-Y46BX6SQ-DfNocUti.js → sankeyDiagram-Y46BX6SQ-ZPgHNk3C.js} +1 -1
- package/dist/assets/{sequenceDiagram-G6AWOVSC-Xu2X2iUX.js → sequenceDiagram-G6AWOVSC-BRzYMlx2.js} +1 -1
- package/dist/assets/stateDiagram-MAYHULR4-CCNAeCLE.js +1 -0
- package/dist/assets/stateDiagram-v2-4JROLMXI-Bz1kxCVf.js +1 -0
- package/dist/assets/{timeline-definition-U7ZMHBDA-CNra78s7.js → timeline-definition-U7ZMHBDA-BSw5vmnf.js} +1 -1
- package/dist/assets/{xychartDiagram-6QU3TZC5-BSwY5sKC.js → xychartDiagram-6QU3TZC5-BuqrahPf.js} +2 -2
- package/dist/index.html +2 -2
- package/dist-server/app.module.js +4 -0
- package/dist-server/bootstrap/crewx-server.js +6 -3
- package/dist-server/common/limits/defaults.js +2 -0
- package/dist-server/domain/auth/auth.module.js +2 -1
- package/dist-server/domain/auth/guards/base-auth.guard.js +36 -14
- package/dist-server/domain/doc/doc.controller.js +21 -0
- package/dist-server/domain/doc/doc.service.js +36 -6
- package/dist-server/domain/doc/image-constants.js +11 -0
- package/dist-server/domain/events/events.module.js +31 -0
- package/dist-server/domain/events/server-conversation.plugin.js +45 -0
- package/dist-server/domain/events/workspace-event.bus.js +32 -0
- package/dist-server/domain/events/workspace-event.types.js +12 -0
- package/dist-server/domain/events/workspace-events-test.controller.js +83 -0
- package/dist-server/domain/events/workspace-events.controller.js +47 -0
- package/dist-server/domain/mcp/browser-session.store.js +50 -0
- package/dist-server/domain/mcp/chromex-blacklist.js +26 -0
- package/dist-server/domain/mcp/chromex-negotiate.controller.js +77 -0
- package/dist-server/domain/mcp/chromex-negotiate.dto.js +95 -0
- package/dist-server/domain/mcp/chromex-negotiate.service.js +249 -0
- package/dist-server/domain/mcp/mcp-auth.guard.js +40 -26
- package/dist-server/domain/mcp/mcp.constants.js +4 -1
- package/dist-server/domain/mcp/mcp.controller.js +5 -4
- package/dist-server/domain/mcp/mcp.module.js +6 -2
- package/dist-server/domain/mcp/mcp.service.js +62 -5
- package/dist-server/domain/message/dto/send-message.dto.js +48 -1
- package/dist-server/domain/message/message.controller.js +40 -1
- package/dist-server/domain/message/message.service.js +50 -5
- package/dist-server/domain/task/task.service.js +34 -13
- package/dist-server/domain/thread/thread.controller.js +16 -0
- package/dist-server/domain/thread/thread.module.js +2 -0
- package/dist-server/domain/thread/thread.service.js +80 -11
- package/dist-server/domain/token/token.controller.js +73 -0
- package/dist-server/domain/token/token.dto.js +56 -0
- package/dist-server/domain/token/token.module.js +22 -0
- package/dist-server/domain/token/token.service.js +228 -0
- package/dist-server/main.js +23 -2
- package/dist-server/modules/crewx-pool.service.js +129 -5
- package/dist-server/modules/crewx.module.js +6 -0
- package/package.json +17 -14
- package/packages/cli/dist/builtin.js +2 -0
- package/packages/cli/dist/commands/init.js +5 -5
- package/packages/cli/dist/commands/registry.js +1 -1
- package/packages/cli/package.json +2 -1
- package/dist/assets/_baseUniq-B5kIqYhv.js +0 -1
- package/dist/assets/architectureDiagram-UYN6MBPD-Bvk4uHjQ.js +0 -36
- package/dist/assets/blockDiagram-ZHA2E4KO-C9-iioqe.js +0 -121
- package/dist/assets/channel-B1S1YSXQ.js +0 -1
- package/dist/assets/classDiagram-LNE6IOMH-BMiPdBl0.js +0 -1
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-BMiPdBl0.js +0 -1
- package/dist/assets/clone-DkRtovKq.js +0 -1
- package/dist/assets/dagre-4EVJKHTY-LsgK-pqb.js +0 -4
- package/dist/assets/diagram-QW4FP2JN-uvhH5Eln.js +0 -24
- package/dist/assets/gitGraph-YCYPL57B-D9Hkzkze.js +0 -133
- package/dist/assets/gitGraphDiagram-NRZ2UAAF-oWiYlrjZ.js +0 -65
- package/dist/assets/graph-D66Q_If6.js +0 -1
- package/dist/assets/infoDiagram-A4XQUW5V-aJ6i9Snw.js +0 -2
- package/dist/assets/layout-CaxkerSC.js +0 -1
- package/dist/assets/main-3IYf82MG.css +0 -10
- package/dist/assets/main-DCHJFonK.js +0 -1167
- package/dist/assets/min-uKYaLZUT.js +0 -1
- package/dist/assets/pieDiagram-YF2LJOPJ-BFJ_DAm-.js +0 -30
- package/dist/assets/stateDiagram-MAYHULR4-WmJVTQsF.js +0 -1
- package/dist/assets/stateDiagram-v2-4JROLMXI-DNAOV6w0.js +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TokenController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const token_service_js_1 = require("./token.service.js");
|
|
18
|
+
const token_dto_js_1 = require("./token.dto.js");
|
|
19
|
+
let TokenController = class TokenController {
|
|
20
|
+
tokenService;
|
|
21
|
+
constructor(tokenService) {
|
|
22
|
+
this.tokenService = tokenService;
|
|
23
|
+
}
|
|
24
|
+
listTokens() {
|
|
25
|
+
return { success: true, data: this.tokenService.listTokens() };
|
|
26
|
+
}
|
|
27
|
+
async createToken(dto) {
|
|
28
|
+
const { token, meta } = await this.tokenService.createToken(dto, 'direct');
|
|
29
|
+
const response = new token_dto_js_1.CreateTokenResponseDto();
|
|
30
|
+
Object.assign(response, meta);
|
|
31
|
+
response.token = token;
|
|
32
|
+
return { success: true, data: response };
|
|
33
|
+
}
|
|
34
|
+
getToken(id) {
|
|
35
|
+
return { success: true, data: this.tokenService.getToken(id) };
|
|
36
|
+
}
|
|
37
|
+
async revokeToken(id) {
|
|
38
|
+
await this.tokenService.revokeToken(id);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.TokenController = TokenController;
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, common_1.Get)(),
|
|
44
|
+
__metadata("design:type", Function),
|
|
45
|
+
__metadata("design:paramtypes", []),
|
|
46
|
+
__metadata("design:returntype", Object)
|
|
47
|
+
], TokenController.prototype, "listTokens", null);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, common_1.Post)(),
|
|
50
|
+
__param(0, (0, common_1.Body)(new common_1.ValidationPipe({ whitelist: true }))),
|
|
51
|
+
__metadata("design:type", Function),
|
|
52
|
+
__metadata("design:paramtypes", [token_dto_js_1.CreateTokenDto]),
|
|
53
|
+
__metadata("design:returntype", Promise)
|
|
54
|
+
], TokenController.prototype, "createToken", null);
|
|
55
|
+
__decorate([
|
|
56
|
+
(0, common_1.Get)(':id'),
|
|
57
|
+
__param(0, (0, common_1.Param)('id')),
|
|
58
|
+
__metadata("design:type", Function),
|
|
59
|
+
__metadata("design:paramtypes", [String]),
|
|
60
|
+
__metadata("design:returntype", Object)
|
|
61
|
+
], TokenController.prototype, "getToken", null);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, common_1.Delete)(':id'),
|
|
64
|
+
(0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
|
|
65
|
+
__param(0, (0, common_1.Param)('id')),
|
|
66
|
+
__metadata("design:type", Function),
|
|
67
|
+
__metadata("design:paramtypes", [String]),
|
|
68
|
+
__metadata("design:returntype", Promise)
|
|
69
|
+
], TokenController.prototype, "revokeToken", null);
|
|
70
|
+
exports.TokenController = TokenController = __decorate([
|
|
71
|
+
(0, common_1.Controller)('tokens'),
|
|
72
|
+
__metadata("design:paramtypes", [token_service_js_1.TokenService])
|
|
73
|
+
], TokenController);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CreateTokenResponseDto = exports.TokenMetaResponseDto = exports.CreateTokenDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class CreateTokenDto {
|
|
15
|
+
name;
|
|
16
|
+
expiresIn;
|
|
17
|
+
agentScope;
|
|
18
|
+
browserScope;
|
|
19
|
+
}
|
|
20
|
+
exports.CreateTokenDto = CreateTokenDto;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_validator_1.IsString)(),
|
|
23
|
+
(0, class_validator_1.MinLength)(1),
|
|
24
|
+
(0, class_validator_1.MaxLength)(50),
|
|
25
|
+
__metadata("design:type", String)
|
|
26
|
+
], CreateTokenDto.prototype, "name", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, class_validator_1.IsIn)(['30d', '90d', '1y']),
|
|
29
|
+
__metadata("design:type", String)
|
|
30
|
+
], CreateTokenDto.prototype, "expiresIn", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_validator_1.IsOptional)(),
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], CreateTokenDto.prototype, "agentScope", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, class_validator_1.IsOptional)(),
|
|
38
|
+
(0, class_validator_1.IsString)(),
|
|
39
|
+
__metadata("design:type", String)
|
|
40
|
+
], CreateTokenDto.prototype, "browserScope", void 0);
|
|
41
|
+
class TokenMetaResponseDto {
|
|
42
|
+
id;
|
|
43
|
+
name;
|
|
44
|
+
jti;
|
|
45
|
+
agentScope;
|
|
46
|
+
source;
|
|
47
|
+
expiresAt;
|
|
48
|
+
createdAt;
|
|
49
|
+
revokedAt;
|
|
50
|
+
lastUsedAt;
|
|
51
|
+
}
|
|
52
|
+
exports.TokenMetaResponseDto = TokenMetaResponseDto;
|
|
53
|
+
class CreateTokenResponseDto extends TokenMetaResponseDto {
|
|
54
|
+
token;
|
|
55
|
+
}
|
|
56
|
+
exports.CreateTokenResponseDto = CreateTokenResponseDto;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.TokenModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const token_controller_js_1 = require("./token.controller.js");
|
|
12
|
+
const token_service_js_1 = require("./token.service.js");
|
|
13
|
+
let TokenModule = class TokenModule {
|
|
14
|
+
};
|
|
15
|
+
exports.TokenModule = TokenModule;
|
|
16
|
+
exports.TokenModule = TokenModule = __decorate([
|
|
17
|
+
(0, common_1.Module)({
|
|
18
|
+
controllers: [token_controller_js_1.TokenController],
|
|
19
|
+
providers: [token_service_js_1.TokenService],
|
|
20
|
+
exports: [token_service_js_1.TokenService],
|
|
21
|
+
})
|
|
22
|
+
], TokenModule);
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TokenService = void 0;
|
|
13
|
+
exports.readJwtSecret = readJwtSecret;
|
|
14
|
+
exports.ensureJwtSecret = ensureJwtSecret;
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const fs_1 = require("fs");
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
const os_1 = require("os");
|
|
19
|
+
const crypto_1 = require("crypto");
|
|
20
|
+
const jose_1 = require("jose");
|
|
21
|
+
const nanoid_1 = require("nanoid");
|
|
22
|
+
const limits_provider_js_1 = require("../../common/limits/limits-provider.js");
|
|
23
|
+
const token_dto_js_1 = require("./token.dto.js");
|
|
24
|
+
function resolveDbPath() {
|
|
25
|
+
if (process.env.CREWX_DB)
|
|
26
|
+
return process.env.CREWX_DB;
|
|
27
|
+
return (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'crewx.db');
|
|
28
|
+
}
|
|
29
|
+
function hashToken(rawToken) {
|
|
30
|
+
return (0, crypto_1.createHash)('sha256').update(rawToken).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
function calcExpiresAt(expiresIn) {
|
|
33
|
+
const now = new Date();
|
|
34
|
+
if (expiresIn === '30d')
|
|
35
|
+
return new Date(now.getTime() + 30 * 86400000);
|
|
36
|
+
if (expiresIn === '90d')
|
|
37
|
+
return new Date(now.getTime() + 90 * 86400000);
|
|
38
|
+
return new Date(now.getTime() + 365 * 86400000);
|
|
39
|
+
}
|
|
40
|
+
function rowToDto(row) {
|
|
41
|
+
const dto = new token_dto_js_1.TokenMetaResponseDto();
|
|
42
|
+
dto.id = row.id;
|
|
43
|
+
dto.name = row.name;
|
|
44
|
+
dto.jti = row.jti;
|
|
45
|
+
dto.agentScope = row.agent_scope;
|
|
46
|
+
dto.source = row.source;
|
|
47
|
+
dto.expiresAt = row.expires_at;
|
|
48
|
+
dto.createdAt = row.created_at;
|
|
49
|
+
dto.revokedAt = row.revoked_at;
|
|
50
|
+
dto.lastUsedAt = row.last_used_at;
|
|
51
|
+
return dto;
|
|
52
|
+
}
|
|
53
|
+
let TokenService = class TokenService {
|
|
54
|
+
limitsProvider;
|
|
55
|
+
db;
|
|
56
|
+
secretBytes;
|
|
57
|
+
constructor(limitsProvider) {
|
|
58
|
+
this.limitsProvider = limitsProvider;
|
|
59
|
+
}
|
|
60
|
+
onModuleInit() {
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
62
|
+
const Database = require('better-sqlite3');
|
|
63
|
+
const dbPath = resolveDbPath();
|
|
64
|
+
this.db = new Database(dbPath);
|
|
65
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
66
|
+
this.db.exec('PRAGMA busy_timeout = 5000');
|
|
67
|
+
this.ensureSchema();
|
|
68
|
+
const secretStr = readJwtSecret();
|
|
69
|
+
this.secretBytes = new Uint8Array(Buffer.from(secretStr, 'hex'));
|
|
70
|
+
}
|
|
71
|
+
onModuleDestroy() {
|
|
72
|
+
try {
|
|
73
|
+
this.db?.close();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// ignore close errors during shutdown
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async createToken(dto, source = 'direct') {
|
|
80
|
+
if (source === 'negotiate' && !dto.agentScope) {
|
|
81
|
+
throw new common_1.BadRequestException('agentScope is required for negotiate tokens');
|
|
82
|
+
}
|
|
83
|
+
const limits = await this.limitsProvider.resolve();
|
|
84
|
+
if (limits.tier === 'FREE') {
|
|
85
|
+
const row = this.db
|
|
86
|
+
.prepare('SELECT COUNT(*) as cnt FROM api_tokens WHERE revoked_at IS NULL')
|
|
87
|
+
.get();
|
|
88
|
+
const activeCount = row?.cnt ?? 0;
|
|
89
|
+
if (activeCount >= limits.apiTokens) {
|
|
90
|
+
throw new common_1.HttpException({ code: 'TOKEN_LIMIT_EXCEEDED', limit: limits.apiTokens }, 412);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const id = 'tok_' + (0, nanoid_1.nanoid)(21);
|
|
94
|
+
const jti = 'jti_' + (0, nanoid_1.nanoid)(21);
|
|
95
|
+
const expiresAt = calcExpiresAt(dto.expiresIn);
|
|
96
|
+
const createdAt = new Date().toISOString();
|
|
97
|
+
const jwt = await new jose_1.SignJWT({
|
|
98
|
+
sub: dto.name,
|
|
99
|
+
jti,
|
|
100
|
+
scope: dto.agentScope ?? null,
|
|
101
|
+
browser_scope: dto.browserScope ?? null,
|
|
102
|
+
source,
|
|
103
|
+
})
|
|
104
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
105
|
+
.setExpirationTime(Math.floor(expiresAt.getTime() / 1000))
|
|
106
|
+
.sign(this.secretBytes);
|
|
107
|
+
const rawToken = 'crewx_' + jwt;
|
|
108
|
+
const tokenHash = hashToken(rawToken);
|
|
109
|
+
this.db
|
|
110
|
+
.prepare(`INSERT INTO api_tokens (id, name, token_hash, jti, agent_scope, source, expires_at, created_at)
|
|
111
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
112
|
+
.run(id, dto.name, tokenHash, jti, dto.agentScope ?? null, source, expiresAt.toISOString(), createdAt);
|
|
113
|
+
const row = this.db
|
|
114
|
+
.prepare('SELECT * FROM api_tokens WHERE id = ?')
|
|
115
|
+
.get(id);
|
|
116
|
+
return { token: rawToken, meta: rowToDto(row) };
|
|
117
|
+
}
|
|
118
|
+
async validateToken(rawToken) {
|
|
119
|
+
if (!rawToken.startsWith('crewx_'))
|
|
120
|
+
return null;
|
|
121
|
+
const jwt = rawToken.slice(6);
|
|
122
|
+
let jti;
|
|
123
|
+
let agentScope;
|
|
124
|
+
let browserScope;
|
|
125
|
+
let source;
|
|
126
|
+
let sub;
|
|
127
|
+
try {
|
|
128
|
+
const { payload } = await (0, jose_1.jwtVerify)(jwt, this.secretBytes);
|
|
129
|
+
jti = payload.jti ?? '';
|
|
130
|
+
sub = payload.sub ?? '';
|
|
131
|
+
agentScope = payload['scope'] ?? null;
|
|
132
|
+
browserScope = payload['browser_scope'] ?? null;
|
|
133
|
+
source = payload['source'] === 'negotiate' ? 'negotiate' : 'direct';
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const tokenHash = hashToken(rawToken);
|
|
139
|
+
const row = this.db
|
|
140
|
+
.prepare('SELECT * FROM api_tokens WHERE token_hash = ?')
|
|
141
|
+
.get(tokenHash);
|
|
142
|
+
if (!row)
|
|
143
|
+
return null;
|
|
144
|
+
if (row.revoked_at)
|
|
145
|
+
return null;
|
|
146
|
+
if (row.jti !== jti)
|
|
147
|
+
return null;
|
|
148
|
+
if (row.source !== source)
|
|
149
|
+
return null;
|
|
150
|
+
// best-effort update, non-blocking
|
|
151
|
+
try {
|
|
152
|
+
this.db
|
|
153
|
+
.prepare("UPDATE api_tokens SET last_used_at = datetime('now') WHERE id = ?")
|
|
154
|
+
.run(row.id);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// ignore
|
|
158
|
+
}
|
|
159
|
+
return { sub, jti, agentScope, browserScope, source };
|
|
160
|
+
}
|
|
161
|
+
async revokeToken(id) {
|
|
162
|
+
const existing = this.db
|
|
163
|
+
.prepare('SELECT id FROM api_tokens WHERE id = ?')
|
|
164
|
+
.get(id);
|
|
165
|
+
if (!existing)
|
|
166
|
+
throw new common_1.NotFoundException(`Token ${id} not found`);
|
|
167
|
+
this.db
|
|
168
|
+
.prepare("UPDATE api_tokens SET revoked_at = datetime('now') WHERE id = ?")
|
|
169
|
+
.run(id);
|
|
170
|
+
}
|
|
171
|
+
listTokens() {
|
|
172
|
+
const rows = this.db
|
|
173
|
+
.prepare('SELECT * FROM api_tokens ORDER BY created_at DESC')
|
|
174
|
+
.all();
|
|
175
|
+
return rows.map(rowToDto);
|
|
176
|
+
}
|
|
177
|
+
getToken(id) {
|
|
178
|
+
const row = this.db
|
|
179
|
+
.prepare('SELECT * FROM api_tokens WHERE id = ?')
|
|
180
|
+
.get(id);
|
|
181
|
+
if (!row)
|
|
182
|
+
throw new common_1.NotFoundException(`Token ${id} not found`);
|
|
183
|
+
return rowToDto(row);
|
|
184
|
+
}
|
|
185
|
+
ensureSchema() {
|
|
186
|
+
this.db.exec(`
|
|
187
|
+
CREATE TABLE IF NOT EXISTS api_tokens (
|
|
188
|
+
id TEXT PRIMARY KEY,
|
|
189
|
+
name TEXT NOT NULL,
|
|
190
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
191
|
+
jti TEXT NOT NULL UNIQUE,
|
|
192
|
+
agent_scope TEXT,
|
|
193
|
+
source TEXT NOT NULL DEFAULT 'direct',
|
|
194
|
+
expires_at TEXT NOT NULL,
|
|
195
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
196
|
+
revoked_at TEXT,
|
|
197
|
+
last_used_at TEXT
|
|
198
|
+
)
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
exports.TokenService = TokenService;
|
|
203
|
+
exports.TokenService = TokenService = __decorate([
|
|
204
|
+
(0, common_1.Injectable)(),
|
|
205
|
+
__metadata("design:paramtypes", [limits_provider_js_1.LimitsProvider])
|
|
206
|
+
], TokenService);
|
|
207
|
+
// --- jwt_secret helpers ---
|
|
208
|
+
function readJwtSecret() {
|
|
209
|
+
const secretPath = getJwtSecretPath();
|
|
210
|
+
if (!(0, fs_1.existsSync)(secretPath)) {
|
|
211
|
+
throw new Error('[crewx] jwt_secret file not found. Call ensureJwtSecret() at bootstrap.');
|
|
212
|
+
}
|
|
213
|
+
return (0, fs_1.readFileSync)(secretPath, 'utf8').trim();
|
|
214
|
+
}
|
|
215
|
+
function ensureJwtSecret() {
|
|
216
|
+
const secretPath = getJwtSecretPath();
|
|
217
|
+
if (!(0, fs_1.existsSync)(secretPath)) {
|
|
218
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(secretPath), { recursive: true });
|
|
219
|
+
const secret = (0, crypto_1.randomBytes)(32).toString('hex');
|
|
220
|
+
// mode: 0o600 — POSIX only; Windows relies on NTFS ACL from parent dir
|
|
221
|
+
(0, fs_1.writeFileSync)(secretPath, secret, { mode: 0o600, encoding: 'utf8' });
|
|
222
|
+
}
|
|
223
|
+
return (0, fs_1.readFileSync)(secretPath, 'utf8').trim();
|
|
224
|
+
}
|
|
225
|
+
function getJwtSecretPath() {
|
|
226
|
+
const crewxHome = process.env.CREWX_HOME ?? (0, path_1.join)((0, os_1.homedir)(), '.crewx');
|
|
227
|
+
return (0, path_1.join)(crewxHome, 'jwt_secret');
|
|
228
|
+
}
|
package/dist-server/main.js
CHANGED
|
@@ -66,6 +66,20 @@ async function bootstrap() {
|
|
|
66
66
|
}
|
|
67
67
|
// Cookie parser must come before Guards that read req.cookies
|
|
68
68
|
app.use((0, cookie_parser_1.default)());
|
|
69
|
+
// CSRF Double Submit Cookie — issue on first visit (httpOnly:false so JS can read it)
|
|
70
|
+
app.use((req, res, next) => {
|
|
71
|
+
if (!req.cookies?.['crewx_csrf']) {
|
|
72
|
+
const { randomBytes } = require('crypto');
|
|
73
|
+
const token = randomBytes(32).toString('hex');
|
|
74
|
+
res.cookie('crewx_csrf', token, {
|
|
75
|
+
httpOnly: false,
|
|
76
|
+
sameSite: 'strict',
|
|
77
|
+
path: '/',
|
|
78
|
+
secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
next();
|
|
82
|
+
});
|
|
69
83
|
// Conditional JSON body parser: skip for /adapters/* (raw body needed for webhook signatures),
|
|
70
84
|
// apply express.json() for all other routes so DTOs receive parsed bodies.
|
|
71
85
|
app.use((req, res, next) => {
|
|
@@ -86,14 +100,21 @@ async function bootstrap() {
|
|
|
86
100
|
callback(null, (0, cors_js_1.isAllowedOrigin)(origin, allowedExtensionIds));
|
|
87
101
|
},
|
|
88
102
|
credentials: true,
|
|
89
|
-
allowedHeaders: ['Content-Type', 'Authorization', 'Mcp-Session-Id', 'Accept'],
|
|
103
|
+
allowedHeaders: ['Content-Type', 'Authorization', 'Mcp-Session-Id', 'Accept', 'X-CSRF-Token'],
|
|
90
104
|
exposedHeaders: ['Mcp-Session-Id'],
|
|
91
105
|
});
|
|
92
106
|
// Enable class-validator DTO validation
|
|
93
107
|
app.useGlobalPipes(new common_1.ValidationPipe({ whitelist: true, transform: true }));
|
|
94
108
|
// Global prefix for API routes (exclude MCP and auth login/logout — they live at /mcp, /auth/*)
|
|
95
109
|
app.setGlobalPrefix('api/v1', {
|
|
96
|
-
exclude: [
|
|
110
|
+
exclude: [
|
|
111
|
+
{ path: 'mcp', method: common_1.RequestMethod.ALL },
|
|
112
|
+
{ path: 'mcp/health', method: common_1.RequestMethod.ALL },
|
|
113
|
+
{ path: 'auth/login', method: common_1.RequestMethod.ALL },
|
|
114
|
+
{ path: 'auth/logout', method: common_1.RequestMethod.ALL },
|
|
115
|
+
{ path: 'api/chromex', method: common_1.RequestMethod.ALL },
|
|
116
|
+
{ path: 'api/chromex/(.*)', method: common_1.RequestMethod.ALL },
|
|
117
|
+
],
|
|
97
118
|
});
|
|
98
119
|
// Swagger documentation setup
|
|
99
120
|
const swaggerConfig = new swagger_1.DocumentBuilder()
|
|
@@ -5,6 +5,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
8
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
15
|
exports.CrewxPoolService = void 0;
|
|
10
16
|
const common_1 = require("@nestjs/common");
|
|
@@ -12,6 +18,7 @@ const path_1 = require("path");
|
|
|
12
18
|
const fs_1 = require("fs");
|
|
13
19
|
const crewx_server_js_1 = require("../bootstrap/crewx-server.js");
|
|
14
20
|
const workspace_context_store_js_1 = require("../common/workspace-context.store.js");
|
|
21
|
+
const workspace_event_bus_js_1 = require("../domain/events/workspace-event.bus.js");
|
|
15
22
|
/**
|
|
16
23
|
* CrewxPoolService — per-workspace Crewx instance pool.
|
|
17
24
|
*
|
|
@@ -26,7 +33,12 @@ const workspace_context_store_js_1 = require("../common/workspace-context.store.
|
|
|
26
33
|
* Services must NEVER call crewx.close() themselves.
|
|
27
34
|
*/
|
|
28
35
|
let CrewxPoolService = class CrewxPoolService {
|
|
36
|
+
eventBus;
|
|
29
37
|
pool = new Map();
|
|
38
|
+
poolMeta = new Map();
|
|
39
|
+
constructor(eventBus) {
|
|
40
|
+
this.eventBus = eventBus;
|
|
41
|
+
}
|
|
30
42
|
/**
|
|
31
43
|
* Return the Crewx instance for the current request's workspace.
|
|
32
44
|
* Uses AsyncLocalStorage-backed getWorkspacePath().
|
|
@@ -40,9 +52,14 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
40
52
|
}
|
|
41
53
|
/**
|
|
42
54
|
* Return the Crewx instance for an explicit workspace path.
|
|
43
|
-
*
|
|
55
|
+
* When CREWX_CONFIG is set, all paths resolve to a single shared instance
|
|
56
|
+
* keyed by '__crewx_config__'. Otherwise, workspace yaml is used per path.
|
|
44
57
|
*/
|
|
45
58
|
getForPath(workspacePath) {
|
|
59
|
+
const configPath = process.env.CREWX_CONFIG;
|
|
60
|
+
if (configPath) {
|
|
61
|
+
return this.getOrCreate('__crewx_config__', configPath);
|
|
62
|
+
}
|
|
46
63
|
const candidates = [
|
|
47
64
|
(0, path_1.join)(workspacePath, 'crewx.yaml'),
|
|
48
65
|
(0, path_1.join)(workspacePath, 'crewx.yml'),
|
|
@@ -52,24 +69,131 @@ let CrewxPoolService = class CrewxPoolService {
|
|
|
52
69
|
return this.getOrCreate(workspacePath, yamlPath);
|
|
53
70
|
}
|
|
54
71
|
invalidate(workspacePath) {
|
|
55
|
-
const key = workspacePath;
|
|
72
|
+
const key = process.env.CREWX_CONFIG ? '__crewx_config__' : workspacePath;
|
|
73
|
+
this.evict(key);
|
|
74
|
+
}
|
|
75
|
+
evict(key) {
|
|
76
|
+
const meta = this.poolMeta.get(key);
|
|
77
|
+
if (meta) {
|
|
78
|
+
meta.unsub();
|
|
79
|
+
this.poolMeta.delete(key);
|
|
80
|
+
}
|
|
56
81
|
const existing = this.pool.get(key);
|
|
57
82
|
if (existing) {
|
|
58
|
-
existing.then(c => c.close()).catch(() => { });
|
|
83
|
+
existing.then((c) => c.close()).catch(() => { });
|
|
59
84
|
this.pool.delete(key);
|
|
60
85
|
}
|
|
61
86
|
}
|
|
62
87
|
getOrCreate(key, configPath) {
|
|
63
88
|
if (!this.pool.has(key)) {
|
|
64
|
-
|
|
89
|
+
const instancePromise = (0, crewx_server_js_1.createServerCrewx)(configPath, this.eventBus);
|
|
90
|
+
this.pool.set(key, instancePromise);
|
|
91
|
+
// Wire up SDK event subscriptions after the instance resolves.
|
|
92
|
+
instancePromise.then((crewx) => this.wireEvents(key, crewx)).catch(() => { });
|
|
65
93
|
}
|
|
66
94
|
return this.pool.get(key);
|
|
67
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to task:start / task:end / task:output on a newly created Crewx instance and
|
|
98
|
+
* fan-out task.created / task.updated / task.logged events to WorkspaceEventBus.
|
|
99
|
+
* thread.* and message.* events are emitted by ServerConversationPlugin instead.
|
|
100
|
+
* threadId for task.updated is read from task:end metadata (propagated by SDK from task:start).
|
|
101
|
+
* threadId for task.logged is enriched from threadIdByTask (task:output has no threadId).
|
|
102
|
+
*/
|
|
103
|
+
wireEvents(key, crewx) {
|
|
104
|
+
if (!this.eventBus)
|
|
105
|
+
return;
|
|
106
|
+
const bus = this.eventBus;
|
|
107
|
+
const wsId = crewx.workspaceId;
|
|
108
|
+
// task.logged: taskId → threadId (populated on task:start, cleared on task:end)
|
|
109
|
+
const threadIdByTask = new Map();
|
|
110
|
+
// task.logged throttle: taskId → pending setTimeout handle (200ms window)
|
|
111
|
+
const throttleByTask = new Map();
|
|
112
|
+
const THROTTLE_MS = 200;
|
|
113
|
+
// Hard cap: evict oldest entry when exceeded (guards against missed task:end on crash)
|
|
114
|
+
const MAX_TASK_ENTRIES = 500;
|
|
115
|
+
const unsubStart = crewx.on('task:start', (e) => {
|
|
116
|
+
threadIdByTask.set(e.traceId, e.threadId ?? '');
|
|
117
|
+
if (threadIdByTask.size > MAX_TASK_ENTRIES) {
|
|
118
|
+
const oldest = threadIdByTask.keys().next().value;
|
|
119
|
+
if (oldest !== undefined)
|
|
120
|
+
threadIdByTask.delete(oldest);
|
|
121
|
+
}
|
|
122
|
+
bus.emit(wsId, {
|
|
123
|
+
type: 'task.created',
|
|
124
|
+
data: {
|
|
125
|
+
taskId: e.traceId,
|
|
126
|
+
threadId: e.threadId ?? '',
|
|
127
|
+
agent: e.agentRef,
|
|
128
|
+
status: 'running',
|
|
129
|
+
updatedAt: e.timestamp.toISOString(),
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
const unsubOutput = crewx.on('task:output', (e) => {
|
|
134
|
+
const taskId = e.traceId;
|
|
135
|
+
if (throttleByTask.has(taskId))
|
|
136
|
+
return; // absorb within 200ms window
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
throttleByTask.delete(taskId);
|
|
139
|
+
bus.emit(wsId, {
|
|
140
|
+
type: 'task.logged',
|
|
141
|
+
data: { taskId, threadId: threadIdByTask.get(taskId) ?? '' },
|
|
142
|
+
});
|
|
143
|
+
}, THROTTLE_MS);
|
|
144
|
+
throttleByTask.set(taskId, timer);
|
|
145
|
+
});
|
|
146
|
+
const unsubEnd = crewx.on('task:end', (e) => {
|
|
147
|
+
const taskId = e.traceId;
|
|
148
|
+
// flush pending task.logged before emitting task.updated
|
|
149
|
+
const timer = throttleByTask.get(taskId);
|
|
150
|
+
if (timer !== undefined) {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
throttleByTask.delete(taskId);
|
|
153
|
+
bus.emit(wsId, {
|
|
154
|
+
type: 'task.logged',
|
|
155
|
+
data: { taskId, threadId: threadIdByTask.get(taskId) ?? '' },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
threadIdByTask.delete(taskId);
|
|
159
|
+
const threadId = e.metadata?.threadId ?? '';
|
|
160
|
+
bus.emit(wsId, {
|
|
161
|
+
type: 'task.updated',
|
|
162
|
+
data: {
|
|
163
|
+
taskId: e.traceId,
|
|
164
|
+
threadId,
|
|
165
|
+
agent: e.agentRef,
|
|
166
|
+
status: e.error ? 'failed' : 'success',
|
|
167
|
+
...(e.error ? { error: e.error.message } : {}),
|
|
168
|
+
...(e.costUsd != null ? { costUsd: e.costUsd } : {}),
|
|
169
|
+
updatedAt: e.timestamp.toISOString(),
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
this.poolMeta.set(key, {
|
|
174
|
+
unsub: () => {
|
|
175
|
+
unsubStart();
|
|
176
|
+
unsubOutput();
|
|
177
|
+
unsubEnd();
|
|
178
|
+
for (const t of throttleByTask.values())
|
|
179
|
+
clearTimeout(t);
|
|
180
|
+
throttleByTask.clear();
|
|
181
|
+
threadIdByTask.clear();
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
68
185
|
async onApplicationShutdown() {
|
|
186
|
+
// Unsubscribe all SDK listeners before closing instances.
|
|
187
|
+
for (const meta of this.poolMeta.values()) {
|
|
188
|
+
meta.unsub();
|
|
189
|
+
}
|
|
190
|
+
this.poolMeta.clear();
|
|
69
191
|
await Promise.all([...this.pool.values()].map((p) => p.then((c) => c.close()).catch(() => { })));
|
|
70
192
|
}
|
|
71
193
|
};
|
|
72
194
|
exports.CrewxPoolService = CrewxPoolService;
|
|
73
195
|
exports.CrewxPoolService = CrewxPoolService = __decorate([
|
|
74
|
-
(0, common_1.Injectable)()
|
|
196
|
+
(0, common_1.Injectable)(),
|
|
197
|
+
__param(0, (0, common_1.Optional)()),
|
|
198
|
+
__metadata("design:paramtypes", [workspace_event_bus_js_1.WorkspaceEventBus])
|
|
75
199
|
], CrewxPoolService);
|
|
@@ -10,6 +10,7 @@ exports.CrewxModule = void 0;
|
|
|
10
10
|
const common_1 = require("@nestjs/common");
|
|
11
11
|
const sdk_1 = require("@crewx/sdk");
|
|
12
12
|
const crewx_pool_service_js_1 = require("./crewx-pool.service.js");
|
|
13
|
+
const events_module_js_1 = require("../domain/events/events.module.js");
|
|
13
14
|
/**
|
|
14
15
|
* CrewxModule — NestJS DI providers for the Crewx facade.
|
|
15
16
|
*
|
|
@@ -25,12 +26,17 @@ const crewx_pool_service_js_1 = require("./crewx-pool.service.js");
|
|
|
25
26
|
*
|
|
26
27
|
* Lifecycle: CrewxPoolService implements OnApplicationShutdown and closes
|
|
27
28
|
* all pooled instances automatically. Do NOT call crewx.close() from call sites.
|
|
29
|
+
*
|
|
30
|
+
* EventsModule is imported here so WorkspaceEventBus can be injected into
|
|
31
|
+
* CrewxPoolService for SDK-event → SSE fan-out. EventsModule does NOT import
|
|
32
|
+
* CrewxModule, avoiding a circular dependency.
|
|
28
33
|
*/
|
|
29
34
|
let CrewxModule = class CrewxModule {
|
|
30
35
|
};
|
|
31
36
|
exports.CrewxModule = CrewxModule;
|
|
32
37
|
exports.CrewxModule = CrewxModule = __decorate([
|
|
33
38
|
(0, common_1.Module)({
|
|
39
|
+
imports: [events_module_js_1.EventsModule],
|
|
34
40
|
providers: [
|
|
35
41
|
crewx_pool_service_js_1.CrewxPoolService,
|
|
36
42
|
{
|