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.
Files changed (104) hide show
  1. package/dist/assets/MarketPage-DDPL2d7x.js +36 -0
  2. package/dist/assets/{PromptTab-hdXDbbT6.js → PromptTab-UC3K4qx2.js} +3 -3
  3. package/dist/assets/{arc--CBVVcyH.js → arc-DdP3-fUR.js} +1 -1
  4. package/dist/assets/{architectureDiagram-UYN6MBPD-DUiYwqJn.js → architectureDiagram-UYN6MBPD-D16VON1i.js} +1 -1
  5. package/dist/assets/{blockDiagram-ZHA2E4KO-DV86vAgG.js → blockDiagram-ZHA2E4KO-qs1dIRkl.js} +4 -4
  6. package/dist/assets/{c4Diagram-6F5ED5ID-CvWE7Cx_.js → c4Diagram-6F5ED5ID-BeDy8akQ.js} +1 -1
  7. package/dist/assets/channel-DtrxBSkc.js +1 -0
  8. package/dist/assets/{chunk-5HRBRIJM-DAxM5Ul_.js → chunk-5HRBRIJM-BC1zIb11.js} +1 -1
  9. package/dist/assets/{chunk-7U56Z5CX-BuYaEj8L.js → chunk-7U56Z5CX-BuGil3QD.js} +1 -1
  10. package/dist/assets/{chunk-ASOPGD6M-2Kx0e45C.js → chunk-ASOPGD6M-DmKdDtBQ.js} +1 -1
  11. package/dist/assets/{chunk-KFBOBJHC-CR4QS6Rt.js → chunk-KFBOBJHC-D3BvHLPa.js} +1 -1
  12. package/dist/assets/{chunk-T2TOU4HS-CffcwNHx.js → chunk-T2TOU4HS-B5MoUp-N.js} +1 -1
  13. package/dist/assets/{chunk-TMUBEWPD-BGpYtekN.js → chunk-TMUBEWPD-AvWe_lsV.js} +1 -1
  14. package/dist/assets/classDiagram-LNE6IOMH-BMT5B_JB.js +1 -0
  15. package/dist/assets/classDiagram-v2-MQ7JQ4JX-BMT5B_JB.js +1 -0
  16. package/dist/assets/{dagre-4EVJKHTY-yBWukkrE.js → dagre-4EVJKHTY-B6DewFyM.js} +1 -1
  17. package/dist/assets/{diagram-QW4FP2JN-CIr1PLq3.js → diagram-QW4FP2JN-BTsQ8JSd.js} +1 -1
  18. package/dist/assets/{erDiagram-6RL3IURR-xG7SygyU.js → erDiagram-6RL3IURR-BM5nvgVR.js} +1 -1
  19. package/dist/assets/{flowDiagram-7ASYPVHJ-CKalfOqp.js → flowDiagram-7ASYPVHJ-D1UCCuOG.js} +1 -1
  20. package/dist/assets/{ganttDiagram-NTVNEXSI-Bnx7zrX4.js → ganttDiagram-NTVNEXSI-CNIGs7Cn.js} +1 -1
  21. package/dist/assets/{gitGraph-YCYPL57B-TQlF0fiA.js → gitGraph-YCYPL57B-DSLjx1yQ.js} +1 -1
  22. package/dist/assets/{gitGraphDiagram-NRZ2UAAF-DxlaFkwO.js → gitGraphDiagram-NRZ2UAAF-B11zX1Fa.js} +1 -1
  23. package/dist/assets/{infoDiagram-A4XQUW5V-B1-arkqa.js → infoDiagram-A4XQUW5V-DkjvODYl.js} +1 -1
  24. package/dist/assets/{journeyDiagram-G5WM74LC-Cj439GUX.js → journeyDiagram-G5WM74LC-BkCQPMn6.js} +1 -1
  25. package/dist/assets/{kanban-definition-QRCXZQQD-BvrIwEIc.js → kanban-definition-QRCXZQQD-_OX7pNfJ.js} +1 -1
  26. package/dist/assets/{linear-BKYNK0ri.js → linear-BYjwVAR6.js} +1 -1
  27. package/dist/assets/main-CZdKLdUl.css +10 -0
  28. package/dist/assets/main-CkkZNCy-.js +1561 -0
  29. package/dist/assets/{mindmap-definition-GWI6TPTV-BExfJY0G.js → mindmap-definition-GWI6TPTV-BzYpSnv8.js} +1 -1
  30. package/dist/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
  31. package/dist/assets/{pieDiagram-YF2LJOPJ-RPWpvrC8.js → pieDiagram-YF2LJOPJ-CEFg6ao0.js} +1 -1
  32. package/dist/assets/{quadrantDiagram-OS5C2QUG-DmbBzk5Q.js → quadrantDiagram-OS5C2QUG-_5w2x3VV.js} +1 -1
  33. package/dist/assets/{requirementDiagram-MIRIMTAZ-DFQuFXq3.js → requirementDiagram-MIRIMTAZ-B_VQYReZ.js} +1 -1
  34. package/dist/assets/{sankeyDiagram-Y46BX6SQ-p_bw56lJ.js → sankeyDiagram-Y46BX6SQ-CgmOlLhh.js} +1 -1
  35. package/dist/assets/{sequenceDiagram-G6AWOVSC-DTbBkLeD.js → sequenceDiagram-G6AWOVSC-MGVt0Ai2.js} +1 -1
  36. package/dist/assets/{stateDiagram-MAYHULR4-DBvC6UcF.js → stateDiagram-MAYHULR4-DaZjgTu-.js} +1 -1
  37. package/dist/assets/stateDiagram-v2-4JROLMXI-DN1wGYkV.js +1 -0
  38. package/dist/assets/{timeline-definition-U7ZMHBDA-B6utvIg1.js → timeline-definition-U7ZMHBDA-CusetmK6.js} +1 -1
  39. package/dist/assets/transformers.web-BN8Ug43_.js +45 -0
  40. package/dist/assets/{xychartDiagram-6QU3TZC5-BHG0s1Ye.js → xychartDiagram-6QU3TZC5-CTndf-if.js} +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist-server/app.module.js +4 -0
  43. package/dist-server/bootstrap/codex-writable-roots.js +88 -0
  44. package/dist-server/bootstrap/crewx-server.js +10 -2
  45. package/dist-server/common/decorator/skip-request-log.decorator.js +8 -0
  46. package/dist-server/common/interceptor/http-logging.interceptor.js +17 -4
  47. package/dist-server/common/task-log/stdout-json-fallback.js +200 -0
  48. package/dist-server/domain/agent/agent.controller.js +14 -0
  49. package/dist-server/domain/agent/agent.service.js +246 -18
  50. package/dist-server/domain/agent/dto/create-agent.dto.js +12 -0
  51. package/dist-server/domain/agent/dto/remote-provider.dto.js +82 -0
  52. package/dist-server/domain/agent/dto/reorder-agents.dto.js +51 -0
  53. package/dist-server/domain/agent/dto/update-agent.dto.js +3 -1
  54. package/dist-server/domain/agent/provider.controller.js +12 -1
  55. package/dist-server/domain/doc/doc.controller.js +43 -9
  56. package/dist-server/domain/doc/doc.service.js +80 -6
  57. package/dist-server/domain/doc/image-constants.js +11 -1
  58. package/dist-server/domain/events/workspace-events.controller.js +2 -0
  59. package/dist-server/domain/message/dto/send-message.dto.js +49 -1
  60. package/dist-server/domain/message/message.controller.js +1 -1
  61. package/dist-server/domain/message/message.module.js +2 -1
  62. package/dist-server/domain/message/message.service.js +83 -13
  63. package/dist-server/domain/settings/settings.service.js +188 -1
  64. package/dist-server/domain/system/system.controller.js +41 -0
  65. package/dist-server/domain/system/system.module.js +19 -0
  66. package/dist-server/domain/task/task.constants.js +8 -1
  67. package/dist-server/domain/task/task.service.js +59 -67
  68. package/dist-server/domain/thread/dto/list-threads.dto.js +18 -0
  69. package/dist-server/domain/thread/overdrive.types.js +38 -0
  70. package/dist-server/domain/thread/thread.service.js +83 -70
  71. package/dist-server/domain/usage/usage.controller.js +18 -0
  72. package/dist-server/domain/usage/usage.module.js +1 -0
  73. package/dist-server/domain/usage/usage.service.js +246 -4
  74. package/dist-server/domain/usage-report/dto/usage-report.dto.js +2 -0
  75. package/dist-server/domain/usage-report/usage-report.controller.js +72 -0
  76. package/dist-server/domain/usage-report/usage-report.module.js +23 -0
  77. package/dist-server/domain/usage-report/usage-report.service.js +135 -0
  78. package/dist-server/domain/wi/wi.service.js +45 -3
  79. package/dist-server/main.js +1 -0
  80. package/dist-server/repository/repository.module.js +2 -0
  81. package/dist-server/repository/task.repository.js +56 -0
  82. package/dist-server/repository/thread.repository.js +13 -0
  83. package/dist-server/repository/usage-report.repository.js +27 -0
  84. package/package.json +9 -9
  85. package/packages/cli/dist/bootstrap/codex-writable-roots.d.ts +45 -0
  86. package/packages/cli/dist/bootstrap/codex-writable-roots.js +88 -0
  87. package/packages/cli/dist/bootstrap/crewx-cli.js +2 -0
  88. package/packages/cli/dist/commands/execute.js +10 -2
  89. package/packages/cli/dist/commands/parse-common-flags.d.ts +5 -1
  90. package/packages/cli/dist/commands/parse-common-flags.js +9 -2
  91. package/packages/cli/dist/commands/ps.js +51 -2
  92. package/packages/cli/dist/commands/query.js +10 -2
  93. package/packages/cli/dist/commands/restart.js +20 -6
  94. package/packages/cli/dist/commands/write-output.d.ts +3 -0
  95. package/packages/cli/dist/commands/write-output.js +24 -0
  96. package/packages/cli/dist/main.js +7 -0
  97. package/packages/cli/package.json +1 -1
  98. package/dist/assets/MarketPage-T1o_oIrw.js +0 -41
  99. package/dist/assets/channel-7q16D7P9.js +0 -1
  100. package/dist/assets/classDiagram-LNE6IOMH-C6qjk4uc.js +0 -1
  101. package/dist/assets/classDiagram-v2-MQ7JQ4JX-C6qjk4uc.js +0 -1
  102. package/dist/assets/main-C5mwv79r.css +0 -10
  103. package/dist/assets/main-DmvFaz-d.js +0 -1486
  104. 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
- constructor(threadRepo) {
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);
@@ -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.2",
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.2",
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/sdk": "0.9.0-rc.2",
89
+ "@crewx/knowledge-core": "0.1.17-rc.30",
91
90
  "@crewx/search": "0.1.10",
92
- "@crewx/shared": "0.0.6",
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/workflow": "0.3.22"
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());