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,77 @@
|
|
|
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.ChromexNegotiateController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const base_auth_guard_js_1 = require("../auth/guards/base-auth.guard.js");
|
|
18
|
+
const chromex_negotiate_dto_js_1 = require("./chromex-negotiate.dto.js");
|
|
19
|
+
const chromex_negotiate_service_js_1 = require("./chromex-negotiate.service.js");
|
|
20
|
+
let ChromexNegotiateController = class ChromexNegotiateController {
|
|
21
|
+
negotiateService;
|
|
22
|
+
constructor(negotiateService) {
|
|
23
|
+
this.negotiateService = negotiateService;
|
|
24
|
+
}
|
|
25
|
+
async negotiate(dto) {
|
|
26
|
+
return this.negotiateService.negotiate(dto);
|
|
27
|
+
}
|
|
28
|
+
async approve(dto) {
|
|
29
|
+
return await this.negotiateService.approve(dto.requestId, {
|
|
30
|
+
expiresIn: dto.expiresIn,
|
|
31
|
+
scope: dto.scope,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
deny(dto) {
|
|
35
|
+
this.negotiateService.deny(dto.requestId, dto.reason);
|
|
36
|
+
}
|
|
37
|
+
async revokeSession(dto) {
|
|
38
|
+
await this.negotiateService.revokeSession(dto.tokenId);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.ChromexNegotiateController = ChromexNegotiateController;
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, common_1.Post)('negotiate'),
|
|
44
|
+
__param(0, (0, common_1.Body)()),
|
|
45
|
+
__metadata("design:type", Function),
|
|
46
|
+
__metadata("design:paramtypes", [chromex_negotiate_dto_js_1.NegotiateRequestDto]),
|
|
47
|
+
__metadata("design:returntype", Promise)
|
|
48
|
+
], ChromexNegotiateController.prototype, "negotiate", null);
|
|
49
|
+
__decorate([
|
|
50
|
+
(0, common_1.Post)('approve'),
|
|
51
|
+
(0, common_1.HttpCode)(200),
|
|
52
|
+
__param(0, (0, common_1.Body)()),
|
|
53
|
+
__metadata("design:type", Function),
|
|
54
|
+
__metadata("design:paramtypes", [chromex_negotiate_dto_js_1.ApproveRequestDto]),
|
|
55
|
+
__metadata("design:returntype", Promise)
|
|
56
|
+
], ChromexNegotiateController.prototype, "approve", null);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, common_1.Post)('deny'),
|
|
59
|
+
(0, common_1.HttpCode)(200),
|
|
60
|
+
__param(0, (0, common_1.Body)()),
|
|
61
|
+
__metadata("design:type", Function),
|
|
62
|
+
__metadata("design:paramtypes", [chromex_negotiate_dto_js_1.DenyRequestDto]),
|
|
63
|
+
__metadata("design:returntype", void 0)
|
|
64
|
+
], ChromexNegotiateController.prototype, "deny", null);
|
|
65
|
+
__decorate([
|
|
66
|
+
(0, common_1.Delete)('session'),
|
|
67
|
+
(0, common_1.HttpCode)(200),
|
|
68
|
+
__param(0, (0, common_1.Body)()),
|
|
69
|
+
__metadata("design:type", Function),
|
|
70
|
+
__metadata("design:paramtypes", [chromex_negotiate_dto_js_1.RevokeSessionDto]),
|
|
71
|
+
__metadata("design:returntype", Promise)
|
|
72
|
+
], ChromexNegotiateController.prototype, "revokeSession", null);
|
|
73
|
+
exports.ChromexNegotiateController = ChromexNegotiateController = __decorate([
|
|
74
|
+
(0, common_1.Controller)('api/chromex'),
|
|
75
|
+
(0, common_1.UseGuards)(base_auth_guard_js_1.BaseAuthGuard),
|
|
76
|
+
__metadata("design:paramtypes", [chromex_negotiate_service_js_1.ChromexNegotiateService])
|
|
77
|
+
], ChromexNegotiateController);
|
|
@@ -0,0 +1,95 @@
|
|
|
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.ChromexTokenResponseDto = exports.RevokeSessionDto = exports.DenyRequestDto = exports.ApproveRequestDto = exports.NegotiateRequestDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class NegotiateRequestDto {
|
|
15
|
+
agentId;
|
|
16
|
+
scope;
|
|
17
|
+
expiresIn;
|
|
18
|
+
tokenName;
|
|
19
|
+
}
|
|
20
|
+
exports.NegotiateRequestDto = NegotiateRequestDto;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_validator_1.IsString)(),
|
|
23
|
+
(0, class_validator_1.MinLength)(1),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], NegotiateRequestDto.prototype, "agentId", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, class_validator_1.IsString)(),
|
|
28
|
+
(0, class_validator_1.MinLength)(1),
|
|
29
|
+
__metadata("design:type", String)
|
|
30
|
+
], NegotiateRequestDto.prototype, "scope", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_validator_1.IsOptional)(),
|
|
33
|
+
(0, class_validator_1.IsIn)(['30d', '90d', '1y']),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], NegotiateRequestDto.prototype, "expiresIn", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, class_validator_1.IsOptional)(),
|
|
38
|
+
(0, class_validator_1.IsString)(),
|
|
39
|
+
__metadata("design:type", String)
|
|
40
|
+
], NegotiateRequestDto.prototype, "tokenName", void 0);
|
|
41
|
+
class ApproveRequestDto {
|
|
42
|
+
requestId;
|
|
43
|
+
expiresIn;
|
|
44
|
+
scope;
|
|
45
|
+
}
|
|
46
|
+
exports.ApproveRequestDto = ApproveRequestDto;
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, class_validator_1.IsString)(),
|
|
49
|
+
(0, class_validator_1.MinLength)(1),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], ApproveRequestDto.prototype, "requestId", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
(0, class_validator_1.IsOptional)(),
|
|
54
|
+
(0, class_validator_1.IsIn)(['30d', '90d', '1y']),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], ApproveRequestDto.prototype, "expiresIn", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, class_validator_1.IsOptional)(),
|
|
59
|
+
(0, class_validator_1.IsString)(),
|
|
60
|
+
__metadata("design:type", String)
|
|
61
|
+
], ApproveRequestDto.prototype, "scope", void 0);
|
|
62
|
+
class DenyRequestDto {
|
|
63
|
+
requestId;
|
|
64
|
+
reason;
|
|
65
|
+
}
|
|
66
|
+
exports.DenyRequestDto = DenyRequestDto;
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, class_validator_1.IsString)(),
|
|
69
|
+
(0, class_validator_1.MinLength)(1),
|
|
70
|
+
__metadata("design:type", String)
|
|
71
|
+
], DenyRequestDto.prototype, "requestId", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, class_validator_1.IsOptional)(),
|
|
74
|
+
(0, class_validator_1.IsString)(),
|
|
75
|
+
__metadata("design:type", String)
|
|
76
|
+
], DenyRequestDto.prototype, "reason", void 0);
|
|
77
|
+
class RevokeSessionDto {
|
|
78
|
+
tokenId;
|
|
79
|
+
}
|
|
80
|
+
exports.RevokeSessionDto = RevokeSessionDto;
|
|
81
|
+
__decorate([
|
|
82
|
+
(0, class_validator_1.IsString)(),
|
|
83
|
+
(0, class_validator_1.MinLength)(1),
|
|
84
|
+
__metadata("design:type", String)
|
|
85
|
+
], RevokeSessionDto.prototype, "tokenId", void 0);
|
|
86
|
+
class ChromexTokenResponseDto {
|
|
87
|
+
token; // crewx_<JWT> — Bearer로 사용
|
|
88
|
+
tokenId; // tok_... (revoke용)
|
|
89
|
+
jti; // JWT ID
|
|
90
|
+
expiresAt; // ISO 8601
|
|
91
|
+
scope;
|
|
92
|
+
agentId;
|
|
93
|
+
mcpSessionId;
|
|
94
|
+
}
|
|
95
|
+
exports.ChromexTokenResponseDto = ChromexTokenResponseDto;
|
|
@@ -0,0 +1,249 @@
|
|
|
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.ChromexNegotiateService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const path_1 = require("path");
|
|
15
|
+
const os_1 = require("os");
|
|
16
|
+
const nanoid_1 = require("nanoid");
|
|
17
|
+
const token_service_js_1 = require("../token/token.service.js");
|
|
18
|
+
const browser_session_store_js_1 = require("./browser-session.store.js");
|
|
19
|
+
const mcp_constants_js_1 = require("./mcp.constants.js");
|
|
20
|
+
const NEGOTIATE_TIMEOUT_MS = 60_000;
|
|
21
|
+
const PROCESSED_TTL_MS = 5 * 60_000;
|
|
22
|
+
let ChromexNegotiateService = class ChromexNegotiateService {
|
|
23
|
+
tokenService;
|
|
24
|
+
browserSessions;
|
|
25
|
+
pendingRequests = new Map();
|
|
26
|
+
pendingAgentIds = new Set();
|
|
27
|
+
processedRequests = new Set();
|
|
28
|
+
db;
|
|
29
|
+
constructor(tokenService, browserSessions) {
|
|
30
|
+
this.tokenService = tokenService;
|
|
31
|
+
this.browserSessions = browserSessions;
|
|
32
|
+
}
|
|
33
|
+
onModuleInit() {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
|
+
const Database = require('better-sqlite3');
|
|
36
|
+
const dbPath = process.env.CREWX_DB ?? (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'crewx.db');
|
|
37
|
+
this.db = new Database(dbPath);
|
|
38
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
39
|
+
this.db.exec('PRAGMA busy_timeout = 5000');
|
|
40
|
+
this.db.exec(`
|
|
41
|
+
CREATE TABLE IF NOT EXISTS chromex_audit_log (
|
|
42
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
|
44
|
+
agent_id TEXT NOT NULL,
|
|
45
|
+
action TEXT NOT NULL,
|
|
46
|
+
jti TEXT,
|
|
47
|
+
scope TEXT,
|
|
48
|
+
tab_url TEXT,
|
|
49
|
+
tool_name TEXT,
|
|
50
|
+
result TEXT,
|
|
51
|
+
detail TEXT
|
|
52
|
+
)
|
|
53
|
+
`);
|
|
54
|
+
}
|
|
55
|
+
onModuleDestroy() {
|
|
56
|
+
try {
|
|
57
|
+
this.db?.close();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore close errors during shutdown
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async negotiate(dto) {
|
|
64
|
+
if (this.pendingAgentIds.has(dto.agentId)) {
|
|
65
|
+
throw new common_1.ConflictException('Negotiate already pending for this agent');
|
|
66
|
+
}
|
|
67
|
+
const browserSession = this.browserSessions.getLatestBrowserSession();
|
|
68
|
+
if (!browserSession) {
|
|
69
|
+
throw new common_1.HttpException({ reason: 'no_browser_connected' }, 503);
|
|
70
|
+
}
|
|
71
|
+
const requestId = 'req_' + (0, nanoid_1.nanoid)(21);
|
|
72
|
+
const expiresIn = dto.expiresIn ?? '90d';
|
|
73
|
+
const negotiateExpiresAt = new Date(Date.now() + NEGOTIATE_TIMEOUT_MS).toISOString();
|
|
74
|
+
const tokenName = dto.tokenName ?? `${dto.agentId}-${new Date().toISOString().slice(0, 7)}`;
|
|
75
|
+
const payload = {
|
|
76
|
+
jsonrpc: '2.0',
|
|
77
|
+
method: mcp_constants_js_1.CHROMEX_EVENTS.NEGOTIATE_REQUEST,
|
|
78
|
+
params: {
|
|
79
|
+
requestId,
|
|
80
|
+
agentId: dto.agentId,
|
|
81
|
+
scope: dto.scope,
|
|
82
|
+
expiresIn,
|
|
83
|
+
mcpSessionId: browserSession.id,
|
|
84
|
+
tokenName,
|
|
85
|
+
negotiateExpiresAt,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
this.pendingAgentIds.add(dto.agentId);
|
|
89
|
+
// log when the negotiate request is registered
|
|
90
|
+
this.auditLog({
|
|
91
|
+
agentId: dto.agentId,
|
|
92
|
+
action: 'negotiate',
|
|
93
|
+
scope: dto.scope,
|
|
94
|
+
result: 'success',
|
|
95
|
+
}).catch(() => { });
|
|
96
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
97
|
+
let unregisterDisconnect = () => { };
|
|
98
|
+
const resolve = (value) => {
|
|
99
|
+
clearTimeout(timeout);
|
|
100
|
+
unregisterDisconnect();
|
|
101
|
+
resolvePromise(value);
|
|
102
|
+
};
|
|
103
|
+
const reject = (reason) => {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
unregisterDisconnect();
|
|
106
|
+
rejectPromise(reason);
|
|
107
|
+
};
|
|
108
|
+
const timeout = setTimeout(() => {
|
|
109
|
+
if (this.pendingRequests.has(requestId)) {
|
|
110
|
+
const entry = this.pendingRequests.get(requestId);
|
|
111
|
+
this.pendingRequests.delete(requestId);
|
|
112
|
+
this.pendingAgentIds.delete(dto.agentId);
|
|
113
|
+
this.processedRequests.add(requestId);
|
|
114
|
+
this.scheduleProcessedCleanup(requestId);
|
|
115
|
+
this.auditLog({
|
|
116
|
+
agentId: dto.agentId,
|
|
117
|
+
action: 'timeout',
|
|
118
|
+
scope: entry.scope,
|
|
119
|
+
result: 'timeout',
|
|
120
|
+
}).catch(() => { });
|
|
121
|
+
reject(new common_1.HttpException({ reason: 'timeout' }, 408));
|
|
122
|
+
}
|
|
123
|
+
}, NEGOTIATE_TIMEOUT_MS);
|
|
124
|
+
this.pendingRequests.set(requestId, {
|
|
125
|
+
agentId: dto.agentId,
|
|
126
|
+
scope: dto.scope,
|
|
127
|
+
expiresIn,
|
|
128
|
+
mcpSessionId: browserSession.id,
|
|
129
|
+
tokenName: dto.tokenName,
|
|
130
|
+
resolve,
|
|
131
|
+
reject,
|
|
132
|
+
timeout,
|
|
133
|
+
});
|
|
134
|
+
unregisterDisconnect = this.browserSessions.onSseDisconnect(browserSession.id, () => {
|
|
135
|
+
if (this.pendingRequests.has(requestId)) {
|
|
136
|
+
this.pendingRequests.delete(requestId);
|
|
137
|
+
this.pendingAgentIds.delete(dto.agentId);
|
|
138
|
+
this.processedRequests.add(requestId);
|
|
139
|
+
this.scheduleProcessedCleanup(requestId);
|
|
140
|
+
reject(new common_1.HttpException({ reason: 'no_browser_connected' }, 503));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const pushed = this.browserSessions.pushNotification(browserSession.id, payload);
|
|
144
|
+
if (!pushed) {
|
|
145
|
+
this.pendingRequests.delete(requestId);
|
|
146
|
+
this.pendingAgentIds.delete(dto.agentId);
|
|
147
|
+
this.processedRequests.add(requestId);
|
|
148
|
+
this.scheduleProcessedCleanup(requestId);
|
|
149
|
+
reject(new common_1.HttpException({ reason: 'no_browser_connected' }, 503));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async approve(requestId, overrides) {
|
|
154
|
+
if (this.processedRequests.has(requestId))
|
|
155
|
+
return undefined;
|
|
156
|
+
const entry = this.pendingRequests.get(requestId);
|
|
157
|
+
if (!entry)
|
|
158
|
+
throw new common_1.NotFoundException('Negotiate request not found');
|
|
159
|
+
this.pendingRequests.delete(requestId);
|
|
160
|
+
this.pendingAgentIds.delete(entry.agentId);
|
|
161
|
+
this.processedRequests.add(requestId);
|
|
162
|
+
this.scheduleProcessedCleanup(requestId);
|
|
163
|
+
clearTimeout(entry.timeout);
|
|
164
|
+
try {
|
|
165
|
+
const expiresIn = overrides?.expiresIn ?? entry.expiresIn;
|
|
166
|
+
const scope = overrides?.scope ?? entry.scope;
|
|
167
|
+
const { token, meta } = await this.tokenService.createToken({
|
|
168
|
+
name: entry.tokenName ?? `${entry.agentId}-${new Date().toISOString().slice(0, 10)}`,
|
|
169
|
+
expiresIn,
|
|
170
|
+
agentScope: entry.agentId,
|
|
171
|
+
browserScope: scope,
|
|
172
|
+
}, 'negotiate');
|
|
173
|
+
this.auditLog({
|
|
174
|
+
agentId: entry.agentId,
|
|
175
|
+
action: 'approve',
|
|
176
|
+
jti: meta.jti,
|
|
177
|
+
scope,
|
|
178
|
+
result: 'success',
|
|
179
|
+
}).catch(() => { });
|
|
180
|
+
const response = {
|
|
181
|
+
token,
|
|
182
|
+
tokenId: meta.id,
|
|
183
|
+
jti: meta.jti,
|
|
184
|
+
expiresAt: meta.expiresAt,
|
|
185
|
+
scope,
|
|
186
|
+
agentId: entry.agentId,
|
|
187
|
+
mcpSessionId: entry.mcpSessionId,
|
|
188
|
+
};
|
|
189
|
+
entry.resolve(response);
|
|
190
|
+
return response;
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
entry.reject(err);
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
deny(requestId, reason = 'user_denied') {
|
|
198
|
+
if (this.processedRequests.has(requestId))
|
|
199
|
+
return;
|
|
200
|
+
const entry = this.pendingRequests.get(requestId);
|
|
201
|
+
if (!entry)
|
|
202
|
+
throw new common_1.NotFoundException('Negotiate request not found');
|
|
203
|
+
this.pendingRequests.delete(requestId);
|
|
204
|
+
this.pendingAgentIds.delete(entry.agentId);
|
|
205
|
+
this.processedRequests.add(requestId);
|
|
206
|
+
this.scheduleProcessedCleanup(requestId);
|
|
207
|
+
clearTimeout(entry.timeout);
|
|
208
|
+
this.auditLog({
|
|
209
|
+
agentId: entry.agentId,
|
|
210
|
+
action: 'deny',
|
|
211
|
+
scope: entry.scope,
|
|
212
|
+
result: 'denied',
|
|
213
|
+
detail: { reason },
|
|
214
|
+
}).catch(() => { });
|
|
215
|
+
entry.reject(new common_1.HttpException({ reason }, 403));
|
|
216
|
+
}
|
|
217
|
+
async revokeSession(tokenId) {
|
|
218
|
+
await this.tokenService.revokeToken(tokenId);
|
|
219
|
+
}
|
|
220
|
+
getPendingRequests() {
|
|
221
|
+
return Array.from(this.pendingRequests.entries()).map(([requestId, entry]) => ({
|
|
222
|
+
requestId,
|
|
223
|
+
agentId: entry.agentId,
|
|
224
|
+
scope: entry.scope,
|
|
225
|
+
expiresIn: entry.expiresIn,
|
|
226
|
+
tokenName: entry.tokenName,
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
logAudit(params) {
|
|
230
|
+
void this.auditLog(params);
|
|
231
|
+
}
|
|
232
|
+
async auditLog(params) {
|
|
233
|
+
if (!this.db)
|
|
234
|
+
return;
|
|
235
|
+
this.db
|
|
236
|
+
.prepare(`INSERT INTO chromex_audit_log (agent_id, action, jti, scope, tab_url, tool_name, result, detail)
|
|
237
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
238
|
+
.run(params.agentId, params.action, params.jti ?? null, params.scope ?? null, params.tabUrl ?? null, params.toolName ?? null, params.result, params.detail ? JSON.stringify(params.detail) : null);
|
|
239
|
+
}
|
|
240
|
+
scheduleProcessedCleanup(requestId) {
|
|
241
|
+
setTimeout(() => this.processedRequests.delete(requestId), PROCESSED_TTL_MS);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
exports.ChromexNegotiateService = ChromexNegotiateService;
|
|
245
|
+
exports.ChromexNegotiateService = ChromexNegotiateService = __decorate([
|
|
246
|
+
(0, common_1.Injectable)(),
|
|
247
|
+
__metadata("design:paramtypes", [token_service_js_1.TokenService,
|
|
248
|
+
browser_session_store_js_1.BrowserSessionStore])
|
|
249
|
+
], ChromexNegotiateService);
|
|
@@ -14,41 +14,54 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.McpAuthGuard = void 0;
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
|
+
const token_service_js_1 = require("../token/token.service.js");
|
|
17
18
|
const mcp_constants_js_1 = require("./mcp.constants.js");
|
|
19
|
+
const ip_utils_js_1 = require("../auth/utils/ip.utils.js");
|
|
18
20
|
let McpAuthGuard = class McpAuthGuard {
|
|
21
|
+
tokenService;
|
|
19
22
|
tokenInfo;
|
|
20
|
-
constructor(tokenInfo) {
|
|
23
|
+
constructor(tokenService, tokenInfo) {
|
|
24
|
+
this.tokenService = tokenService;
|
|
21
25
|
this.tokenInfo = tokenInfo;
|
|
22
26
|
}
|
|
23
|
-
canActivate(context) {
|
|
27
|
+
async canActivate(context) {
|
|
24
28
|
const request = context.switchToHttp().getRequest();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const rawToken = this.extractToken(request);
|
|
30
|
+
if (rawToken) {
|
|
31
|
+
// DB JWT token validation
|
|
32
|
+
const claims = await this.tokenService.validateToken(rawToken);
|
|
33
|
+
if (claims) {
|
|
34
|
+
request.tokenClaims = claims;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// Legacy fallback: existing Chrome X installs may still store the generated
|
|
38
|
+
// MCP bearer token from server startup instead of a DB-backed crewx_ token.
|
|
39
|
+
if (rawToken === this.tokenInfo.token)
|
|
40
|
+
return true;
|
|
30
41
|
}
|
|
31
|
-
if (
|
|
32
|
-
|
|
42
|
+
if (this.isLocalRequest(request))
|
|
43
|
+
return true;
|
|
44
|
+
if (!rawToken) {
|
|
45
|
+
throw new common_1.UnauthorizedException('Bearer token required');
|
|
33
46
|
}
|
|
34
|
-
|
|
47
|
+
throw new common_1.UnauthorizedException('Invalid token');
|
|
35
48
|
}
|
|
36
|
-
|
|
49
|
+
isLocalRequest(request) {
|
|
50
|
+
const ip = request.socket?.remoteAddress;
|
|
51
|
+
if (!(0, ip_utils_js_1.isLoopback)(ip))
|
|
52
|
+
return false;
|
|
53
|
+
const host = (request.headers['host'] ?? '').split(':')[0];
|
|
54
|
+
return host === 'localhost' || host === '127.0.0.1' || host === '[::1]';
|
|
55
|
+
}
|
|
56
|
+
extractToken(request) {
|
|
37
57
|
const authHeader = request.headers['authorization'] || request.headers['Authorization'];
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const prefix = 'Bearer ';
|
|
42
|
-
if (!authHeader.startsWith(prefix)) {
|
|
43
|
-
return null;
|
|
58
|
+
if (typeof authHeader === 'string' && authHeader.startsWith('Bearer ')) {
|
|
59
|
+
const token = authHeader.slice(7).trim();
|
|
60
|
+
return token || null;
|
|
44
61
|
}
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
extractTokenFromQuery(request) {
|
|
49
|
-
const token = request.query['token'];
|
|
50
|
-
if (typeof token === 'string' && token.length > 0) {
|
|
51
|
-
return token;
|
|
62
|
+
const queryToken = request.query['token'];
|
|
63
|
+
if (typeof queryToken === 'string' && queryToken.length > 0) {
|
|
64
|
+
return queryToken;
|
|
52
65
|
}
|
|
53
66
|
return null;
|
|
54
67
|
}
|
|
@@ -56,6 +69,7 @@ let McpAuthGuard = class McpAuthGuard {
|
|
|
56
69
|
exports.McpAuthGuard = McpAuthGuard;
|
|
57
70
|
exports.McpAuthGuard = McpAuthGuard = __decorate([
|
|
58
71
|
(0, common_1.Injectable)(),
|
|
59
|
-
__param(0, (0, common_1.Inject)(
|
|
60
|
-
|
|
72
|
+
__param(0, (0, common_1.Inject)(token_service_js_1.TokenService)),
|
|
73
|
+
__param(1, (0, common_1.Inject)(mcp_constants_js_1.MCP_TOKEN)),
|
|
74
|
+
__metadata("design:paramtypes", [token_service_js_1.TokenService, Object])
|
|
61
75
|
], McpAuthGuard);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MCP_TOKEN = void 0;
|
|
3
|
+
exports.CHROMEX_EVENTS = exports.MCP_TOKEN = void 0;
|
|
4
4
|
exports.MCP_TOKEN = 'MCP_TOKEN';
|
|
5
|
+
exports.CHROMEX_EVENTS = {
|
|
6
|
+
NEGOTIATE_REQUEST: 'chromex/negotiate',
|
|
7
|
+
};
|
|
@@ -25,7 +25,7 @@ let McpController = McpController_1 = class McpController {
|
|
|
25
25
|
constructor(mcpService) {
|
|
26
26
|
this.mcpService = mcpService;
|
|
27
27
|
}
|
|
28
|
-
async handleMcp(body, sessionId, clientType, res) {
|
|
28
|
+
async handleMcp(body, sessionId, clientType, req, res) {
|
|
29
29
|
this.logger.debug(`MCP request [session=${sessionId ?? 'none'}]: ${JSON.stringify(body)}`);
|
|
30
30
|
// JSON-RPC response from extension (has result or error, no method)
|
|
31
31
|
// Must be checked BEFORE validateRequest — responses have no method field
|
|
@@ -45,7 +45,7 @@ let McpController = McpController_1 = class McpController {
|
|
|
45
45
|
res.status(200).json(validationError);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
const result = await this.mcpService.handleRequest(body, sessionId, clientType);
|
|
48
|
+
const result = await this.mcpService.handleRequest(body, sessionId, clientType, req);
|
|
49
49
|
// Notifications return null
|
|
50
50
|
if (result === null) {
|
|
51
51
|
this.logger.debug(`Notification processed: ${body.method}`);
|
|
@@ -97,9 +97,10 @@ __decorate([
|
|
|
97
97
|
__param(0, (0, common_1.Body)()),
|
|
98
98
|
__param(1, (0, common_1.Headers)(MCP_SESSION_HEADER)),
|
|
99
99
|
__param(2, (0, common_1.Headers)('x-client-type')),
|
|
100
|
-
__param(3, (0, common_1.
|
|
100
|
+
__param(3, (0, common_1.Req)()),
|
|
101
|
+
__param(4, (0, common_1.Res)()),
|
|
101
102
|
__metadata("design:type", Function),
|
|
102
|
-
__metadata("design:paramtypes", [Object, Object, Object, Object]),
|
|
103
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object]),
|
|
103
104
|
__metadata("design:returntype", Promise)
|
|
104
105
|
], McpController.prototype, "handleMcp", null);
|
|
105
106
|
__decorate([
|
|
@@ -17,15 +17,19 @@ const tool_router_service_js_1 = require("./tool-router.service.js");
|
|
|
17
17
|
const mcp_constants_js_1 = require("./mcp.constants.js");
|
|
18
18
|
const crewx_module_js_1 = require("../../modules/crewx.module.js");
|
|
19
19
|
const repository_1 = require("@crewx/sdk/repository");
|
|
20
|
+
const token_module_js_1 = require("../token/token.module.js");
|
|
21
|
+
const chromex_negotiate_controller_js_1 = require("./chromex-negotiate.controller.js");
|
|
22
|
+
const chromex_negotiate_service_js_1 = require("./chromex-negotiate.service.js");
|
|
20
23
|
let McpModule = class McpModule {
|
|
21
24
|
};
|
|
22
25
|
exports.McpModule = McpModule;
|
|
23
26
|
exports.McpModule = McpModule = __decorate([
|
|
24
27
|
(0, common_1.Module)({
|
|
25
|
-
imports: [crewx_module_js_1.CrewxModule],
|
|
26
|
-
controllers: [mcp_controller_js_1.McpController],
|
|
28
|
+
imports: [crewx_module_js_1.CrewxModule, token_module_js_1.TokenModule],
|
|
29
|
+
controllers: [mcp_controller_js_1.McpController, chromex_negotiate_controller_js_1.ChromexNegotiateController],
|
|
27
30
|
providers: [
|
|
28
31
|
mcp_service_js_1.McpService,
|
|
32
|
+
chromex_negotiate_service_js_1.ChromexNegotiateService,
|
|
29
33
|
browser_session_store_js_1.BrowserSessionStore,
|
|
30
34
|
tool_router_service_js_1.ToolRouterService,
|
|
31
35
|
{
|