@crewx/cli 0.8.4-rc.0 → 0.8.4-rc.1

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 (220) hide show
  1. package/README.md +33 -33
  2. package/bin/crewx +2 -2
  3. package/dist/commands/agent.js +23 -23
  4. package/dist/commands/init.js +20 -20
  5. package/dist/commands/task-db.d.ts +33 -0
  6. package/dist/commands/task-db.js +107 -0
  7. package/dist/examples/deny-secrets-plugin.d.ts +22 -0
  8. package/dist/examples/deny-secrets-plugin.js +40 -0
  9. package/dist/main.js +72 -72
  10. package/dist/plugins/examples/echo-hook.d.ts +24 -0
  11. package/dist/plugins/examples/echo-hook.js +60 -0
  12. package/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
  13. package/dist/plugins/examples/verify-echo-hook.js +47 -0
  14. package/dist/plugins/sqlite-tracing.d.ts +11 -0
  15. package/dist/plugins/sqlite-tracing.js +19 -0
  16. package/dist/repository/workspace.repository.d.ts +26 -0
  17. package/dist/repository/workspace.repository.js +111 -0
  18. package/dist/schema/tasks.d.ts +7 -0
  19. package/dist/schema/tasks.js +48 -0
  20. package/package.json +18 -18
  21. package/dist/ai-provider.service.d.ts +0 -34
  22. package/dist/ai-provider.service.js +0 -311
  23. package/dist/ai-provider.service.js.map +0 -1
  24. package/dist/ai.service.d.ts +0 -17
  25. package/dist/ai.service.js +0 -51
  26. package/dist/ai.service.js.map +0 -1
  27. package/dist/app.module.d.ts +0 -5
  28. package/dist/app.module.js +0 -165
  29. package/dist/app.module.js.map +0 -1
  30. package/dist/cli/agent.handler.d.ts +0 -2
  31. package/dist/cli/agent.handler.js +0 -186
  32. package/dist/cli/agent.handler.js.map +0 -1
  33. package/dist/cli/builtin.handler.d.ts +0 -3
  34. package/dist/cli/builtin.handler.js +0 -110
  35. package/dist/cli/builtin.handler.js.map +0 -1
  36. package/dist/cli/chat.handler.d.ts +0 -20
  37. package/dist/cli/chat.handler.js +0 -446
  38. package/dist/cli/chat.handler.js.map +0 -1
  39. package/dist/cli/cli.handler.d.ts +0 -4
  40. package/dist/cli/cli.handler.js +0 -119
  41. package/dist/cli/cli.handler.js.map +0 -1
  42. package/dist/cli/doctor.handler.d.ts +0 -38
  43. package/dist/cli/doctor.handler.js +0 -495
  44. package/dist/cli/doctor.handler.js.map +0 -1
  45. package/dist/cli/execute.handler.d.ts +0 -2
  46. package/dist/cli/execute.handler.js +0 -321
  47. package/dist/cli/execute.handler.js.map +0 -1
  48. package/dist/cli/help.handler.d.ts +0 -2
  49. package/dist/cli/help.handler.js +0 -10
  50. package/dist/cli/help.handler.js.map +0 -1
  51. package/dist/cli/init.handler.d.ts +0 -26
  52. package/dist/cli/init.handler.js +0 -450
  53. package/dist/cli/init.handler.js.map +0 -1
  54. package/dist/cli/log.handler.d.ts +0 -2
  55. package/dist/cli/log.handler.js +0 -69
  56. package/dist/cli/log.handler.js.map +0 -1
  57. package/dist/cli/mcp.handler.d.ts +0 -3
  58. package/dist/cli/mcp.handler.js +0 -121
  59. package/dist/cli/mcp.handler.js.map +0 -1
  60. package/dist/cli/query.handler.d.ts +0 -2
  61. package/dist/cli/query.handler.js +0 -379
  62. package/dist/cli/query.handler.js.map +0 -1
  63. package/dist/cli/skill.handler.d.ts +0 -2
  64. package/dist/cli/skill.handler.js +0 -252
  65. package/dist/cli/skill.handler.js.map +0 -1
  66. package/dist/cli/slack-files.handler.d.ts +0 -2
  67. package/dist/cli/slack-files.handler.js +0 -291
  68. package/dist/cli/slack-files.handler.js.map +0 -1
  69. package/dist/cli/template.handler.d.ts +0 -2
  70. package/dist/cli/template.handler.js +0 -188
  71. package/dist/cli/template.handler.js.map +0 -1
  72. package/dist/cli/templates.handler.d.ts +0 -2
  73. package/dist/cli/templates.handler.js +0 -100
  74. package/dist/cli/templates.handler.js.map +0 -1
  75. package/dist/cli-options.d.ts +0 -39
  76. package/dist/cli-options.js +0 -355
  77. package/dist/cli-options.js.map +0 -1
  78. package/dist/config/timeout.config.d.ts +0 -14
  79. package/dist/config/timeout.config.js +0 -34
  80. package/dist/config/timeout.config.js.map +0 -1
  81. package/dist/conversation/base-conversation-history.provider.d.ts +0 -12
  82. package/dist/conversation/base-conversation-history.provider.js +0 -45
  83. package/dist/conversation/base-conversation-history.provider.js.map +0 -1
  84. package/dist/conversation/cli-conversation-history.provider.d.ts +0 -16
  85. package/dist/conversation/cli-conversation-history.provider.js +0 -111
  86. package/dist/conversation/cli-conversation-history.provider.js.map +0 -1
  87. package/dist/conversation/conversation-provider.factory.d.ts +0 -10
  88. package/dist/conversation/conversation-provider.factory.js +0 -50
  89. package/dist/conversation/conversation-provider.factory.js.map +0 -1
  90. package/dist/conversation/index.d.ts +0 -6
  91. package/dist/conversation/index.js +0 -27
  92. package/dist/conversation/index.js.map +0 -1
  93. package/dist/conversation/slack-conversation-history.provider.d.ts +0 -29
  94. package/dist/conversation/slack-conversation-history.provider.js +0 -302
  95. package/dist/conversation/slack-conversation-history.provider.js.map +0 -1
  96. package/dist/crewx.tool.d.ts +0 -359
  97. package/dist/crewx.tool.js +0 -2501
  98. package/dist/crewx.tool.js.map +0 -1
  99. package/dist/crewx.tool.spec.d.ts +0 -1
  100. package/dist/crewx.tool.spec.js +0 -158
  101. package/dist/crewx.tool.spec.js.map +0 -1
  102. package/dist/guards/bearer-auth.guard.d.ts +0 -7
  103. package/dist/guards/bearer-auth.guard.js +0 -44
  104. package/dist/guards/bearer-auth.guard.js.map +0 -1
  105. package/dist/health.controller.d.ts +0 -6
  106. package/dist/health.controller.js +0 -32
  107. package/dist/health.controller.js.map +0 -1
  108. package/dist/main.js.map +0 -1
  109. package/dist/mcp.controller.d.ts +0 -8
  110. package/dist/mcp.controller.js +0 -62
  111. package/dist/mcp.controller.js.map +0 -1
  112. package/dist/package.json +0 -3
  113. package/dist/providers/dynamic-provider.factory.d.ts +0 -15
  114. package/dist/providers/dynamic-provider.factory.js +0 -133
  115. package/dist/providers/dynamic-provider.factory.js.map +0 -1
  116. package/dist/providers/logger.adapter.d.ts +0 -6
  117. package/dist/providers/logger.adapter.js +0 -102
  118. package/dist/providers/logger.adapter.js.map +0 -1
  119. package/dist/services/agent-loader.service.d.ts +0 -35
  120. package/dist/services/agent-loader.service.js +0 -622
  121. package/dist/services/agent-loader.service.js.map +0 -1
  122. package/dist/services/auth.service.d.ts +0 -9
  123. package/dist/services/auth.service.js +0 -47
  124. package/dist/services/auth.service.js.map +0 -1
  125. package/dist/services/config-validator.service.d.ts +0 -29
  126. package/dist/services/config-validator.service.js +0 -483
  127. package/dist/services/config-validator.service.js.map +0 -1
  128. package/dist/services/config.service.d.ts +0 -45
  129. package/dist/services/config.service.js +0 -352
  130. package/dist/services/config.service.js.map +0 -1
  131. package/dist/services/document-loader.service.d.ts +0 -21
  132. package/dist/services/document-loader.service.js +0 -156
  133. package/dist/services/document-loader.service.js.map +0 -1
  134. package/dist/services/help.service.d.ts +0 -5
  135. package/dist/services/help.service.js +0 -139
  136. package/dist/services/help.service.js.map +0 -1
  137. package/dist/services/intelligent-compression.service.d.ts +0 -20
  138. package/dist/services/intelligent-compression.service.js +0 -179
  139. package/dist/services/intelligent-compression.service.js.map +0 -1
  140. package/dist/services/mcp-client.service.d.ts +0 -26
  141. package/dist/services/mcp-client.service.js +0 -81
  142. package/dist/services/mcp-client.service.js.map +0 -1
  143. package/dist/services/parallel-processing.service.d.ts +0 -108
  144. package/dist/services/parallel-processing.service.js +0 -333
  145. package/dist/services/parallel-processing.service.js.map +0 -1
  146. package/dist/services/provider-bridge.service.d.ts +0 -35
  147. package/dist/services/provider-bridge.service.js +0 -224
  148. package/dist/services/provider-bridge.service.js.map +0 -1
  149. package/dist/services/remote-agent.service.d.ts +0 -50
  150. package/dist/services/remote-agent.service.js +0 -171
  151. package/dist/services/remote-agent.service.js.map +0 -1
  152. package/dist/services/result-formatter.service.d.ts +0 -27
  153. package/dist/services/result-formatter.service.js +0 -126
  154. package/dist/services/result-formatter.service.js.map +0 -1
  155. package/dist/services/skill-loader.service.d.ts +0 -15
  156. package/dist/services/skill-loader.service.js +0 -278
  157. package/dist/services/skill-loader.service.js.map +0 -1
  158. package/dist/services/skill.service.d.ts +0 -67
  159. package/dist/services/skill.service.js +0 -670
  160. package/dist/services/skill.service.js.map +0 -1
  161. package/dist/services/skill.service.spec.d.ts +0 -1
  162. package/dist/services/skill.service.spec.js +0 -35
  163. package/dist/services/skill.service.spec.js.map +0 -1
  164. package/dist/services/task-management.service.d.ts +0 -65
  165. package/dist/services/task-management.service.js +0 -288
  166. package/dist/services/task-management.service.js.map +0 -1
  167. package/dist/services/template.service.d.ts +0 -61
  168. package/dist/services/template.service.js +0 -416
  169. package/dist/services/template.service.js.map +0 -1
  170. package/dist/services/tool-call.service.d.ts +0 -19
  171. package/dist/services/tool-call.service.js +0 -1061
  172. package/dist/services/tool-call.service.js.map +0 -1
  173. package/dist/services/tracing.service.d.ts +0 -200
  174. package/dist/services/tracing.service.js +0 -1290
  175. package/dist/services/tracing.service.js.map +0 -1
  176. package/dist/slack/formatters/message.formatter.d.ts +0 -32
  177. package/dist/slack/formatters/message.formatter.js +0 -352
  178. package/dist/slack/formatters/message.formatter.js.map +0 -1
  179. package/dist/slack/services/slack-file-download.service.d.ts +0 -58
  180. package/dist/slack/services/slack-file-download.service.js +0 -558
  181. package/dist/slack/services/slack-file-download.service.js.map +0 -1
  182. package/dist/slack/slack-bot.d.ts +0 -33
  183. package/dist/slack/slack-bot.js +0 -567
  184. package/dist/slack/slack-bot.js.map +0 -1
  185. package/dist/stderr.logger.d.ts +0 -8
  186. package/dist/stderr.logger.js +0 -26
  187. package/dist/stderr.logger.js.map +0 -1
  188. package/dist/types/usage.types.d.ts +0 -107
  189. package/dist/types/usage.types.js +0 -3
  190. package/dist/types/usage.types.js.map +0 -1
  191. package/dist/utils/config-utils.d.ts +0 -15
  192. package/dist/utils/config-utils.js +0 -69
  193. package/dist/utils/config-utils.js.map +0 -1
  194. package/dist/utils/extract-text.d.ts +0 -1
  195. package/dist/utils/extract-text.js +0 -15
  196. package/dist/utils/extract-text.js.map +0 -1
  197. package/dist/utils/mcp-installer.d.ts +0 -20
  198. package/dist/utils/mcp-installer.js +0 -199
  199. package/dist/utils/mcp-installer.js.map +0 -1
  200. package/dist/utils/project-hash.d.ts +0 -6
  201. package/dist/utils/project-hash.js +0 -70
  202. package/dist/utils/project-hash.js.map +0 -1
  203. package/dist/utils/simple-security.d.ts +0 -3
  204. package/dist/utils/simple-security.js +0 -20
  205. package/dist/utils/simple-security.js.map +0 -1
  206. package/dist/utils/stdin-utils.d.ts +0 -6
  207. package/dist/utils/stdin-utils.js +0 -109
  208. package/dist/utils/stdin-utils.js.map +0 -1
  209. package/dist/utils/template-processor.d.ts +0 -4
  210. package/dist/utils/template-processor.js +0 -266
  211. package/dist/utils/template-processor.js.map +0 -1
  212. package/dist/utils/terminal-message-formatter.d.ts +0 -23
  213. package/dist/utils/terminal-message-formatter.js +0 -136
  214. package/dist/utils/terminal-message-formatter.js.map +0 -1
  215. package/dist/version.d.ts +0 -1
  216. package/dist/version.js +0 -17
  217. package/dist/version.js.map +0 -1
  218. package/dist/workspace.service.d.ts +0 -44
  219. package/dist/workspace.service.js +0 -299
  220. package/dist/workspace.service.js.map +0 -1
