crewx 0.9.0-rc.2 → 0.9.0-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/dist/assets/MarketPage-DDPL2d7x.js +36 -0
- package/dist/assets/{PromptTab-hdXDbbT6.js → PromptTab-UC3K4qx2.js} +3 -3
- package/dist/assets/{arc--CBVVcyH.js → arc-DdP3-fUR.js} +1 -1
- package/dist/assets/{architectureDiagram-UYN6MBPD-DUiYwqJn.js → architectureDiagram-UYN6MBPD-D16VON1i.js} +1 -1
- package/dist/assets/{blockDiagram-ZHA2E4KO-DV86vAgG.js → blockDiagram-ZHA2E4KO-qs1dIRkl.js} +4 -4
- package/dist/assets/{c4Diagram-6F5ED5ID-CvWE7Cx_.js → c4Diagram-6F5ED5ID-BeDy8akQ.js} +1 -1
- package/dist/assets/channel-DtrxBSkc.js +1 -0
- package/dist/assets/{chunk-5HRBRIJM-DAxM5Ul_.js → chunk-5HRBRIJM-BC1zIb11.js} +1 -1
- package/dist/assets/{chunk-7U56Z5CX-BuYaEj8L.js → chunk-7U56Z5CX-BuGil3QD.js} +1 -1
- package/dist/assets/{chunk-ASOPGD6M-2Kx0e45C.js → chunk-ASOPGD6M-DmKdDtBQ.js} +1 -1
- package/dist/assets/{chunk-KFBOBJHC-CR4QS6Rt.js → chunk-KFBOBJHC-D3BvHLPa.js} +1 -1
- package/dist/assets/{chunk-T2TOU4HS-CffcwNHx.js → chunk-T2TOU4HS-B5MoUp-N.js} +1 -1
- package/dist/assets/{chunk-TMUBEWPD-BGpYtekN.js → chunk-TMUBEWPD-AvWe_lsV.js} +1 -1
- package/dist/assets/classDiagram-LNE6IOMH-BMT5B_JB.js +1 -0
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-BMT5B_JB.js +1 -0
- package/dist/assets/{dagre-4EVJKHTY-yBWukkrE.js → dagre-4EVJKHTY-B6DewFyM.js} +1 -1
- package/dist/assets/{diagram-QW4FP2JN-CIr1PLq3.js → diagram-QW4FP2JN-BTsQ8JSd.js} +1 -1
- package/dist/assets/{erDiagram-6RL3IURR-xG7SygyU.js → erDiagram-6RL3IURR-BM5nvgVR.js} +1 -1
- package/dist/assets/{flowDiagram-7ASYPVHJ-CKalfOqp.js → flowDiagram-7ASYPVHJ-D1UCCuOG.js} +1 -1
- package/dist/assets/{ganttDiagram-NTVNEXSI-Bnx7zrX4.js → ganttDiagram-NTVNEXSI-CNIGs7Cn.js} +1 -1
- package/dist/assets/{gitGraph-YCYPL57B-TQlF0fiA.js → gitGraph-YCYPL57B-DSLjx1yQ.js} +1 -1
- package/dist/assets/{gitGraphDiagram-NRZ2UAAF-DxlaFkwO.js → gitGraphDiagram-NRZ2UAAF-B11zX1Fa.js} +1 -1
- package/dist/assets/{infoDiagram-A4XQUW5V-B1-arkqa.js → infoDiagram-A4XQUW5V-DkjvODYl.js} +1 -1
- package/dist/assets/{journeyDiagram-G5WM74LC-Cj439GUX.js → journeyDiagram-G5WM74LC-BkCQPMn6.js} +1 -1
- package/dist/assets/{kanban-definition-QRCXZQQD-BvrIwEIc.js → kanban-definition-QRCXZQQD-_OX7pNfJ.js} +1 -1
- package/dist/assets/{linear-BKYNK0ri.js → linear-BYjwVAR6.js} +1 -1
- package/dist/assets/main-CZdKLdUl.css +10 -0
- package/dist/assets/main-CkkZNCy-.js +1561 -0
- package/dist/assets/{mindmap-definition-GWI6TPTV-BExfJY0G.js → mindmap-definition-GWI6TPTV-BzYpSnv8.js} +1 -1
- package/dist/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
- package/dist/assets/{pieDiagram-YF2LJOPJ-RPWpvrC8.js → pieDiagram-YF2LJOPJ-CEFg6ao0.js} +1 -1
- package/dist/assets/{quadrantDiagram-OS5C2QUG-DmbBzk5Q.js → quadrantDiagram-OS5C2QUG-_5w2x3VV.js} +1 -1
- package/dist/assets/{requirementDiagram-MIRIMTAZ-DFQuFXq3.js → requirementDiagram-MIRIMTAZ-B_VQYReZ.js} +1 -1
- package/dist/assets/{sankeyDiagram-Y46BX6SQ-p_bw56lJ.js → sankeyDiagram-Y46BX6SQ-CgmOlLhh.js} +1 -1
- package/dist/assets/{sequenceDiagram-G6AWOVSC-DTbBkLeD.js → sequenceDiagram-G6AWOVSC-MGVt0Ai2.js} +1 -1
- package/dist/assets/{stateDiagram-MAYHULR4-DBvC6UcF.js → stateDiagram-MAYHULR4-DaZjgTu-.js} +1 -1
- package/dist/assets/stateDiagram-v2-4JROLMXI-DN1wGYkV.js +1 -0
- package/dist/assets/{timeline-definition-U7ZMHBDA-B6utvIg1.js → timeline-definition-U7ZMHBDA-CusetmK6.js} +1 -1
- package/dist/assets/transformers.web-BN8Ug43_.js +45 -0
- package/dist/assets/{xychartDiagram-6QU3TZC5-BHG0s1Ye.js → xychartDiagram-6QU3TZC5-CTndf-if.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/app.module.js +4 -0
- package/dist-server/bootstrap/codex-writable-roots.js +88 -0
- package/dist-server/bootstrap/crewx-server.js +10 -2
- package/dist-server/common/decorator/skip-request-log.decorator.js +8 -0
- package/dist-server/common/interceptor/http-logging.interceptor.js +17 -4
- package/dist-server/common/task-log/stdout-json-fallback.js +200 -0
- package/dist-server/domain/agent/agent.controller.js +14 -0
- package/dist-server/domain/agent/agent.service.js +246 -18
- package/dist-server/domain/agent/dto/create-agent.dto.js +12 -0
- package/dist-server/domain/agent/dto/remote-provider.dto.js +82 -0
- package/dist-server/domain/agent/dto/reorder-agents.dto.js +51 -0
- package/dist-server/domain/agent/dto/update-agent.dto.js +3 -1
- package/dist-server/domain/agent/provider.controller.js +12 -1
- package/dist-server/domain/doc/doc.controller.js +43 -9
- package/dist-server/domain/doc/doc.service.js +80 -6
- package/dist-server/domain/doc/image-constants.js +11 -1
- package/dist-server/domain/events/workspace-events.controller.js +2 -0
- package/dist-server/domain/message/dto/send-message.dto.js +49 -1
- package/dist-server/domain/message/message.controller.js +1 -1
- package/dist-server/domain/message/message.module.js +2 -1
- package/dist-server/domain/message/message.service.js +83 -13
- package/dist-server/domain/settings/settings.service.js +188 -1
- package/dist-server/domain/system/system.controller.js +41 -0
- package/dist-server/domain/system/system.module.js +19 -0
- package/dist-server/domain/task/task.constants.js +8 -1
- package/dist-server/domain/task/task.service.js +59 -67
- package/dist-server/domain/thread/dto/list-threads.dto.js +18 -0
- package/dist-server/domain/thread/overdrive.types.js +38 -0
- package/dist-server/domain/thread/thread.service.js +83 -70
- package/dist-server/domain/usage/usage.controller.js +18 -0
- package/dist-server/domain/usage/usage.module.js +1 -0
- package/dist-server/domain/usage/usage.service.js +246 -4
- package/dist-server/domain/usage-report/dto/usage-report.dto.js +2 -0
- package/dist-server/domain/usage-report/usage-report.controller.js +72 -0
- package/dist-server/domain/usage-report/usage-report.module.js +23 -0
- package/dist-server/domain/usage-report/usage-report.service.js +135 -0
- package/dist-server/domain/wi/wi.service.js +45 -3
- package/dist-server/main.js +1 -0
- package/dist-server/repository/repository.module.js +2 -0
- package/dist-server/repository/task.repository.js +56 -0
- package/dist-server/repository/thread.repository.js +13 -0
- package/dist-server/repository/usage-report.repository.js +27 -0
- package/package.json +9 -9
- package/packages/cli/dist/bootstrap/codex-writable-roots.d.ts +45 -0
- package/packages/cli/dist/bootstrap/codex-writable-roots.js +88 -0
- package/packages/cli/dist/bootstrap/crewx-cli.js +2 -0
- package/packages/cli/dist/commands/execute.js +10 -2
- package/packages/cli/dist/commands/parse-common-flags.d.ts +5 -1
- package/packages/cli/dist/commands/parse-common-flags.js +9 -2
- package/packages/cli/dist/commands/ps.js +51 -2
- package/packages/cli/dist/commands/query.js +10 -2
- package/packages/cli/dist/commands/restart.js +20 -6
- package/packages/cli/dist/commands/write-output.d.ts +3 -0
- package/packages/cli/dist/commands/write-output.js +24 -0
- package/packages/cli/dist/main.js +7 -0
- package/packages/cli/package.json +1 -1
- package/dist/assets/MarketPage-T1o_oIrw.js +0 -41
- package/dist/assets/channel-7q16D7P9.js +0 -1
- package/dist/assets/classDiagram-LNE6IOMH-C6qjk4uc.js +0 -1
- package/dist/assets/classDiagram-v2-MQ7JQ4JX-C6qjk4uc.js +0 -1
- package/dist/assets/main-C5mwv79r.css +0 -10
- package/dist/assets/main-DmvFaz-d.js +0 -1486
- package/dist/assets/stateDiagram-v2-4JROLMXI-CCMApx_v.js +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
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.UsageReportController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
18
|
+
const usage_report_service_js_1 = require("./usage-report.service.js");
|
|
19
|
+
const workspace_decorator_js_1 = require("../../common/decorators/workspace.decorator.js");
|
|
20
|
+
let UsageReportController = class UsageReportController {
|
|
21
|
+
usageReportService;
|
|
22
|
+
constructor(usageReportService) {
|
|
23
|
+
this.usageReportService = usageReportService;
|
|
24
|
+
}
|
|
25
|
+
// USR.PUBLISH: Publish (or re-publish/upsert) a usage report snapshot for the given month.
|
|
26
|
+
publish(month, ws) {
|
|
27
|
+
const data = this.usageReportService.publish(month, ws.id);
|
|
28
|
+
return { success: true, data };
|
|
29
|
+
}
|
|
30
|
+
// USR.GET: Fetch a previously published report. 404 if the month was never published.
|
|
31
|
+
get(month, ws) {
|
|
32
|
+
const data = this.usageReportService.get(month, ws.id);
|
|
33
|
+
return { success: true, data };
|
|
34
|
+
}
|
|
35
|
+
// USR.LIST: List publish history for the workspace.
|
|
36
|
+
list(ws) {
|
|
37
|
+
const data = this.usageReportService.list(ws.id);
|
|
38
|
+
return { success: true, data };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.UsageReportController = UsageReportController;
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, swagger_1.ApiOperation)({ operationId: 'USR.PUBLISH', summary: 'Publish a usage report snapshot for a month' }),
|
|
44
|
+
(0, common_1.Post)(':month/publish'),
|
|
45
|
+
__param(0, (0, common_1.Param)('month')),
|
|
46
|
+
__param(1, (0, workspace_decorator_js_1.Workspace)()),
|
|
47
|
+
__metadata("design:type", Function),
|
|
48
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
49
|
+
__metadata("design:returntype", void 0)
|
|
50
|
+
], UsageReportController.prototype, "publish", null);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, swagger_1.ApiOperation)({ operationId: 'USR.GET', summary: 'Get the published usage report for a month' }),
|
|
53
|
+
(0, common_1.Get)(':month'),
|
|
54
|
+
__param(0, (0, common_1.Param)('month')),
|
|
55
|
+
__param(1, (0, workspace_decorator_js_1.Workspace)()),
|
|
56
|
+
__metadata("design:type", Function),
|
|
57
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
58
|
+
__metadata("design:returntype", void 0)
|
|
59
|
+
], UsageReportController.prototype, "get", null);
|
|
60
|
+
__decorate([
|
|
61
|
+
(0, swagger_1.ApiOperation)({ operationId: 'USR.LIST', summary: 'List published usage report history' }),
|
|
62
|
+
(0, common_1.Get)(),
|
|
63
|
+
__param(0, (0, workspace_decorator_js_1.Workspace)()),
|
|
64
|
+
__metadata("design:type", Function),
|
|
65
|
+
__metadata("design:paramtypes", [Object]),
|
|
66
|
+
__metadata("design:returntype", void 0)
|
|
67
|
+
], UsageReportController.prototype, "list", null);
|
|
68
|
+
exports.UsageReportController = UsageReportController = __decorate([
|
|
69
|
+
(0, swagger_1.ApiTags)('usage-report'),
|
|
70
|
+
(0, common_1.Controller)('ws/:slug/usage-reports'),
|
|
71
|
+
__metadata("design:paramtypes", [usage_report_service_js_1.UsageReportService])
|
|
72
|
+
], UsageReportController);
|
|
@@ -0,0 +1,23 @@
|
|
|
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.UsageReportModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const usage_module_js_1 = require("../usage/usage.module.js");
|
|
12
|
+
const usage_report_controller_js_1 = require("./usage-report.controller.js");
|
|
13
|
+
const usage_report_service_js_1 = require("./usage-report.service.js");
|
|
14
|
+
let UsageReportModule = class UsageReportModule {
|
|
15
|
+
};
|
|
16
|
+
exports.UsageReportModule = UsageReportModule;
|
|
17
|
+
exports.UsageReportModule = UsageReportModule = __decorate([
|
|
18
|
+
(0, common_1.Module)({
|
|
19
|
+
imports: [usage_module_js_1.UsageModule],
|
|
20
|
+
controllers: [usage_report_controller_js_1.UsageReportController],
|
|
21
|
+
providers: [usage_report_service_js_1.UsageReportService],
|
|
22
|
+
})
|
|
23
|
+
], UsageReportModule);
|
|
@@ -0,0 +1,135 @@
|
|
|
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.UsageReportService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const nanoid_1 = require("nanoid");
|
|
15
|
+
const usage_service_js_1 = require("../usage/usage.service.js");
|
|
16
|
+
const usage_report_repository_js_1 = require("../../repository/usage-report.repository.js");
|
|
17
|
+
const nanoidBase62 = (0, nanoid_1.customAlphabet)('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 12);
|
|
18
|
+
const MONTH_RE = /^\d{4}-\d{2}$/;
|
|
19
|
+
// Tier thresholds — duplicated from src/ui/lib/tier.ts (server code cannot import from src/ui;
|
|
20
|
+
// tsconfig.server.json explicitly excludes it). Only the tier *name* is needed here; keep the
|
|
21
|
+
// `min` thresholds in sync with tier.ts's TIERS array if that ever changes.
|
|
22
|
+
const TIER_THRESHOLDS = [
|
|
23
|
+
{ name: 'Iron', min: 0 },
|
|
24
|
+
{ name: 'Bronze', min: 5_000_000 },
|
|
25
|
+
{ name: 'Silver', min: 10_000_000 },
|
|
26
|
+
{ name: 'Gold', min: 30_000_000 },
|
|
27
|
+
{ name: 'Platinum', min: 50_000_000 },
|
|
28
|
+
{ name: 'Emerald', min: 100_000_000 },
|
|
29
|
+
{ name: 'Diamond', min: 300_000_000 },
|
|
30
|
+
{ name: 'Master', min: 500_000_000 },
|
|
31
|
+
{ name: 'Grandmaster', min: 1_000_000_000 },
|
|
32
|
+
{ name: 'Challenger', min: 1_500_000_000 },
|
|
33
|
+
];
|
|
34
|
+
function getTierName(totalTokens) {
|
|
35
|
+
let name = TIER_THRESHOLDS[0].name;
|
|
36
|
+
for (const t of TIER_THRESHOLDS) {
|
|
37
|
+
if (totalTokens >= t.min)
|
|
38
|
+
name = t.name;
|
|
39
|
+
}
|
|
40
|
+
return name;
|
|
41
|
+
}
|
|
42
|
+
let UsageReportService = class UsageReportService {
|
|
43
|
+
usageService;
|
|
44
|
+
usageReportRepository;
|
|
45
|
+
constructor(usageService, usageReportRepository) {
|
|
46
|
+
this.usageService = usageService;
|
|
47
|
+
this.usageReportRepository = usageReportRepository;
|
|
48
|
+
}
|
|
49
|
+
// USR.PUBLISH: Aggregate the given month and store (or re-publish/upsert) a snapshot.
|
|
50
|
+
publish(month, workspaceId) {
|
|
51
|
+
this.validateMonth(month);
|
|
52
|
+
const { from, to } = this.monthRange(month);
|
|
53
|
+
const agentUsage = this.usageService.getAgentUsage(from, to, workspaceId);
|
|
54
|
+
const providerUsage = this.usageService.getProviderUsage(from, to, workspaceId);
|
|
55
|
+
const modelUsage = this.usageService.getModelUsage(from, to, workspaceId);
|
|
56
|
+
const reworkRate = this.usageService.getReworkRate(from, to, workspaceId);
|
|
57
|
+
const hourlyUsage = this.usageService.getHourlyUsage(from, to, workspaceId);
|
|
58
|
+
const failureBreakdown = this.usageService.getFailureBreakdown(from, to, workspaceId);
|
|
59
|
+
const agentSuccess = this.usageService.getAgentSuccess(from, to, workspaceId);
|
|
60
|
+
const totalTokens = agentUsage.totals.totalTokens;
|
|
61
|
+
const totalCostUsd = agentUsage.totals.totalCost;
|
|
62
|
+
const tier = getTierName(totalTokens);
|
|
63
|
+
const generatedAt = new Date().toISOString();
|
|
64
|
+
const sourceRange = `${from}/${to}`;
|
|
65
|
+
const payload = {
|
|
66
|
+
agentUsage,
|
|
67
|
+
providerUsage,
|
|
68
|
+
modelUsage,
|
|
69
|
+
reworkRate,
|
|
70
|
+
hourlyUsage,
|
|
71
|
+
failureBreakdown,
|
|
72
|
+
agentSuccess,
|
|
73
|
+
};
|
|
74
|
+
this.usageReportRepository.publish({
|
|
75
|
+
id: `usr_${nanoidBase62()}`,
|
|
76
|
+
workspaceId,
|
|
77
|
+
month,
|
|
78
|
+
generatedAt,
|
|
79
|
+
sourceRange,
|
|
80
|
+
tier,
|
|
81
|
+
totalTokens,
|
|
82
|
+
totalCostUsd,
|
|
83
|
+
payload: JSON.stringify(payload),
|
|
84
|
+
});
|
|
85
|
+
return { month, generatedAt, sourceRange, tier, totalTokens, totalCostUsd, payload };
|
|
86
|
+
}
|
|
87
|
+
// USR.GET: Fetch a previously published report. 404 if the month was never published.
|
|
88
|
+
get(month, workspaceId) {
|
|
89
|
+
this.validateMonth(month);
|
|
90
|
+
const row = this.usageReportRepository.findByMonth(workspaceId, month);
|
|
91
|
+
if (!row) {
|
|
92
|
+
throw new common_1.NotFoundException(`Usage report not found for month: ${month}`);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
month: row.month,
|
|
96
|
+
generatedAt: row.generated_at,
|
|
97
|
+
sourceRange: row.source_range,
|
|
98
|
+
tier: row.tier,
|
|
99
|
+
totalTokens: row.total_tokens,
|
|
100
|
+
totalCostUsd: row.total_cost_usd,
|
|
101
|
+
payload: JSON.parse(row.payload),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// USR.LIST: List publish history (summary columns only, no payload).
|
|
105
|
+
list(workspaceId) {
|
|
106
|
+
const rows = this.usageReportRepository.listByWorkspace(workspaceId);
|
|
107
|
+
const reports = rows.map((row) => ({
|
|
108
|
+
month: row.month,
|
|
109
|
+
generatedAt: row.generated_at,
|
|
110
|
+
tier: row.tier,
|
|
111
|
+
totalTokens: row.total_tokens,
|
|
112
|
+
totalCostUsd: row.total_cost_usd,
|
|
113
|
+
}));
|
|
114
|
+
return { reports };
|
|
115
|
+
}
|
|
116
|
+
validateMonth(month) {
|
|
117
|
+
if (!MONTH_RE.test(month)) {
|
|
118
|
+
throw new common_1.BadRequestException(`Invalid month format: ${month}. Expected YYYY-MM.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
monthRange(month) {
|
|
122
|
+
const [year, mon] = month.split('-').map(Number);
|
|
123
|
+
const from = `${month}-01T00:00:00.000Z`;
|
|
124
|
+
const nextMonth = mon === 12 ? 1 : mon + 1;
|
|
125
|
+
const nextYear = mon === 12 ? year + 1 : year;
|
|
126
|
+
const to = `${nextYear}-${String(nextMonth).padStart(2, '0')}-01T00:00:00.000Z`;
|
|
127
|
+
return { from, to };
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
exports.UsageReportService = UsageReportService;
|
|
131
|
+
exports.UsageReportService = UsageReportService = __decorate([
|
|
132
|
+
(0, common_1.Injectable)(),
|
|
133
|
+
__metadata("design:paramtypes", [usage_service_js_1.UsageService,
|
|
134
|
+
usage_report_repository_js_1.UsageReportRepository])
|
|
135
|
+
], UsageReportService);
|
|
@@ -15,6 +15,7 @@ const fs_1 = require("fs");
|
|
|
15
15
|
const path_1 = require("path");
|
|
16
16
|
const js_yaml_1 = require("js-yaml");
|
|
17
17
|
const thread_repository_js_1 = require("../../repository/thread.repository.js");
|
|
18
|
+
const task_repository_js_1 = require("../../repository/task.repository.js");
|
|
18
19
|
const WI_ID_RE = /^WI-\d{8}-\d+$/;
|
|
19
20
|
const VALID_STATUSES = new Set(['draft', 'approved', 'in-progress', 'done', 'cancelled']);
|
|
20
21
|
function toArray(v) {
|
|
@@ -36,8 +37,10 @@ function normalizeStatus(v) {
|
|
|
36
37
|
}
|
|
37
38
|
let WiService = class WiService {
|
|
38
39
|
threadRepo;
|
|
39
|
-
|
|
40
|
+
taskRepo;
|
|
41
|
+
constructor(threadRepo, taskRepo) {
|
|
40
42
|
this.threadRepo = threadRepo;
|
|
43
|
+
this.taskRepo = taskRepo;
|
|
41
44
|
}
|
|
42
45
|
validatePathBoundary(dir, fullPath) {
|
|
43
46
|
const dirCmp = dir.replace(/\\/g, '/').toLowerCase();
|
|
@@ -91,9 +94,44 @@ let WiService = class WiService {
|
|
|
91
94
|
const match = entries.find((f) => f === `${wiId}.md` || (f.startsWith(`${wiId}-`) && f.endsWith('.md')));
|
|
92
95
|
return match ?? null;
|
|
93
96
|
}
|
|
97
|
+
parseTaskMetadata(raw) {
|
|
98
|
+
if (!raw)
|
|
99
|
+
return {};
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(raw);
|
|
102
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
103
|
+
? parsed
|
|
104
|
+
: {};
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Find the workflow_card task for a thread, if any.
|
|
112
|
+
* Reuses TaskRepository.findTasksByThread (not batchFetchTasks, which is
|
|
113
|
+
* scoped to top-level tasks only and would miss workflow-nested tasks).
|
|
114
|
+
* No new proof API — proof stays embedded in the task result raw string;
|
|
115
|
+
* the caller (frontend) re-derives it from `raw` as needed.
|
|
116
|
+
*/
|
|
117
|
+
findWorkflowCard(threadId) {
|
|
118
|
+
const tasks = this.taskRepo.findTasksByThread(threadId);
|
|
119
|
+
const cardTasks = tasks.filter((t) => this.parseTaskMetadata(t.metadata).kind === 'workflow_card');
|
|
120
|
+
if (cardTasks.length === 0)
|
|
121
|
+
return undefined;
|
|
122
|
+
// findTasksByThread orders by started_at ASC — take the most recent card
|
|
123
|
+
const latest = cardTasks[cardTasks.length - 1];
|
|
124
|
+
const metadata = this.parseTaskMetadata(latest.metadata);
|
|
125
|
+
return {
|
|
126
|
+
taskId: latest.id,
|
|
127
|
+
workflowRunId: typeof metadata['workflowRunId'] === 'string' ? metadata['workflowRunId'] : undefined,
|
|
128
|
+
raw: latest.result ?? undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
94
131
|
/**
|
|
95
132
|
* Build a map from WI ID → runtime task info by loading all threads + their
|
|
96
|
-
* latest tasks in two bulk queries (no N+1)
|
|
133
|
+
* latest tasks in two bulk queries (no N+1), plus a per-thread
|
|
134
|
+
* workflow_card lookup (documented N+1, thread task volume is small).
|
|
97
135
|
*
|
|
98
136
|
* Matching rule: thread.id === wiId OR thread.title === wiId
|
|
99
137
|
*/
|
|
@@ -126,6 +164,7 @@ let WiService = class WiService {
|
|
|
126
164
|
current_task_id: latest?.id,
|
|
127
165
|
task_status: latest?.status,
|
|
128
166
|
task_agent: latest?.agent_id || undefined,
|
|
167
|
+
workflowCard: this.findWorkflowCard(thread.id),
|
|
129
168
|
});
|
|
130
169
|
}
|
|
131
170
|
return result;
|
|
@@ -154,6 +193,7 @@ let WiService = class WiService {
|
|
|
154
193
|
summary.current_task_id = runtime.current_task_id;
|
|
155
194
|
summary.task_status = runtime.task_status;
|
|
156
195
|
summary.task_agent = runtime.task_agent;
|
|
196
|
+
summary.workflowCard = runtime.workflowCard;
|
|
157
197
|
}
|
|
158
198
|
return { summary, frontmatter: data, body };
|
|
159
199
|
}
|
|
@@ -226,6 +266,7 @@ let WiService = class WiService {
|
|
|
226
266
|
summary.current_task_id = runtime.current_task_id;
|
|
227
267
|
summary.task_status = runtime.task_status;
|
|
228
268
|
summary.task_agent = runtime.task_agent;
|
|
269
|
+
summary.workflowCard = runtime.workflowCard;
|
|
229
270
|
}
|
|
230
271
|
}
|
|
231
272
|
return out.sort((a, b) => b.mtime - a.mtime);
|
|
@@ -234,5 +275,6 @@ let WiService = class WiService {
|
|
|
234
275
|
exports.WiService = WiService;
|
|
235
276
|
exports.WiService = WiService = __decorate([
|
|
236
277
|
(0, common_1.Injectable)(),
|
|
237
|
-
__metadata("design:paramtypes", [thread_repository_js_1.ThreadRepository
|
|
278
|
+
__metadata("design:paramtypes", [thread_repository_js_1.ThreadRepository,
|
|
279
|
+
task_repository_js_1.TaskRepository])
|
|
238
280
|
], WiService);
|
package/dist-server/main.js
CHANGED
|
@@ -59,6 +59,7 @@ async function bootstrap() {
|
|
|
59
59
|
// Adapter routes (/adapters/*) need raw Buffer body for signature verification.
|
|
60
60
|
const app = await core_1.NestFactory.create(app_module_js_1.AppModule, {
|
|
61
61
|
bodyParser: false,
|
|
62
|
+
forceCloseConnections: true,
|
|
62
63
|
...(httpsOptions ? { httpsOptions } : {}),
|
|
63
64
|
});
|
|
64
65
|
// trust proxy — set before other middleware so req.ip is correct behind nginx/Caddy
|
|
@@ -15,6 +15,7 @@ const box_repository_js_1 = require("./box.repository.js");
|
|
|
15
15
|
const project_repository_js_1 = require("./project.repository.js");
|
|
16
16
|
const request_log_repository_js_1 = require("./request-log.repository.js");
|
|
17
17
|
const usage_limit_snapshot_repository_js_1 = require("./usage-limit-snapshot.repository.js");
|
|
18
|
+
const usage_report_repository_js_1 = require("./usage-report.repository.js");
|
|
18
19
|
const repositories = [
|
|
19
20
|
thread_repository_js_1.ThreadRepository,
|
|
20
21
|
task_repository_js_1.TaskRepository,
|
|
@@ -23,6 +24,7 @@ const repositories = [
|
|
|
23
24
|
project_repository_js_1.ProjectRepository,
|
|
24
25
|
request_log_repository_js_1.RequestLogRepository,
|
|
25
26
|
usage_limit_snapshot_repository_js_1.UsageLimitSnapshotRepository,
|
|
27
|
+
usage_report_repository_js_1.UsageReportRepository,
|
|
26
28
|
];
|
|
27
29
|
let RepositoryModule = class RepositoryModule {
|
|
28
30
|
};
|
|
@@ -158,6 +158,14 @@ let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.Ba
|
|
|
158
158
|
getProviderUsage(from, to, workspace) {
|
|
159
159
|
return this.sdk.getProviderUsage(from, to, workspace);
|
|
160
160
|
}
|
|
161
|
+
/** USG.MODEL: Aggregate per-model (canonical) usage within a date range. */
|
|
162
|
+
getModelUsage(from, to, workspace) {
|
|
163
|
+
return this.sdk.getModelUsage(from, to, workspace);
|
|
164
|
+
}
|
|
165
|
+
/** USR: Generic raw task rows for usage-report aggregation (rework/hourly/failure). */
|
|
166
|
+
getTasksForReport(from, to, workspace) {
|
|
167
|
+
return this.sdk.getTasksForReport(from, to, workspace);
|
|
168
|
+
}
|
|
161
169
|
/** Find a task by id with workspace isolation for stop operation. */
|
|
162
170
|
findTaskForStop(taskId, workspaceId) {
|
|
163
171
|
const row = this.sdk.findTaskForStop(taskId, workspaceId);
|
|
@@ -279,6 +287,54 @@ let TaskRepository = class TaskRepository extends base_sqlite_repository_js_1.Ba
|
|
|
279
287
|
handle.close();
|
|
280
288
|
}
|
|
281
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Resume-failure safety net for `instructTask`. If the resume `crewx.execute()`
|
|
292
|
+
* rejects before the new process ever reports its pid (task:start), the row
|
|
293
|
+
* is stuck at status='running', pid=NULL forever. This transitions it to
|
|
294
|
+
* `failed`, but ONLY when the row is still exactly in that pending-resume
|
|
295
|
+
* state:
|
|
296
|
+
* - status = 'running'
|
|
297
|
+
* - run_epoch = the epoch bumped by this resume (markTaskResumedForInstruction)
|
|
298
|
+
* - pid IS NULL
|
|
299
|
+
*
|
|
300
|
+
* If a new process already reported its pid (task:start beat the catch), or
|
|
301
|
+
* a later instruction bumped run_epoch again, this is a no-op — we must
|
|
302
|
+
* never clobber a task that already recovered.
|
|
303
|
+
*
|
|
304
|
+
* @returns true if the row was updated.
|
|
305
|
+
*/
|
|
306
|
+
markTaskFailedIfResumePending(taskId, runEpoch, error, workspaceId) {
|
|
307
|
+
const handle = (0, repository_1.openDrizzleDb)(this.resolveDbPath());
|
|
308
|
+
try {
|
|
309
|
+
const condition = workspaceId
|
|
310
|
+
? (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId), (0, drizzle_orm_1.eq)(repository_1.tasks.workspace_id, workspaceId), (0, drizzle_orm_1.eq)(repository_1.tasks.status, 'running'), (0, drizzle_orm_1.eq)(repository_1.tasks.run_epoch, runEpoch), (0, drizzle_orm_1.isNull)(repository_1.tasks.pid))
|
|
311
|
+
: (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(repository_1.tasks.id, taskId), (0, drizzle_orm_1.eq)(repository_1.tasks.status, 'running'), (0, drizzle_orm_1.eq)(repository_1.tasks.run_epoch, runEpoch), (0, drizzle_orm_1.isNull)(repository_1.tasks.pid));
|
|
312
|
+
const row = handle.db
|
|
313
|
+
.select({ id: repository_1.tasks.id })
|
|
314
|
+
.from(repository_1.tasks)
|
|
315
|
+
.where(condition)
|
|
316
|
+
.limit(1)
|
|
317
|
+
.get();
|
|
318
|
+
if (!row)
|
|
319
|
+
return false;
|
|
320
|
+
const params = workspaceId
|
|
321
|
+
? ['failed', error, new Date().toISOString(), taskId, runEpoch, workspaceId]
|
|
322
|
+
: ['failed', error, new Date().toISOString(), taskId, runEpoch];
|
|
323
|
+
const where = workspaceId
|
|
324
|
+
? `WHERE id = ? AND run_epoch = ? AND pid IS NULL AND status = 'running' AND workspace_id = ?`
|
|
325
|
+
: `WHERE id = ? AND run_epoch = ? AND pid IS NULL AND status = 'running'`;
|
|
326
|
+
handle.runRaw(`UPDATE tasks SET status = ?, error = ?, completed_at = ? ${where}`, params);
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
if (e instanceof repository_1.RepositoryError)
|
|
331
|
+
throw e;
|
|
332
|
+
throw new repository_1.RepositoryError('DB_ERROR', 'Failed to mark task failed for pending resume', e);
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
handle.close();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
282
338
|
appendLog(taskId, entry) {
|
|
283
339
|
this.sdk.appendLog(taskId, entry);
|
|
284
340
|
}
|
|
@@ -149,6 +149,19 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
149
149
|
throw e;
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
/** Atomically resolve overdrive state for an incoming request.
|
|
153
|
+
* See SdkThreadRepository.resolveOverdriveForRequest for the state model.
|
|
154
|
+
* Returns null when the thread is not found. */
|
|
155
|
+
resolveOverdriveForRequest(threadId, workspaceId, action, opts) {
|
|
156
|
+
try {
|
|
157
|
+
return this.sdkThread.resolveOverdriveForRequest(threadId, workspaceId, action, opts);
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
if (e instanceof repository_1.RepositoryError)
|
|
161
|
+
return null;
|
|
162
|
+
throw e;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
152
165
|
/** Reorder pinned threads by writing pinOrder to each thread's metadata. */
|
|
153
166
|
reorderPins(threadIds, workspaceId) {
|
|
154
167
|
try {
|
|
@@ -0,0 +1,27 @@
|
|
|
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.UsageReportRepository = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const repository_1 = require("@crewx/sdk/repository");
|
|
12
|
+
let UsageReportRepository = class UsageReportRepository {
|
|
13
|
+
sdk = new repository_1.UsageReportRepository();
|
|
14
|
+
publish(input) {
|
|
15
|
+
return this.sdk.publish(input);
|
|
16
|
+
}
|
|
17
|
+
findByMonth(workspaceId, month) {
|
|
18
|
+
return this.sdk.findByMonth(workspaceId, month);
|
|
19
|
+
}
|
|
20
|
+
listByWorkspace(workspaceId) {
|
|
21
|
+
return this.sdk.listByWorkspace(workspaceId);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
exports.UsageReportRepository = UsageReportRepository;
|
|
25
|
+
exports.UsageReportRepository = UsageReportRepository = __decorate([
|
|
26
|
+
(0, common_1.Injectable)()
|
|
27
|
+
], UsageReportRepository);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewx",
|
|
3
|
-
"version": "0.9.0-rc.
|
|
3
|
+
"version": "0.9.0-rc.21",
|
|
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": {
|
|
@@ -81,19 +81,19 @@
|
|
|
81
81
|
"yargs": "17.7.0",
|
|
82
82
|
"zod": "3.25.76",
|
|
83
83
|
"@crewx/chromex": "0.1.0",
|
|
84
|
-
"@crewx/cli": "0.9.0-rc.
|
|
85
|
-
"@crewx/cron": "0.1.10",
|
|
84
|
+
"@crewx/cli": "0.9.0-rc.21",
|
|
86
85
|
"@crewx/doc": "0.1.9",
|
|
86
|
+
"@crewx/cron": "0.1.10",
|
|
87
87
|
"@crewx/dreaming": "0.1.0",
|
|
88
|
-
"@crewx/knowledge-core": "0.1.17",
|
|
89
88
|
"@crewx/memory": "0.1.23",
|
|
90
|
-
"@crewx/
|
|
89
|
+
"@crewx/knowledge-core": "0.1.17-rc.30",
|
|
91
90
|
"@crewx/search": "0.1.10",
|
|
92
|
-
"@crewx/
|
|
93
|
-
"@crewx/wbs": "0.1.10",
|
|
94
|
-
"@crewx/wi": "0.1.10",
|
|
91
|
+
"@crewx/sdk": "0.9.0-rc.21",
|
|
95
92
|
"@crewx/skill": "0.1.20",
|
|
96
|
-
"@crewx/
|
|
93
|
+
"@crewx/shared": "0.0.6",
|
|
94
|
+
"@crewx/workflow": "0.3.22-rc.67",
|
|
95
|
+
"@crewx/wi": "0.1.10-rc.41",
|
|
96
|
+
"@crewx/wbs": "0.1.10"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@ccusage/codex": "0.0.1",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-layer policy: inject CrewX home as an extra writable root for Codex
|
|
3
|
+
* `workspace-write` sandbox invocations.
|
|
4
|
+
*
|
|
5
|
+
* Why this lives in packages/cli (not packages/sdk):
|
|
6
|
+
* CrewX home (`~/.crewx` by default) is a product-specific path. The SDK's
|
|
7
|
+
* `additionalArgsProvider` extension point (WI-20260703-001) exists precisely
|
|
8
|
+
* so that product layers like this CLI can inject such paths without the SDK
|
|
9
|
+
* knowing about them.
|
|
10
|
+
*
|
|
11
|
+
* Why `-c sandbox_workspace_write.writable_roots=[...]` instead of `--add-dir`:
|
|
12
|
+
* Verified against codex-cli 0.139.0 (`codex exec --help` / `codex exec resume --help`):
|
|
13
|
+
* - `codex exec` supports `--add-dir <DIR>`.
|
|
14
|
+
* - `codex exec resume` does NOT support `--add-dir` (only `-c/--config`).
|
|
15
|
+
* Using the `-c` config-override form works identically for both `exec` and
|
|
16
|
+
* `exec resume`, avoiding a resume-incompatible flag.
|
|
17
|
+
*/
|
|
18
|
+
import type { AdditionalArgsProvider } from '@crewx/sdk';
|
|
19
|
+
/**
|
|
20
|
+
* Resolve CrewX home directory.
|
|
21
|
+
* Priority: `CREWX_HOME` env var → `~/.crewx`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveCrewxHome(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Escape a string for embedding inside a TOML basic string (`"..."`).
|
|
26
|
+
* Sufficient for filesystem paths: handles backslash (Windows paths),
|
|
27
|
+
* double-quote, and control characters that could otherwise break TOML
|
|
28
|
+
* parsing of the `-c key="value"` override.
|
|
29
|
+
*/
|
|
30
|
+
export declare function escapeTomlBasicString(value: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Build the `-c sandbox_workspace_write.writable_roots=["<path>"]` arg pair.
|
|
33
|
+
* Works for both `codex exec` and `codex exec resume` (config-override form).
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildCodexWritableRootArgs(crewxHome: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Create an `AdditionalArgsProvider` (SDK extension point, WI-20260703-001)
|
|
38
|
+
* that injects CrewX home as a Codex writable root for `workspace-write`
|
|
39
|
+
* equivalent modes (`agent`, `auto`, `undefined`).
|
|
40
|
+
*
|
|
41
|
+
* No-ops for:
|
|
42
|
+
* - Non-codex providers.
|
|
43
|
+
* - Modes in {@link NO_INJECT_MODES} (read-only/plan/agent-full-access/yolo/danger-full-access).
|
|
44
|
+
*/
|
|
45
|
+
export declare function createCodexWritableRootsProvider(crewxHome?: string): AdditionalArgsProvider;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI-layer policy: inject CrewX home as an extra writable root for Codex
|
|
4
|
+
* `workspace-write` sandbox invocations.
|
|
5
|
+
*
|
|
6
|
+
* Why this lives in packages/cli (not packages/sdk):
|
|
7
|
+
* CrewX home (`~/.crewx` by default) is a product-specific path. The SDK's
|
|
8
|
+
* `additionalArgsProvider` extension point (WI-20260703-001) exists precisely
|
|
9
|
+
* so that product layers like this CLI can inject such paths without the SDK
|
|
10
|
+
* knowing about them.
|
|
11
|
+
*
|
|
12
|
+
* Why `-c sandbox_workspace_write.writable_roots=[...]` instead of `--add-dir`:
|
|
13
|
+
* Verified against codex-cli 0.139.0 (`codex exec --help` / `codex exec resume --help`):
|
|
14
|
+
* - `codex exec` supports `--add-dir <DIR>`.
|
|
15
|
+
* - `codex exec resume` does NOT support `--add-dir` (only `-c/--config`).
|
|
16
|
+
* Using the `-c` config-override form works identically for both `exec` and
|
|
17
|
+
* `exec resume`, avoiding a resume-incompatible flag.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.resolveCrewxHome = resolveCrewxHome;
|
|
21
|
+
exports.escapeTomlBasicString = escapeTomlBasicString;
|
|
22
|
+
exports.buildCodexWritableRootArgs = buildCodexWritableRootArgs;
|
|
23
|
+
exports.createCodexWritableRootsProvider = createCodexWritableRootsProvider;
|
|
24
|
+
const os_1 = require("os");
|
|
25
|
+
const path_1 = require("path");
|
|
26
|
+
/**
|
|
27
|
+
* crewx.yaml / query-option `mode` values for which Codex is NOT running in
|
|
28
|
+
* (or being pushed into) a `workspace-write`-equivalent sandbox. Injecting an
|
|
29
|
+
* extra writable root is meaningless (read-only/plan) or redundant
|
|
30
|
+
* (full-access variants already allow writes everywhere) in these modes.
|
|
31
|
+
*/
|
|
32
|
+
const NO_INJECT_MODES = new Set([
|
|
33
|
+
'read-only',
|
|
34
|
+
'plan',
|
|
35
|
+
'agent-full-access',
|
|
36
|
+
'yolo',
|
|
37
|
+
'danger-full-access',
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* Resolve CrewX home directory.
|
|
41
|
+
* Priority: `CREWX_HOME` env var → `~/.crewx`.
|
|
42
|
+
*/
|
|
43
|
+
function resolveCrewxHome() {
|
|
44
|
+
return process.env.CREWX_HOME ?? (0, path_1.join)((0, os_1.homedir)(), '.crewx');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Escape a string for embedding inside a TOML basic string (`"..."`).
|
|
48
|
+
* Sufficient for filesystem paths: handles backslash (Windows paths),
|
|
49
|
+
* double-quote, and control characters that could otherwise break TOML
|
|
50
|
+
* parsing of the `-c key="value"` override.
|
|
51
|
+
*/
|
|
52
|
+
function escapeTomlBasicString(value) {
|
|
53
|
+
return value
|
|
54
|
+
.replace(/\\/g, '\\\\')
|
|
55
|
+
.replace(/"/g, '\\"')
|
|
56
|
+
.replace(/\n/g, '\\n')
|
|
57
|
+
.replace(/\r/g, '\\r')
|
|
58
|
+
.replace(/\t/g, '\\t');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the `-c sandbox_workspace_write.writable_roots=["<path>"]` arg pair.
|
|
62
|
+
* Works for both `codex exec` and `codex exec resume` (config-override form).
|
|
63
|
+
*/
|
|
64
|
+
function buildCodexWritableRootArgs(crewxHome) {
|
|
65
|
+
const escaped = escapeTomlBasicString(crewxHome);
|
|
66
|
+
return ['-c', `sandbox_workspace_write.writable_roots=["${escaped}"]`];
|
|
67
|
+
}
|
|
68
|
+
function isCodexProvider(ctx) {
|
|
69
|
+
return ctx.providerId === 'codex' || ctx.providerStr === 'cli/codex';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create an `AdditionalArgsProvider` (SDK extension point, WI-20260703-001)
|
|
73
|
+
* that injects CrewX home as a Codex writable root for `workspace-write`
|
|
74
|
+
* equivalent modes (`agent`, `auto`, `undefined`).
|
|
75
|
+
*
|
|
76
|
+
* No-ops for:
|
|
77
|
+
* - Non-codex providers.
|
|
78
|
+
* - Modes in {@link NO_INJECT_MODES} (read-only/plan/agent-full-access/yolo/danger-full-access).
|
|
79
|
+
*/
|
|
80
|
+
function createCodexWritableRootsProvider(crewxHome = resolveCrewxHome()) {
|
|
81
|
+
return (ctx) => {
|
|
82
|
+
if (!isCodexProvider(ctx))
|
|
83
|
+
return [];
|
|
84
|
+
if (ctx.mode !== undefined && NO_INJECT_MODES.has(ctx.mode))
|
|
85
|
+
return [];
|
|
86
|
+
return buildCodexWritableRootArgs(crewxHome);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -9,6 +9,7 @@ const plugins_1 = require("@crewx/sdk/plugins");
|
|
|
9
9
|
const repository_1 = require("@crewx/sdk/repository");
|
|
10
10
|
const register_builtin_tools_1 = require("../register-builtin-tools");
|
|
11
11
|
const version_1 = require("../utils/version");
|
|
12
|
+
const codex_writable_roots_1 = require("./codex-writable-roots");
|
|
12
13
|
/**
|
|
13
14
|
* Build a Crewx instance with CLI-standard plugins (FileLogger + SqliteTracing)
|
|
14
15
|
* and built-in tools registered. Use this from any CLI command that needs a
|
|
@@ -52,6 +53,7 @@ async function createCliCrewx(configPath = process.env.CREWX_CONFIG ?? 'crewx.ya
|
|
|
52
53
|
}
|
|
53
54
|
const crewx = await sdk_1.Crewx.loadYaml(yamlPath, {
|
|
54
55
|
remoteFactory: createCliCrewx,
|
|
56
|
+
additionalArgsProvider: (0, codex_writable_roots_1.createCodexWritableRootsProvider)(),
|
|
55
57
|
});
|
|
56
58
|
(0, register_builtin_tools_1.registerBuiltinToolsIfNeeded)(crewx);
|
|
57
59
|
await crewx.use(new plugins_1.ConversationPlugin());
|