@@ -1,1290 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
- 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;
22
- return c > 3 && r && Object.defineProperty(target, key, r), r;
23
- };
24
- var __importStar = (this && this.__importStar) || (function () {
25
- var ownKeys = function(o) {
26
- ownKeys = Object.getOwnPropertyNames || function (o) {
27
- var ar = [];
28
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
- return ar;
30
- };
31
- return ownKeys(o);
32
- };
33
- return function (mod) {
34
- if (mod && mod.__esModule) return mod;
35
- var result = {};
36
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
- __setModuleDefault(result, mod);
38
- return result;
39
- };
40
- })();
41
- var __metadata = (this && this.__metadata) || function (k, v) {
42
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
- };
44
- var __param = (this && this.__param) || function (paramIndex, decorator) {
45
- return function (target, key) { decorator(target, key, paramIndex); }
46
- };
47
- var TracingService_1;
48
- Object.defineProperty(exports, "__esModule", { value: true });
49
- exports.TracingService = exports.SpanKind = exports.TaskStatus = void 0;
50
- const common_1 = require("@nestjs/common");
51
- const fs = __importStar(require("fs"));
52
- const os = __importStar(require("os"));
53
- const path = __importStar(require("path"));
54
- const crypto_1 = require("crypto");
55
- const crewx_sdk_1 = require("@sowonai/crewx-sdk");
56
- let Database = null;
57
- try {
58
- Database = require('node-sqlite3-wasm').Database;
59
- }
60
- catch {
61
- }
62
- var TaskStatus;
63
- (function (TaskStatus) {
64
- TaskStatus["PENDING"] = "pending";
65
- TaskStatus["RUNNING"] = "running";
66
- TaskStatus["SUCCESS"] = "success";
67
- TaskStatus["FAILED"] = "failed";
68
- })(TaskStatus || (exports.TaskStatus = TaskStatus = {}));
69
- var SpanKind;
70
- (function (SpanKind) {
71
- SpanKind["INTERNAL"] = "internal";
72
- SpanKind["CLIENT"] = "client";
73
- SpanKind["SERVER"] = "server";
74
- SpanKind["PRODUCER"] = "producer";
75
- SpanKind["CONSUMER"] = "consumer";
76
- })(SpanKind || (exports.SpanKind = SpanKind = {}));
77
- let TracingService = TracingService_1 = class TracingService {
78
- constructor(options) {
79
- this.logger = new common_1.Logger(TracingService_1.name);
80
- this.db = null;
81
- if (options?.dbPath) {
82
- this.dbPath = options.dbPath;
83
- return;
84
- }
85
- this.dbPath = this.resolveDbPath();
86
- }
87
- onModuleInit() {
88
- this.initializeDatabase();
89
- }
90
- onModuleDestroy() {
91
- this.close();
92
- }
93
- initializeDatabase() {
94
- if (!Database) {
95
- this.logger.warn('node-sqlite3-wasm not available — tracing disabled');
96
- return;
97
- }
98
- try {
99
- const dir = path.dirname(this.dbPath);
100
- if (!fs.existsSync(dir)) {
101
- fs.mkdirSync(dir, { recursive: true });
102
- }
103
- this.migrateDbFileName();
104
- this.db = new Database(this.dbPath);
105
- this.db.exec('PRAGMA journal_mode = DELETE');
106
- this.db.exec('PRAGMA foreign_keys = ON');
107
- this.db.exec(`
108
- CREATE TABLE IF NOT EXISTS tasks (
109
- id TEXT PRIMARY KEY,
110
- agent_id TEXT NOT NULL,
111
- user_id TEXT,
112
- prompt TEXT NOT NULL,
113
- mode TEXT NOT NULL CHECK (mode IN ('query', 'execute')),
114
- status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'success', 'failed')),
115
- result TEXT,
116
- error TEXT,
117
- started_at TEXT NOT NULL,
118
- completed_at TEXT,
119
- duration_ms INTEGER,
120
- metadata TEXT,
121
- workspace_id TEXT,
122
- workspace_name TEXT
123
- )
124
- `);
125
- this.db.exec(`
126
- CREATE TABLE IF NOT EXISTS spans (
127
- id TEXT PRIMARY KEY,
128
- task_id TEXT NOT NULL,
129
- parent_span_id TEXT,
130
- name TEXT NOT NULL,
131
- kind TEXT NOT NULL CHECK (kind IN ('internal', 'client', 'server', 'producer', 'consumer')),
132
- status TEXT NOT NULL CHECK (status IN ('ok', 'error')),
133
- started_at TEXT NOT NULL,
134
- completed_at TEXT,
135
- duration_ms INTEGER,
136
- input TEXT,
137
- output TEXT,
138
- error TEXT,
139
- attributes TEXT,
140
- FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
141
- FOREIGN KEY (parent_span_id) REFERENCES spans(id) ON DELETE SET NULL
142
- )
143
- `);
144
- this.db.exec(`
145
- CREATE INDEX IF NOT EXISTS idx_tasks_agent_id ON tasks(agent_id);
146
- CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
147
- CREATE INDEX IF NOT EXISTS idx_tasks_started_at ON tasks(started_at);
148
- CREATE INDEX IF NOT EXISTS idx_spans_task_id ON spans(task_id);
149
- CREATE INDEX IF NOT EXISTS idx_spans_parent_span_id ON spans(parent_span_id);
150
- `);
151
- this.migrateSchemaPhase3a();
152
- this.migrateSchemaPhase4();
153
- this.migrateSchemaPhase5();
154
- this.migrateWorkspaceRenaming();
155
- this.migrateSchemaPhase6();
156
- this.migrateSchemaPhase7();
157
- this.createToolCallsTable();
158
- this.createThreadBoxesTable();
159
- this.logger.log(`Tracing database initialized at ${this.dbPath}`);
160
- }
161
- catch (error) {
162
- this.logger.error(`Failed to initialize tracing database: ${error instanceof Error ? error.message : String(error)}`);
163
- }
164
- }
165
- columnExists(tableName, columnName) {
166
- if (!this.db)
167
- return false;
168
- try {
169
- const result = this.db.all(`PRAGMA table_info(${tableName})`);
170
- return result.some(col => col.name === columnName);
171
- }
172
- catch {
173
- return false;
174
- }
175
- }
176
- migrateSchemaPhase3a() {
177
- if (!this.db)
178
- return;
179
- const newColumns = [
180
- { name: 'trace_id', type: 'TEXT' },
181
- { name: 'parent_task_id', type: 'TEXT' },
182
- { name: 'caller_agent_id', type: 'TEXT' },
183
- { name: 'model', type: 'TEXT' },
184
- { name: 'platform', type: 'TEXT', default: "'cli'" },
185
- { name: 'crewx_version', type: 'TEXT' },
186
- { name: 'input_tokens', type: 'INTEGER', default: '0' },
187
- { name: 'output_tokens', type: 'INTEGER', default: '0' },
188
- { name: 'cost_usd', type: 'REAL', default: '0' },
189
- ];
190
- let migratedCount = 0;
191
- for (const col of newColumns) {
192
- if (!this.columnExists('tasks', col.name)) {
193
- try {
194
- const defaultClause = col.default ? ` DEFAULT ${col.default}` : '';
195
- this.db.exec(`ALTER TABLE tasks ADD COLUMN ${col.name} ${col.type}${defaultClause}`);
196
- migratedCount++;
197
- this.logger.debug(`Added column tasks.${col.name}`);
198
- }
199
- catch (error) {
200
- this.logger.warn(`Failed to add column ${col.name}: ${error instanceof Error ? error.message : String(error)}`);
201
- }
202
- }
203
- }
204
- const indexes = [
205
- { name: 'idx_tasks_trace_id', column: 'trace_id' },
206
- { name: 'idx_tasks_parent_task_id', column: 'parent_task_id' },
207
- { name: 'idx_tasks_crewx_version', column: 'crewx_version' },
208
- ];
209
- for (const idx of indexes) {
210
- try {
211
- this.db.exec(`CREATE INDEX IF NOT EXISTS ${idx.name} ON tasks(${idx.column})`);
212
- }
213
- catch (error) {
214
- this.logger.warn(`Failed to create index ${idx.name}: ${error instanceof Error ? error.message : String(error)}`);
215
- }
216
- }
217
- if (migratedCount > 0) {
218
- this.logger.log(`Phase 3a migration: Added ${migratedCount} new columns to tasks table`);
219
- }
220
- }
221
- migrateSchemaPhase4() {
222
- if (!this.db)
223
- return;
224
- const newColumns = [
225
- { name: 'pid', type: 'INTEGER' },
226
- { name: 'rendered_prompt', type: 'TEXT' },
227
- { name: 'command', type: 'TEXT' },
228
- { name: 'coding_agent_command', type: 'TEXT' },
229
- { name: 'exit_code', type: 'INTEGER' },
230
- { name: 'logs', type: 'TEXT' },
231
- ];
232
- let migratedCount = 0;
233
- for (const col of newColumns) {
234
- if (!this.columnExists('tasks', col.name)) {
235
- try {
236
- this.db.exec(`ALTER TABLE tasks ADD COLUMN ${col.name} ${col.type}`);
237
- migratedCount++;
238
- this.logger.debug(`Added column tasks.${col.name}`);
239
- }
240
- catch (error) {
241
- this.logger.warn(`Failed to add column ${col.name}: ${error instanceof Error ? error.message : String(error)}`);
242
- }
243
- }
244
- }
245
- try {
246
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_pid ON tasks(pid)`);
247
- }
248
- catch (error) {
249
- this.logger.warn(`Failed to create index idx_tasks_pid: ${error instanceof Error ? error.message : String(error)}`);
250
- }
251
- if (migratedCount > 0) {
252
- this.logger.log(`Phase 4 migration: Added ${migratedCount} new columns to tasks table`);
253
- }
254
- }
255
- migrateSchemaPhase5() {
256
- if (!this.db)
257
- return;
258
- const columnsToEnsure = [
259
- { newName: 'workspace_id', oldName: 'project_id', type: 'TEXT' },
260
- { newName: 'workspace_name', oldName: 'project_name', type: 'TEXT' },
261
- ];
262
- let migratedCount = 0;
263
- for (const col of columnsToEnsure) {
264
- if (!this.columnExists('tasks', col.newName) && !this.columnExists('tasks', col.oldName)) {
265
- try {
266
- this.db.exec(`ALTER TABLE tasks ADD COLUMN ${col.newName} ${col.type}`);
267
- migratedCount++;
268
- this.logger.debug(`Added column tasks.${col.newName}`);
269
- }
270
- catch (error) {
271
- this.logger.warn(`Failed to add column ${col.newName}: ${error instanceof Error ? error.message : String(error)}`);
272
- }
273
- }
274
- }
275
- try {
276
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_workspace_id ON tasks(workspace_id)`);
277
- }
278
- catch (error) {
279
- }
280
- if (migratedCount > 0) {
281
- this.logger.log(`Phase 5 migration: Added ${migratedCount} workspace columns to tasks table`);
282
- }
283
- }
284
- migrateWorkspaceRenaming() {
285
- if (!this.db)
286
- return;
287
- const projectsTableExists = !!this.db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='projects'");
288
- const workspacesTableExists = !!this.db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='workspaces'");
289
- if (projectsTableExists && workspacesTableExists) {
290
- try {
291
- if (this.columnExists('projects', 'repo_path')) {
292
- this.db.exec(`
293
- INSERT OR IGNORE INTO workspaces (id, slug, name, workspace_path, is_active, created_at, updated_at)
294
- SELECT id, COALESCE(slug, name, id), COALESCE(name, id), repo_path, 1, COALESCE(created_at, datetime('now')), COALESCE(updated_at, datetime('now'))
295
- FROM projects
296
- `);
297
- }
298
- else if (this.columnExists('projects', 'workspace_path')) {
299
- this.db.exec(`
300
- INSERT OR IGNORE INTO workspaces (id, slug, name, workspace_path, is_active, created_at, updated_at)
301
- SELECT id, COALESCE(slug, name, id), COALESCE(name, id), workspace_path, 1, COALESCE(created_at, datetime('now')), COALESCE(updated_at, datetime('now'))
302
- FROM projects
303
- `);
304
- }
305
- this.db.exec('DROP TABLE projects');
306
- this.logger.log('Workspace renaming migration: merged projects → workspaces, dropped projects table');
307
- }
308
- catch (error) {
309
- this.logger.warn(`Projects merge/drop failed: ${error instanceof Error ? error.message : String(error)}`);
310
- }
311
- }
312
- else if (projectsTableExists && !workspacesTableExists) {
313
- try {
314
- this.db.exec('ALTER TABLE projects RENAME TO workspaces');
315
- if (this.columnExists('workspaces', 'repo_path')) {
316
- this.db.exec('ALTER TABLE workspaces RENAME COLUMN repo_path TO workspace_path');
317
- }
318
- this.logger.log('Workspace renaming migration: projects → workspaces renamed');
319
- }
320
- catch (error) {
321
- this.logger.warn(`Projects table rename failed: ${error instanceof Error ? error.message : String(error)}`);
322
- }
323
- }
324
- if (this.columnExists('tasks', 'project_id') && this.columnExists('tasks', 'workspace_id')) {
325
- try {
326
- this.db.exec(`
327
- UPDATE tasks SET workspace_id = project_id WHERE workspace_id IS NULL AND project_id IS NOT NULL;
328
- UPDATE tasks SET workspace_name = project_name WHERE workspace_name IS NULL AND project_name IS NOT NULL;
329
- `);
330
- this.logger.log('Workspace renaming migration: copied project_id/name → workspace_id/name');
331
- }
332
- catch (error) {
333
- this.logger.warn(`Tasks data copy failed: ${error instanceof Error ? error.message : String(error)}`);
334
- }
335
- }
336
- else if (this.columnExists('tasks', 'project_id') && !this.columnExists('tasks', 'workspace_id')) {
337
- try {
338
- this.db.exec('ALTER TABLE tasks RENAME COLUMN project_id TO workspace_id');
339
- this.db.exec('ALTER TABLE tasks RENAME COLUMN project_name TO workspace_name');
340
- this.logger.log('Workspace renaming migration: tasks columns renamed');
341
- }
342
- catch (error) {
343
- this.logger.warn(`Tasks column renaming failed: ${error instanceof Error ? error.message : String(error)}`);
344
- }
345
- }
346
- if (this.columnExists('tasks', 'project_ref') && this.columnExists('tasks', 'workspace_ref')) {
347
- try {
348
- this.db.exec(`
349
- UPDATE tasks SET workspace_ref = project_ref WHERE workspace_ref IS NULL AND project_ref IS NOT NULL;
350
- `);
351
- }
352
- catch (error) {
353
- this.logger.warn(`Tasks workspace_ref data copy failed: ${error instanceof Error ? error.message : String(error)}`);
354
- }
355
- }
356
- else if (this.columnExists('tasks', 'project_ref') && !this.columnExists('tasks', 'workspace_ref')) {
357
- try {
358
- this.db.exec('ALTER TABLE tasks RENAME COLUMN project_ref TO workspace_ref');
359
- }
360
- catch (error) {
361
- this.logger.warn(`Tasks project_ref renaming failed: ${error instanceof Error ? error.message : String(error)}`);
362
- }
363
- }
364
- if (this.columnExists('threads', 'project_id') && !this.columnExists('threads', 'workspace_id')) {
365
- try {
366
- this.db.exec('ALTER TABLE threads RENAME COLUMN project_id TO workspace_id');
367
- }
368
- catch (error) {
369
- this.logger.warn(`Threads column renaming failed: ${error instanceof Error ? error.message : String(error)}`);
370
- }
371
- }
372
- try {
373
- this.db.exec(`
374
- DROP INDEX IF EXISTS idx_tasks_project_id;
375
- DROP INDEX IF EXISTS idx_tasks_project_ref;
376
- DROP INDEX IF EXISTS idx_threads_project_id;
377
- `);
378
- if (this.columnExists('tasks', 'workspace_id')) {
379
- this.db.exec('CREATE INDEX IF NOT EXISTS idx_tasks_workspace_id ON tasks(workspace_id)');
380
- }
381
- if (this.columnExists('tasks', 'workspace_ref')) {
382
- this.db.exec('CREATE INDEX IF NOT EXISTS idx_tasks_workspace_ref ON tasks(workspace_ref)');
383
- }
384
- if (this.columnExists('threads', 'workspace_id')) {
385
- this.db.exec('CREATE INDEX IF NOT EXISTS idx_threads_workspace_id ON threads(workspace_id)');
386
- }
387
- }
388
- catch (error) {
389
- this.logger.warn(`Index cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
390
- }
391
- if (workspacesTableExists || !projectsTableExists) {
392
- try {
393
- const hashSlugs = this.db.all('SELECT id, name, slug FROM workspaces WHERE slug = id');
394
- for (const row of hashSlugs) {
395
- try {
396
- this.db.run('UPDATE workspaces SET slug = ? WHERE id = ?', [row.name, row.id]);
397
- }
398
- catch {
399
- this.logger.warn(`Could not update slug for workspace ${row.name}, keeping hash-based slug`);
400
- }
401
- }
402
- }
403
- catch {
404
- }
405
- }
406
- }
407
- migrateSchemaPhase6() {
408
- if (!this.db)
409
- return;
410
- try {
411
- this.db.exec(`
412
- CREATE TABLE IF NOT EXISTS workspaces (
413
- id TEXT PRIMARY KEY,
414
- slug TEXT NOT NULL UNIQUE,
415
- name TEXT NOT NULL,
416
- workspace_path TEXT,
417
- is_active INTEGER NOT NULL DEFAULT 1,
418
- created_at TEXT NOT NULL,
419
- updated_at TEXT NOT NULL
420
- )
421
- `);
422
- }
423
- catch (error) {
424
- this.logger.warn(`Failed to create workspaces table: ${error instanceof Error ? error.message : String(error)}`);
425
- }
426
- try {
427
- this.db.exec(`
428
- CREATE TABLE IF NOT EXISTS threads (
429
- id TEXT PRIMARY KEY,
430
- workspace_id TEXT,
431
- platform TEXT NOT NULL DEFAULT 'cli',
432
- title TEXT,
433
- first_message TEXT,
434
- last_message TEXT,
435
- message_count INTEGER NOT NULL DEFAULT 0,
436
- created_at TEXT NOT NULL,
437
- updated_at TEXT NOT NULL,
438
- metadata TEXT,
439
- FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
440
- )
441
- `);
442
- }
443
- catch (error) {
444
- this.logger.warn(`Failed to create threads table: ${error instanceof Error ? error.message : String(error)}`);
445
- }
446
- const columnsToEnsure = [
447
- { newName: 'thread_id', oldName: 'thread_id', type: 'TEXT' },
448
- { newName: 'workspace_ref', oldName: 'project_ref', type: 'TEXT' },
449
- ];
450
- let migratedCount = 0;
451
- for (const col of columnsToEnsure) {
452
- if (!this.columnExists('tasks', col.newName) && !this.columnExists('tasks', col.oldName)) {
453
- try {
454
- this.db.exec(`ALTER TABLE tasks ADD COLUMN ${col.newName} ${col.type}`);
455
- migratedCount++;
456
- this.logger.debug(`Added column tasks.${col.newName}`);
457
- }
458
- catch (error) {
459
- this.logger.warn(`Failed to add column ${col.newName}: ${error instanceof Error ? error.message : String(error)}`);
460
- }
461
- }
462
- }
463
- try {
464
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_thread_id ON tasks(thread_id)`);
465
- }
466
- catch (error) {
467
- this.logger.warn(`Failed to create index idx_tasks_thread_id: ${error instanceof Error ? error.message : String(error)}`);
468
- }
469
- try {
470
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_threads_workspace_id ON threads(workspace_id)`);
471
- }
472
- catch (error) {
473
- this.logger.warn(`Failed to create index idx_threads_workspace_id: ${error instanceof Error ? error.message : String(error)}`);
474
- }
475
- try {
476
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_tasks_workspace_ref ON tasks(workspace_ref)`);
477
- }
478
- catch (error) {
479
- this.logger.warn(`Failed to create index idx_tasks_workspace_ref: ${error instanceof Error ? error.message : String(error)}`);
480
- }
481
- if (migratedCount > 0) {
482
- this.logger.log(`Phase 6 migration: Added ${migratedCount} new columns to tasks table`);
483
- }
484
- }
485
- migrateSchemaPhase7() {
486
- if (!this.db)
487
- return;
488
- try {
489
- this.db.exec(`
490
- CREATE TABLE IF NOT EXISTS request_logs (
491
- id TEXT PRIMARY KEY,
492
- path TEXT NOT NULL,
493
- method TEXT NOT NULL,
494
- status_code INTEGER NOT NULL,
495
- duration_ms INTEGER NOT NULL,
496
- ip TEXT,
497
- request_headers TEXT,
498
- response_headers TEXT,
499
- request_body TEXT,
500
- response_body TEXT,
501
- query TEXT,
502
- user_id TEXT,
503
- project_id TEXT,
504
- partition_key TEXT NOT NULL,
505
- timestamp TEXT NOT NULL DEFAULT (datetime('now')),
506
- metadata TEXT
507
- )
508
- `);
509
- }
510
- catch (error) {
511
- this.logger.warn(`Failed to create request_logs table: ${error instanceof Error ? error.message : String(error)}`);
512
- return;
513
- }
514
- const indexes = [
515
- { name: 'idx_request_logs_timestamp', sql: 'CREATE INDEX IF NOT EXISTS idx_request_logs_timestamp ON request_logs(timestamp DESC)' },
516
- { name: 'idx_request_logs_path', sql: 'CREATE INDEX IF NOT EXISTS idx_request_logs_path ON request_logs(path)' },
517
- { name: 'idx_request_logs_status_code', sql: 'CREATE INDEX IF NOT EXISTS idx_request_logs_status_code ON request_logs(status_code)' },
518
- { name: 'idx_request_logs_partition_key', sql: 'CREATE INDEX IF NOT EXISTS idx_request_logs_partition_key ON request_logs(partition_key)' },
519
- ];
520
- for (const idx of indexes) {
521
- try {
522
- this.db.exec(idx.sql);
523
- }
524
- catch (error) {
525
- this.logger.warn(`Failed to create index ${idx.name}: ${error instanceof Error ? error.message : String(error)}`);
526
- }
527
- }
528
- this.logger.log('Phase 7 migration: Created request_logs table with 4 indexes');
529
- }
530
- createToolCallsTable() {
531
- if (!this.db)
532
- return;
533
- try {
534
- const tableExists = this.db.get("SELECT name FROM sqlite_master WHERE type='table' AND name='tool_calls'");
535
- if (!tableExists) {
536
- this.db.exec(`
537
- CREATE TABLE tool_calls (
538
- id TEXT PRIMARY KEY,
539
- task_id TEXT NOT NULL,
540
- session_id TEXT,
541
- tool_name TEXT NOT NULL,
542
- files TEXT,
543
- input TEXT,
544
- output TEXT,
545
- duration_ms INTEGER,
546
- timestamp TEXT NOT NULL,
547
- FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
548
- )
549
- `);
550
- this.db.exec(`
551
- CREATE INDEX IF NOT EXISTS idx_tool_calls_task_id ON tool_calls(task_id);
552
- CREATE INDEX IF NOT EXISTS idx_tool_calls_tool_name ON tool_calls(tool_name);
553
- CREATE INDEX IF NOT EXISTS idx_tool_calls_timestamp ON tool_calls(timestamp);
554
- `);
555
- this.logger.log('Issue #104: Created tool_calls table for hooks observability');
556
- }
557
- }
558
- catch (error) {
559
- this.logger.warn(`Failed to create tool_calls table: ${error instanceof Error ? error.message : String(error)}`);
560
- }
561
- }
562
- createThreadBoxesTable() {
563
- if (!this.db)
564
- return;
565
- try {
566
- this.db.exec(`
567
- CREATE TABLE IF NOT EXISTS thread_boxes (
568
- id TEXT PRIMARY KEY,
569
- thread_id TEXT NOT NULL,
570
- seq INTEGER NOT NULL,
571
-
572
- first_task_id TEXT NOT NULL,
573
- mid_task_id TEXT NOT NULL,
574
- last_task_id TEXT NOT NULL,
575
- task_count INTEGER NOT NULL,
576
-
577
- summary TEXT,
578
-
579
- source_tokens INTEGER NOT NULL,
580
- summary_tokens INTEGER,
581
-
582
- created_at TEXT NOT NULL,
583
-
584
- FOREIGN KEY (thread_id) REFERENCES threads(id) ON DELETE CASCADE,
585
- UNIQUE (thread_id, seq)
586
- );
587
-
588
- CREATE INDEX IF NOT EXISTS idx_thread_boxes_thread_id ON thread_boxes(thread_id);
589
- CREATE INDEX IF NOT EXISTS idx_thread_boxes_seq ON thread_boxes(thread_id, seq DESC);
590
- `);
591
- this.logger.log('Phase 8: Created thread_boxes table with indexes');
592
- }
593
- catch (error) {
594
- this.logger.warn(`Failed to create thread_boxes table: ${error instanceof Error ? error.message : String(error)}`);
595
- }
596
- }
597
- close() {
598
- if (this.db) {
599
- this.db.close();
600
- this.db = null;
601
- this.logger.log('Tracing database connection closed');
602
- }
603
- }
604
- generateId() {
605
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
606
- const r = (Math.random() * 16) | 0;
607
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
608
- return v.toString(16);
609
- });
610
- }
611
- now() {
612
- return new Date().toISOString();
613
- }
614
- estimateTokens(text) {
615
- if (!text)
616
- return 0;
617
- return Math.ceil(text.length * 0.4);
618
- }
619
- createTask(input) {
620
- if (!this.db) {
621
- return null;
622
- }
623
- const id = input.id ?? this.generateId();
624
- const now = this.now();
625
- const { workspaceId, workspaceName } = this.resolveWorkspaceContext(input.workspace_id, input.workspace_name);
626
- const metadata = input.metadata ? { ...input.metadata } : {};
627
- const MAX_RENDERED_PROMPT_SIZE = 10 * 1024 * 1024;
628
- let renderedPrompt = input.rendered_prompt ?? null;
629
- if (renderedPrompt && renderedPrompt.length > MAX_RENDERED_PROMPT_SIZE) {
630
- renderedPrompt = renderedPrompt.slice(0, MAX_RENDERED_PROMPT_SIZE) + '\n[TRUNCATED]';
631
- this.logger.warn(`Rendered prompt for task ${id} truncated to 10MB limit`);
632
- }
633
- try {
634
- this.db.run(`
635
- INSERT INTO tasks (
636
- id, agent_id, user_id, prompt, mode, status, started_at, metadata,
637
- trace_id, parent_task_id, caller_agent_id, model, platform, crewx_version,
638
- input_tokens, output_tokens, cost_usd,
639
- pid, rendered_prompt, command, coding_agent_command, exit_code, logs,
640
- workspace_id, workspace_name,
641
- thread_id, workspace_ref
642
- )
643
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
644
- `, [
645
- id,
646
- input.agent_id,
647
- input.user_id ?? null,
648
- input.prompt,
649
- input.mode,
650
- TaskStatus.RUNNING,
651
- now,
652
- Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,
653
- input.trace_id ?? null,
654
- input.parent_task_id ?? null,
655
- input.caller_agent_id ?? null,
656
- input.model ?? null,
657
- input.platform ?? 'cli',
658
- input.crewx_version ?? null,
659
- input.input_tokens ?? 0,
660
- input.output_tokens ?? 0,
661
- input.cost_usd ?? 0,
662
- input.pid ?? null,
663
- renderedPrompt,
664
- input.command ?? null,
665
- input.coding_agent_command ?? null,
666
- input.exit_code ?? null,
667
- '[]',
668
- workspaceId,
669
- workspaceName,
670
- input.thread_id ?? null,
671
- input.workspace_ref ?? null,
672
- ]);
673
- this.logger.debug(`Task created: ${id} for agent ${input.agent_id} (model: ${input.model ?? 'default'})`);
674
- return id;
675
- }
676
- catch (error) {
677
- this.logger.error(`Failed to create task: ${error instanceof Error ? error.message : String(error)}`);
678
- return null;
679
- }
680
- }
681
- extractUsageFromLogs(logs) {
682
- if (!logs || logs.length === 0)
683
- return null;
684
- let found = null;
685
- for (const entry of logs) {
686
- if (entry.level !== 'stdout')
687
- continue;
688
- let parsed;
689
- try {
690
- parsed = JSON.parse(entry.message);
691
- }
692
- catch {
693
- continue;
694
- }
695
- if (parsed.type === 'result' &&
696
- parsed.subtype === 'success' &&
697
- parsed.usage &&
698
- typeof parsed.usage === 'object') {
699
- const u = parsed.usage;
700
- if (typeof u.input_tokens === 'number' && typeof u.output_tokens === 'number') {
701
- found = { inputTokens: u.input_tokens, outputTokens: u.output_tokens };
702
- }
703
- }
704
- if (parsed.type === 'turn.completed' &&
705
- parsed.usage &&
706
- typeof parsed.usage === 'object') {
707
- const u = parsed.usage;
708
- if (typeof u.input_tokens === 'number' && typeof u.output_tokens === 'number') {
709
- found = { inputTokens: u.input_tokens, outputTokens: u.output_tokens };
710
- }
711
- }
712
- }
713
- return found;
714
- }
715
- completeTask(taskId, result, exitCode, usage) {
716
- if (!this.db) {
717
- return false;
718
- }
719
- try {
720
- const now = this.now();
721
- const safeResult = typeof result === 'string'
722
- ? result
723
- : Array.isArray(result)
724
- ? result.filter((c) => c?.text).map((c) => c.text).join('\n')
725
- : result != null ? String(result) : null;
726
- const task = this.getTask(taskId);
727
- const model = task?.model ?? null;
728
- const resolvedUsage = usage ?? this.extractUsageFromLogs(task?.logs);
729
- let inputTokens = 0;
730
- let outputTokens = 0;
731
- let costUsd = 0;
732
- if (resolvedUsage) {
733
- inputTokens = resolvedUsage.inputTokens;
734
- outputTokens = resolvedUsage.outputTokens;
735
- costUsd = (0, crewx_sdk_1.calculateCost)(inputTokens, outputTokens, model);
736
- }
737
- const info = this.db.run(`
738
- UPDATE tasks
739
- SET status = ?, result = ?, completed_at = ?,
740
- duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER),
741
- input_tokens = ?,
742
- output_tokens = ?,
743
- cost_usd = ?,
744
- exit_code = ?
745
- WHERE id = ?
746
- `, [
747
- TaskStatus.SUCCESS,
748
- safeResult ?? null,
749
- now,
750
- now,
751
- inputTokens,
752
- outputTokens,
753
- costUsd,
754
- exitCode ?? null,
755
- taskId,
756
- ]);
757
- return info.changes > 0;
758
- }
759
- catch (error) {
760
- this.logger.error(`Failed to complete task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
761
- return false;
762
- }
763
- }
764
- failTask(taskId, error, exitCode, usage) {
765
- if (!this.db) {
766
- return false;
767
- }
768
- try {
769
- const now = this.now();
770
- const task = this.getTask(taskId);
771
- const model = task?.model ?? null;
772
- const resolvedUsage = usage ?? this.extractUsageFromLogs(task?.logs);
773
- let inputTokens = 0;
774
- let outputTokens = 0;
775
- let costUsd = 0;
776
- if (resolvedUsage) {
777
- inputTokens = resolvedUsage.inputTokens;
778
- outputTokens = resolvedUsage.outputTokens;
779
- costUsd = (0, crewx_sdk_1.calculateCost)(inputTokens, outputTokens, model);
780
- }
781
- const info = this.db.run(`
782
- UPDATE tasks
783
- SET status = ?, error = ?, completed_at = ?,
784
- duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER),
785
- input_tokens = ?,
786
- output_tokens = ?,
787
- cost_usd = ?,
788
- exit_code = ?
789
- WHERE id = ?
790
- `, [
791
- TaskStatus.FAILED,
792
- error,
793
- now,
794
- now,
795
- inputTokens,
796
- outputTokens,
797
- costUsd,
798
- exitCode ?? null,
799
- taskId,
800
- ]);
801
- return info.changes > 0;
802
- }
803
- catch (error) {
804
- this.logger.error(`Failed to fail task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
805
- return false;
806
- }
807
- }
808
- updateTaskPid(taskId, pid) {
809
- if (!this.db) {
810
- return false;
811
- }
812
- try {
813
- const info = this.db.run(`
814
- UPDATE tasks
815
- SET pid = ?
816
- WHERE id = ?
817
- `, [pid, taskId]);
818
- if (info.changes > 0) {
819
- this.logger.debug(`Updated task ${taskId} with PID ${pid}`);
820
- }
821
- return info.changes > 0;
822
- }
823
- catch (error) {
824
- this.logger.error(`Failed to update PID for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
825
- return false;
826
- }
827
- }
828
- updateTaskCodingAgentCommand(taskId, command) {
829
- if (!this.db) {
830
- return false;
831
- }
832
- try {
833
- const info = this.db.run(`
834
- UPDATE tasks
835
- SET coding_agent_command = ?
836
- WHERE id = ?
837
- `, [command, taskId]);
838
- if (info.changes > 0) {
839
- this.logger.debug(`Updated task ${taskId} with coding agent command`);
840
- }
841
- return info.changes > 0;
842
- }
843
- catch (error) {
844
- this.logger.error(`Failed to update coding agent command for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
845
- return false;
846
- }
847
- }
848
- updateTaskRenderedPrompt(taskId, renderedPrompt) {
849
- if (!this.db) {
850
- return false;
851
- }
852
- try {
853
- const MAX_RENDERED_PROMPT_SIZE = 10 * 1024 * 1024;
854
- let truncatedPrompt = renderedPrompt;
855
- if (renderedPrompt.length > MAX_RENDERED_PROMPT_SIZE) {
856
- truncatedPrompt = renderedPrompt.slice(0, MAX_RENDERED_PROMPT_SIZE) + '\n[TRUNCATED]';
857
- this.logger.warn(`Rendered prompt for task ${taskId} truncated to 10MB limit`);
858
- }
859
- const info = this.db.run(`
860
- UPDATE tasks
861
- SET rendered_prompt = ?
862
- WHERE id = ?
863
- `, [truncatedPrompt, taskId]);
864
- return info.changes > 0;
865
- }
866
- catch (error) {
867
- this.logger.error(`Failed to update rendered_prompt for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
868
- return false;
869
- }
870
- }
871
- appendTaskLog(taskId, entry) {
872
- if (!this.db) {
873
- return false;
874
- }
875
- try {
876
- const row = this.db.get('SELECT logs FROM tasks WHERE id = ?', [taskId]);
877
- if (!row) {
878
- this.logger.warn(`Task ${taskId} not found when appending log`);
879
- return false;
880
- }
881
- let logs = [];
882
- if (row.logs) {
883
- try {
884
- logs = JSON.parse(row.logs);
885
- }
886
- catch {
887
- logs = [];
888
- }
889
- }
890
- logs.push(entry);
891
- const info = this.db.run('UPDATE tasks SET logs = ? WHERE id = ?', [JSON.stringify(logs), taskId]);
892
- return info.changes > 0;
893
- }
894
- catch (error) {
895
- this.logger.error(`Failed to append log for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
896
- return false;
897
- }
898
- }
899
- getTask(taskId) {
900
- if (!this.db) {
901
- return null;
902
- }
903
- try {
904
- const row = this.db.get('SELECT * FROM tasks WHERE id = ?', [taskId]);
905
- if (!row) {
906
- return null;
907
- }
908
- return {
909
- ...row,
910
- metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
911
- logs: row.logs ? JSON.parse(row.logs) : undefined,
912
- };
913
- }
914
- catch (error) {
915
- this.logger.error(`Failed to get task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
916
- return null;
917
- }
918
- }
919
- listTasks(options) {
920
- if (!this.db) {
921
- return [];
922
- }
923
- const limit = options?.limit ?? 20;
924
- const offset = options?.offset ?? 0;
925
- try {
926
- let query = 'SELECT * FROM tasks';
927
- const params = [];
928
- if (options?.agentId) {
929
- query += ' WHERE agent_id = ?';
930
- params.push(options.agentId);
931
- }
932
- query += ' ORDER BY started_at DESC LIMIT ? OFFSET ?';
933
- params.push(limit, offset);
934
- const rows = this.db.all(query, params);
935
- return rows.map((row) => ({
936
- ...row,
937
- metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
938
- logs: row.logs ? JSON.parse(row.logs) : undefined,
939
- }));
940
- }
941
- catch (error) {
942
- this.logger.error(`Failed to list tasks: ${error instanceof Error ? error.message : String(error)}`);
943
- return [];
944
- }
945
- }
946
- createSpan(input) {
947
- if (!this.db) {
948
- return null;
949
- }
950
- const id = this.generateId();
951
- const now = this.now();
952
- try {
953
- this.db.run(`
954
- INSERT INTO spans (id, task_id, parent_span_id, name, kind, status, started_at, input, attributes)
955
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
956
- `, [
957
- id,
958
- input.task_id,
959
- input.parent_span_id ?? null,
960
- input.name,
961
- input.kind ?? SpanKind.INTERNAL,
962
- 'ok',
963
- now,
964
- input.input ?? null,
965
- input.attributes ? JSON.stringify(input.attributes) : null,
966
- ]);
967
- this.logger.debug(`Span created: ${id} for task ${input.task_id}`);
968
- return id;
969
- }
970
- catch (error) {
971
- this.logger.error(`Failed to create span: ${error instanceof Error ? error.message : String(error)}`);
972
- return null;
973
- }
974
- }
975
- completeSpan(spanId, output) {
976
- if (!this.db) {
977
- return false;
978
- }
979
- try {
980
- const now = this.now();
981
- const info = this.db.run(`
982
- UPDATE spans
983
- SET status = 'ok', output = ?, completed_at = ?,
984
- duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
985
- WHERE id = ?
986
- `, [output ?? null, now, now, spanId]);
987
- return info.changes > 0;
988
- }
989
- catch (error) {
990
- this.logger.error(`Failed to complete span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
991
- return false;
992
- }
993
- }
994
- failSpan(spanId, error) {
995
- if (!this.db) {
996
- return false;
997
- }
998
- try {
999
- const now = this.now();
1000
- const info = this.db.run(`
1001
- UPDATE spans
1002
- SET status = 'error', error = ?, completed_at = ?,
1003
- duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
1004
- WHERE id = ?
1005
- `, [error, now, now, spanId]);
1006
- return info.changes > 0;
1007
- }
1008
- catch (error) {
1009
- this.logger.error(`Failed to fail span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
1010
- return false;
1011
- }
1012
- }
1013
- getSpansForTask(taskId) {
1014
- if (!this.db) {
1015
- return [];
1016
- }
1017
- try {
1018
- const rows = this.db.all('SELECT * FROM spans WHERE task_id = ? ORDER BY started_at ASC', [taskId]);
1019
- return rows.map((row) => ({
1020
- ...row,
1021
- attributes: row.attributes ? JSON.parse(row.attributes) : undefined,
1022
- }));
1023
- }
1024
- catch (error) {
1025
- this.logger.error(`Failed to get spans for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
1026
- return [];
1027
- }
1028
- }
1029
- getSpan(spanId) {
1030
- if (!this.db) {
1031
- return null;
1032
- }
1033
- try {
1034
- const row = this.db.get('SELECT * FROM spans WHERE id = ?', [spanId]);
1035
- if (!row) {
1036
- return null;
1037
- }
1038
- return {
1039
- ...row,
1040
- attributes: row.attributes ? JSON.parse(row.attributes) : undefined,
1041
- };
1042
- }
1043
- catch (error) {
1044
- this.logger.error(`Failed to get span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
1045
- return null;
1046
- }
1047
- }
1048
- logToolCall(input) {
1049
- if (!this.db) {
1050
- return null;
1051
- }
1052
- const id = this.generateId();
1053
- const timestamp = this.now();
1054
- try {
1055
- this.db.run(`
1056
- INSERT INTO tool_calls (
1057
- id, task_id, session_id, tool_name, files, input, output, duration_ms, timestamp
1058
- )
1059
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1060
- `, [
1061
- id,
1062
- input.task_id,
1063
- input.session_id ?? null,
1064
- input.tool_name,
1065
- input.files ? JSON.stringify(input.files) : null,
1066
- input.input ? JSON.stringify(input.input) : null,
1067
- input.output ? JSON.stringify(input.output) : null,
1068
- input.duration_ms ?? null,
1069
- timestamp,
1070
- ]);
1071
- this.logger.debug(`Tool call logged: ${input.tool_name} for task ${input.task_id}`);
1072
- return id;
1073
- }
1074
- catch (error) {
1075
- this.logger.error(`Failed to log tool call: ${error instanceof Error ? error.message : String(error)}`);
1076
- return null;
1077
- }
1078
- }
1079
- getToolCallsForTask(taskId) {
1080
- if (!this.db) {
1081
- return [];
1082
- }
1083
- try {
1084
- const rows = this.db.all('SELECT * FROM tool_calls WHERE task_id = ? ORDER BY timestamp ASC', [taskId]);
1085
- return rows.map((row) => ({
1086
- ...row,
1087
- files: row.files ? JSON.parse(row.files) : undefined,
1088
- input: row.input ? JSON.parse(row.input) : undefined,
1089
- output: row.output ? JSON.parse(row.output) : undefined,
1090
- }));
1091
- }
1092
- catch (error) {
1093
- this.logger.error(`Failed to get tool calls for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
1094
- return [];
1095
- }
1096
- }
1097
- getToolCallStats(taskId) {
1098
- if (!this.db) {
1099
- return null;
1100
- }
1101
- try {
1102
- const stats = this.db.get(`
1103
- SELECT
1104
- COUNT(*) as total_calls,
1105
- COUNT(DISTINCT tool_name) as unique_tools
1106
- FROM tool_calls
1107
- WHERE task_id = ?
1108
- `, [taskId]);
1109
- const tools = this.db.all(`
1110
- SELECT DISTINCT tool_name FROM tool_calls WHERE task_id = ?
1111
- `, [taskId]);
1112
- return {
1113
- totalCalls: stats.total_calls,
1114
- uniqueTools: stats.unique_tools,
1115
- toolsUsed: tools.map(t => t.tool_name),
1116
- };
1117
- }
1118
- catch (error) {
1119
- this.logger.error(`Failed to get tool call stats for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
1120
- return null;
1121
- }
1122
- }
1123
- cleanupOldTasks(daysToKeep = 30) {
1124
- if (!this.db) {
1125
- return 0;
1126
- }
1127
- try {
1128
- const cutoffDate = new Date();
1129
- cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
1130
- const cutoff = cutoffDate.toISOString();
1131
- const info = this.db.run('DELETE FROM tasks WHERE started_at < ?', [cutoff]);
1132
- if (info.changes > 0) {
1133
- this.logger.log(`Cleaned up ${info.changes} old tasks`);
1134
- }
1135
- return info.changes;
1136
- }
1137
- catch (error) {
1138
- this.logger.error(`Failed to cleanup old tasks: ${error instanceof Error ? error.message : String(error)}`);
1139
- return 0;
1140
- }
1141
- }
1142
- getStats() {
1143
- if (!this.db) {
1144
- return null;
1145
- }
1146
- try {
1147
- const taskCount = this.db.get('SELECT COUNT(*) as count FROM tasks').count;
1148
- const spanCount = this.db.get('SELECT COUNT(*) as count FROM spans').count;
1149
- let toolCallCount = 0;
1150
- try {
1151
- toolCallCount = this.db.get('SELECT COUNT(*) as count FROM tool_calls').count;
1152
- }
1153
- catch {
1154
- }
1155
- let dbSizeBytes = 0;
1156
- if (fs.existsSync(this.dbPath)) {
1157
- const stats = fs.statSync(this.dbPath);
1158
- dbSizeBytes = stats.size;
1159
- }
1160
- return { taskCount, spanCount, toolCallCount, dbSizeBytes };
1161
- }
1162
- catch (error) {
1163
- this.logger.error(`Failed to get stats: ${error instanceof Error ? error.message : String(error)}`);
1164
- return null;
1165
- }
1166
- }
1167
- migrateDbFileName() {
1168
- const dirsToMigrate = new Set([
1169
- path.join(os.homedir(), '.crewx'),
1170
- path.join(process.cwd(), '.crewx'),
1171
- ]);
1172
- for (const dir of dirsToMigrate) {
1173
- const oldPath = path.join(dir, 'traces.db');
1174
- const newPath = path.join(dir, 'crewx.db');
1175
- if (fs.existsSync(oldPath) && !fs.existsSync(newPath)) {
1176
- try {
1177
- fs.renameSync(oldPath, newPath);
1178
- this.logger.log(`Migrated ${oldPath} → ${newPath}`);
1179
- }
1180
- catch (error) {
1181
- this.logger.warn(`Failed to migrate ${oldPath}: ${error instanceof Error ? error.message : String(error)}`);
1182
- }
1183
- }
1184
- }
1185
- }
1186
- resolveDbPath() {
1187
- if (process.env.CREWX_TRACES_DB || process.env.CREWX_DB) {
1188
- return process.env.CREWX_TRACES_DB || process.env.CREWX_DB;
1189
- }
1190
- return path.join(os.homedir(), '.crewx', 'crewx.db');
1191
- }
1192
- resolveWorkspaceIdForPath(dirPath) {
1193
- const workspacePath = path.resolve(dirPath);
1194
- const id = this.hashWorkspaceId(workspacePath);
1195
- const name = this.resolveWorkspaceName(workspacePath);
1196
- const slug = this.resolveWorkspaceSlug(id);
1197
- this.ensureWorkspaceRow(id, slug, name, workspacePath);
1198
- return { workspaceId: id, workspaceName: name };
1199
- }
1200
- resolveWorkspaceContext(workspaceId, workspaceName) {
1201
- const workspacePath = path.resolve(process.cwd());
1202
- const id = workspaceId ?? this.hashWorkspaceId(workspacePath);
1203
- const name = workspaceName ?? this.resolveWorkspaceName(workspacePath);
1204
- const slug = this.resolveWorkspaceSlug(id);
1205
- this.ensureWorkspaceRow(id, slug, name, workspacePath);
1206
- return { workspaceId: id, workspaceName: name };
1207
- }
1208
- resolveWorkspaceSlug(workspaceId) {
1209
- const cwd = process.cwd();
1210
- let slug = path.basename(cwd);
1211
- if (this.db) {
1212
- try {
1213
- const existing = this.db.get('SELECT id FROM workspaces WHERE slug = ? AND id != ?', [slug, workspaceId]);
1214
- if (existing) {
1215
- const parent = path.basename(path.dirname(cwd));
1216
- slug = `${parent}/${slug}`;
1217
- }
1218
- }
1219
- catch {
1220
- }
1221
- }
1222
- return slug;
1223
- }
1224
- ensureWorkspaceRow(id, slug, name, workspacePath) {
1225
- if (!this.db)
1226
- return;
1227
- const now = this.now();
1228
- try {
1229
- this.db.run(`
1230
- INSERT OR IGNORE INTO workspaces (id, slug, name, workspace_path, is_active, created_at, updated_at)
1231
- VALUES (?, ?, ?, ?, 1, ?, ?)
1232
- `, [id, slug, name, workspacePath, now, now]);
1233
- this.db.run(`
1234
- UPDATE workspaces SET workspace_path = ?, updated_at = ? WHERE id = ? AND workspace_path IS NULL
1235
- `, [workspacePath, now, id]);
1236
- }
1237
- catch (error) {
1238
- this.logger.warn(`Failed to ensure workspace row: ${error instanceof Error ? error.message : String(error)}`);
1239
- }
1240
- }
1241
- hashWorkspaceId(workspacePath) {
1242
- const normalizedPath = this.normalizeWorkspacePath(workspacePath);
1243
- return (0, crypto_1.createHash)('sha256').update(normalizedPath).digest('hex');
1244
- }
1245
- normalizeWorkspacePath(workspacePath) {
1246
- let resolved = path.resolve(workspacePath);
1247
- if (process.platform === 'win32') {
1248
- resolved = resolved.replace(/\\/g, '/');
1249
- resolved = resolved.replace(/^([A-Z]):/, (match, drive) => `${drive.toLowerCase()}:`);
1250
- }
1251
- if (resolved.length > 1 && !/^[a-zA-Z]:\/$/.test(resolved)) {
1252
- resolved = resolved.replace(/\/+$/, '');
1253
- }
1254
- return resolved;
1255
- }
1256
- resolveWorkspaceName(workspacePath) {
1257
- const gitConfigPath = path.join(workspacePath, '.git', 'config');
1258
- if (fs.existsSync(gitConfigPath)) {
1259
- try {
1260
- const config = fs.readFileSync(gitConfigPath, 'utf-8');
1261
- const match = config.match(/\[remote\s+"[^"]+"\][^\[]*?url\s*=\s*(.+)/m);
1262
- if (match && match[1]) {
1263
- return this.extractRepoName(match[1].trim());
1264
- }
1265
- }
1266
- catch {
1267
- }
1268
- }
1269
- return path.basename(workspacePath);
1270
- }
1271
- extractRepoName(remoteUrl) {
1272
- const cleaned = remoteUrl.replace(/\.git$/i, '');
1273
- const parts = cleaned.split(/[/:]/).filter(Boolean);
1274
- return parts[parts.length - 1] || 'unknown';
1275
- }
1276
- isEnabled() {
1277
- return this.db !== null;
1278
- }
1279
- getDbPath() {
1280
- return this.dbPath;
1281
- }
1282
- };
1283
- exports.TracingService = TracingService;
1284
- exports.TracingService = TracingService = TracingService_1 = __decorate([
1285
- (0, common_1.Injectable)(),
1286
- __param(0, (0, common_1.Optional)()),
1287
- __param(0, (0, common_1.Inject)('TRACING_OPTIONS')),
1288
- __metadata("design:paramtypes", [Object])
1289
- ], TracingService);
1290
- //# sourceMappingURL=tracing.service.js.map