@sowonai/crewx-cli 0.8.0-rc.1 → 0.8.0-rc.11

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 (40) hide show
  1. package/README.md +2 -2
  2. package/dist/app.module.js +4 -0
  3. package/dist/app.module.js.map +1 -1
  4. package/dist/cli/cli.handler.js +4 -0
  5. package/dist/cli/cli.handler.js.map +1 -1
  6. package/dist/cli/init.handler.js +1 -1
  7. package/dist/cli/skill.handler.d.ts +2 -0
  8. package/dist/cli/skill.handler.js +143 -0
  9. package/dist/cli/skill.handler.js.map +1 -0
  10. package/dist/cli-options.d.ts +4 -0
  11. package/dist/cli-options.js +19 -0
  12. package/dist/cli-options.js.map +1 -1
  13. package/dist/crewx.tool.d.ts +3 -1
  14. package/dist/crewx.tool.js +42 -2
  15. package/dist/crewx.tool.js.map +1 -1
  16. package/dist/main.js +6 -1
  17. package/dist/main.js.map +1 -1
  18. package/dist/providers/dynamic-provider.factory.js +2 -0
  19. package/dist/providers/dynamic-provider.factory.js.map +1 -1
  20. package/dist/services/skill.service.d.ts +66 -0
  21. package/dist/services/skill.service.js +584 -0
  22. package/dist/services/skill.service.js.map +1 -0
  23. package/dist/services/skill.service.spec.d.ts +1 -0
  24. package/dist/services/skill.service.spec.js +35 -0
  25. package/dist/services/skill.service.spec.js.map +1 -0
  26. package/dist/services/tracing.service.d.ts +77 -73
  27. package/dist/services/tracing.service.js +250 -338
  28. package/dist/services/tracing.service.js.map +1 -1
  29. package/dist/slack/formatters/message.formatter.d.ts +4 -0
  30. package/dist/slack/formatters/message.formatter.js +93 -49
  31. package/dist/slack/formatters/message.formatter.js.map +1 -1
  32. package/dist/utils/template-processor.js +18 -1
  33. package/dist/utils/template-processor.js.map +1 -1
  34. package/package.json +4 -2
  35. package/templates/agents/default.yaml +2 -0
  36. package/templates/documents/conversation-history-default.hbs +17 -0
  37. package/dist/services/memory.service.d.ts +0 -102
  38. package/dist/services/memory.service.js +0 -707
  39. package/dist/services/memory.service.js.map +0 -1
  40. package/scripts/migrate-tracing-db.mjs +0 -295
@@ -41,470 +41,382 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  var __metadata = (this && this.__metadata) || function (k, v) {
42
42
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
43
  };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ var __importDefault = (this && this.__importDefault) || function (mod) {
48
+ return (mod && mod.__esModule) ? mod : { "default": mod };
49
+ };
44
50
  var TracingService_1;
45
51
  Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.TracingService = exports.TraceStatus = void 0;
52
+ exports.TracingService = exports.SpanKind = exports.TaskStatus = void 0;
47
53
  const common_1 = require("@nestjs/common");
48
- const path = __importStar(require("path"));
49
54
  const fs = __importStar(require("fs"));
50
- var TraceStatus;
51
- (function (TraceStatus) {
52
- TraceStatus["PENDING"] = "pending";
53
- TraceStatus["RUNNING"] = "running";
54
- TraceStatus["COMPLETED"] = "completed";
55
- TraceStatus["FAILED"] = "failed";
56
- TraceStatus["CANCELLED"] = "cancelled";
57
- })(TraceStatus || (exports.TraceStatus = TraceStatus = {}));
55
+ const path = __importStar(require("path"));
56
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
57
+ var TaskStatus;
58
+ (function (TaskStatus) {
59
+ TaskStatus["PENDING"] = "pending";
60
+ TaskStatus["RUNNING"] = "running";
61
+ TaskStatus["SUCCESS"] = "success";
62
+ TaskStatus["FAILED"] = "failed";
63
+ })(TaskStatus || (exports.TaskStatus = TaskStatus = {}));
64
+ var SpanKind;
65
+ (function (SpanKind) {
66
+ SpanKind["INTERNAL"] = "internal";
67
+ SpanKind["CLIENT"] = "client";
68
+ SpanKind["SERVER"] = "server";
69
+ SpanKind["PRODUCER"] = "producer";
70
+ SpanKind["CONSUMER"] = "consumer";
71
+ })(SpanKind || (exports.SpanKind = SpanKind = {}));
58
72
  let TracingService = TracingService_1 = class TracingService {
59
- constructor() {
73
+ constructor(options) {
60
74
  this.logger = new common_1.Logger(TracingService_1.name);
61
75
  this.db = null;
62
- this.initialized = false;
63
- const homeDir = process.env.HOME || process.env.USERPROFILE || '.';
64
- const crewxDir = path.join(homeDir, '.crewx');
65
- this.dbPath = path.join(crewxDir, 'tracing.db');
66
- }
67
- async ensureInitialized() {
68
- if (this.initialized) {
69
- return true;
76
+ if (options?.dbPath) {
77
+ this.dbPath = options.dbPath;
78
+ }
79
+ else {
80
+ const crewxDir = path.join(process.cwd(), '.crewx');
81
+ this.dbPath = path.join(crewxDir, 'traces.db');
70
82
  }
83
+ }
84
+ onModuleInit() {
85
+ this.initializeDatabase();
86
+ }
87
+ onModuleDestroy() {
88
+ this.close();
89
+ }
90
+ initializeDatabase() {
71
91
  try {
72
- const Database = await this.loadBetterSqlite3();
73
- if (!Database) {
74
- this.logger.warn('better-sqlite3 not available, tracing disabled');
75
- return false;
76
- }
77
92
  const dir = path.dirname(this.dbPath);
78
93
  if (!fs.existsSync(dir)) {
79
94
  fs.mkdirSync(dir, { recursive: true });
80
95
  }
81
- this.db = new Database(this.dbPath);
96
+ this.db = new better_sqlite3_1.default(this.dbPath);
82
97
  this.db.pragma('journal_mode = WAL');
83
- this.db.pragma('synchronous = NORMAL');
84
- this.db.pragma('cache_size = -64000');
85
- this.db.pragma('temp_store = MEMORY');
86
- this.createTables();
87
- this.initialized = true;
98
+ this.db.exec(`
99
+ CREATE TABLE IF NOT EXISTS tasks (
100
+ id TEXT PRIMARY KEY,
101
+ agent_id TEXT NOT NULL,
102
+ user_id TEXT,
103
+ prompt TEXT NOT NULL,
104
+ mode TEXT NOT NULL CHECK (mode IN ('query', 'execute')),
105
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'success', 'failed')),
106
+ result TEXT,
107
+ error TEXT,
108
+ started_at TEXT NOT NULL,
109
+ completed_at TEXT,
110
+ duration_ms INTEGER,
111
+ metadata TEXT
112
+ )
113
+ `);
114
+ this.db.exec(`
115
+ CREATE TABLE IF NOT EXISTS spans (
116
+ id TEXT PRIMARY KEY,
117
+ task_id TEXT NOT NULL,
118
+ parent_span_id TEXT,
119
+ name TEXT NOT NULL,
120
+ kind TEXT NOT NULL CHECK (kind IN ('internal', 'client', 'server', 'producer', 'consumer')),
121
+ status TEXT NOT NULL CHECK (status IN ('ok', 'error')),
122
+ started_at TEXT NOT NULL,
123
+ completed_at TEXT,
124
+ duration_ms INTEGER,
125
+ input TEXT,
126
+ output TEXT,
127
+ error TEXT,
128
+ attributes TEXT,
129
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
130
+ FOREIGN KEY (parent_span_id) REFERENCES spans(id) ON DELETE SET NULL
131
+ )
132
+ `);
133
+ this.db.exec(`
134
+ CREATE INDEX IF NOT EXISTS idx_tasks_agent_id ON tasks(agent_id);
135
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
136
+ CREATE INDEX IF NOT EXISTS idx_tasks_started_at ON tasks(started_at);
137
+ CREATE INDEX IF NOT EXISTS idx_spans_task_id ON spans(task_id);
138
+ CREATE INDEX IF NOT EXISTS idx_spans_parent_span_id ON spans(parent_span_id);
139
+ `);
88
140
  this.logger.log(`Tracing database initialized at ${this.dbPath}`);
89
- return true;
90
141
  }
91
142
  catch (error) {
92
- this.logger.warn(`Failed to initialize tracing database: ${error instanceof Error ? error.message : String(error)}`);
93
- return false;
143
+ this.logger.error(`Failed to initialize tracing database: ${error instanceof Error ? error.message : String(error)}`);
94
144
  }
95
145
  }
96
- async loadBetterSqlite3() {
97
- try {
98
- return require('better-sqlite3');
99
- }
100
- catch {
101
- return null;
146
+ close() {
147
+ if (this.db) {
148
+ this.db.close();
149
+ this.db = null;
150
+ this.logger.log('Tracing database connection closed');
102
151
  }
103
152
  }
104
- createTables() {
105
- this.db.exec(`
106
- CREATE TABLE IF NOT EXISTS traces (
107
- id TEXT PRIMARY KEY,
108
- name TEXT NOT NULL,
109
- agent_id TEXT,
110
- session_id TEXT,
111
- start_time INTEGER NOT NULL,
112
- end_time INTEGER,
113
- status TEXT NOT NULL DEFAULT 'pending',
114
- attributes TEXT,
115
- metadata TEXT,
116
- created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
117
- updated_at INTEGER DEFAULT (strftime('%s', 'now') * 1000)
118
- );
119
-
120
- CREATE INDEX IF NOT EXISTS idx_traces_agent_id ON traces(agent_id);
121
- CREATE INDEX IF NOT EXISTS idx_traces_session_id ON traces(session_id);
122
- CREATE INDEX IF NOT EXISTS idx_traces_status ON traces(status);
123
- CREATE INDEX IF NOT EXISTS idx_traces_start_time ON traces(start_time);
124
- `);
125
- this.db.exec(`
126
- CREATE TABLE IF NOT EXISTS trace_spans (
127
- id TEXT PRIMARY KEY,
128
- trace_id TEXT NOT NULL,
129
- name TEXT NOT NULL,
130
- parent_span_id TEXT,
131
- start_time INTEGER NOT NULL,
132
- end_time INTEGER,
133
- status TEXT NOT NULL DEFAULT 'pending',
134
- attributes TEXT,
135
- events TEXT,
136
- created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
137
- FOREIGN KEY (trace_id) REFERENCES traces(id) ON DELETE CASCADE
138
- );
139
-
140
- CREATE INDEX IF NOT EXISTS idx_spans_trace_id ON trace_spans(trace_id);
141
- CREATE INDEX IF NOT EXISTS idx_spans_parent ON trace_spans(parent_span_id);
142
- `);
143
- this.db.exec(`
144
- CREATE TABLE IF NOT EXISTS trace_events (
145
- id INTEGER PRIMARY KEY AUTOINCREMENT,
146
- trace_id TEXT NOT NULL,
147
- span_id TEXT,
148
- name TEXT NOT NULL,
149
- timestamp INTEGER NOT NULL,
150
- attributes TEXT,
151
- FOREIGN KEY (trace_id) REFERENCES traces(id) ON DELETE CASCADE
152
- );
153
-
154
- CREATE INDEX IF NOT EXISTS idx_events_trace_id ON trace_events(trace_id);
155
- `);
156
- }
157
153
  generateId() {
158
- const timestamp = Date.now().toString(36);
159
- const randomPart = Math.random().toString(36).substring(2, 10);
160
- return `${timestamp}-${randomPart}`;
154
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
155
+ const r = (Math.random() * 16) | 0;
156
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
157
+ return v.toString(16);
158
+ });
159
+ }
160
+ now() {
161
+ return new Date().toISOString();
161
162
  }
162
- async startTrace(name, options = {}) {
163
- if (!(await this.ensureInitialized())) {
163
+ createTask(input) {
164
+ if (!this.db) {
164
165
  return null;
165
166
  }
166
167
  const id = this.generateId();
167
- const now = Date.now();
168
+ const now = this.now();
168
169
  try {
169
170
  const stmt = this.db.prepare(`
170
- INSERT INTO traces (id, name, agent_id, session_id, start_time, status, attributes, metadata)
171
+ INSERT INTO tasks (id, agent_id, user_id, prompt, mode, status, started_at, metadata)
171
172
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
172
173
  `);
173
- stmt.run(id, name, options.agentId || null, options.sessionId || null, now, TraceStatus.RUNNING, options.attributes ? JSON.stringify(options.attributes) : null, options.metadata ? JSON.stringify(options.metadata) : null);
174
- this.logger.debug(`Started trace: ${id} (${name})`);
174
+ stmt.run(id, input.agent_id, input.user_id ?? null, input.prompt, input.mode, TaskStatus.RUNNING, now, input.metadata ? JSON.stringify(input.metadata) : null);
175
+ this.logger.debug(`Task created: ${id} for agent ${input.agent_id}`);
175
176
  return id;
176
177
  }
177
178
  catch (error) {
178
- this.logger.error(`Failed to start trace: ${error instanceof Error ? error.message : String(error)}`);
179
+ this.logger.error(`Failed to create task: ${error instanceof Error ? error.message : String(error)}`);
179
180
  return null;
180
181
  }
181
182
  }
182
- async completeTrace(traceId, result) {
183
- if (!(await this.ensureInitialized())) {
183
+ completeTask(taskId, result) {
184
+ if (!this.db) {
184
185
  return false;
185
186
  }
186
187
  try {
187
- const now = Date.now();
188
+ const now = this.now();
188
189
  const stmt = this.db.prepare(`
189
- UPDATE traces
190
- SET status = ?, end_time = ?, metadata = json_patch(COALESCE(metadata, '{}'), ?), updated_at = ?
190
+ UPDATE tasks
191
+ SET status = ?, result = ?, completed_at = ?,
192
+ duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
191
193
  WHERE id = ?
192
194
  `);
193
- const resultMetadata = result ? JSON.stringify({ result }) : '{}';
194
- const changes = stmt.run(TraceStatus.COMPLETED, now, resultMetadata, now, traceId);
195
- if (changes.changes > 0) {
196
- this.logger.debug(`Completed trace: ${traceId}`);
197
- return true;
198
- }
199
- return false;
195
+ const info = stmt.run(TaskStatus.SUCCESS, result ?? null, now, now, taskId);
196
+ return info.changes > 0;
200
197
  }
201
198
  catch (error) {
202
- this.logger.error(`Failed to complete trace: ${error instanceof Error ? error.message : String(error)}`);
199
+ this.logger.error(`Failed to complete task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
203
200
  return false;
204
201
  }
205
202
  }
206
- async failTrace(traceId, error) {
207
- if (!(await this.ensureInitialized())) {
203
+ failTask(taskId, error) {
204
+ if (!this.db) {
208
205
  return false;
209
206
  }
210
207
  try {
211
- const now = Date.now();
212
- const errorInfo = {
213
- error: {
214
- message: error instanceof Error ? error.message : String(error),
215
- stack: error instanceof Error ? error.stack : undefined,
216
- }
217
- };
208
+ const now = this.now();
218
209
  const stmt = this.db.prepare(`
219
- UPDATE traces
220
- SET status = ?, end_time = ?, metadata = json_patch(COALESCE(metadata, '{}'), ?), updated_at = ?
210
+ UPDATE tasks
211
+ SET status = ?, error = ?, completed_at = ?,
212
+ duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
221
213
  WHERE id = ?
222
214
  `);
223
- const changes = stmt.run(TraceStatus.FAILED, now, JSON.stringify(errorInfo), now, traceId);
224
- if (changes.changes > 0) {
225
- this.logger.debug(`Failed trace: ${traceId}`);
226
- return true;
227
- }
228
- return false;
215
+ const info = stmt.run(TaskStatus.FAILED, error, now, now, taskId);
216
+ return info.changes > 0;
229
217
  }
230
- catch (err) {
231
- this.logger.error(`Failed to update trace status: ${err instanceof Error ? err.message : String(err)}`);
218
+ catch (error) {
219
+ this.logger.error(`Failed to fail task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
232
220
  return false;
233
221
  }
234
222
  }
235
- async startSpan(traceId, name, options = {}) {
236
- if (!(await this.ensureInitialized())) {
223
+ getTask(taskId) {
224
+ if (!this.db) {
237
225
  return null;
238
226
  }
239
- const id = this.generateId();
240
- const now = Date.now();
241
227
  try {
242
- const stmt = this.db.prepare(`
243
- INSERT INTO trace_spans (id, trace_id, name, parent_span_id, start_time, status, attributes)
244
- VALUES (?, ?, ?, ?, ?, ?, ?)
245
- `);
246
- stmt.run(id, traceId, name, options.parentSpanId || null, now, TraceStatus.RUNNING, options.attributes ? JSON.stringify(options.attributes) : null);
247
- return id;
228
+ const stmt = this.db.prepare('SELECT * FROM tasks WHERE id = ?');
229
+ const row = stmt.get(taskId);
230
+ if (!row) {
231
+ return null;
232
+ }
233
+ return {
234
+ ...row,
235
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
236
+ };
248
237
  }
249
238
  catch (error) {
250
- this.logger.error(`Failed to start span: ${error instanceof Error ? error.message : String(error)}`);
239
+ this.logger.error(`Failed to get task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
251
240
  return null;
252
241
  }
253
242
  }
254
- async endSpan(spanId, status = TraceStatus.COMPLETED, attributes) {
255
- if (!(await this.ensureInitialized())) {
256
- return false;
243
+ listTasks(options) {
244
+ if (!this.db) {
245
+ return [];
257
246
  }
247
+ const limit = options?.limit ?? 20;
248
+ const offset = options?.offset ?? 0;
258
249
  try {
259
- const now = Date.now();
260
- let stmt;
261
- let changes;
262
- if (attributes) {
263
- stmt = this.db.prepare(`
264
- UPDATE trace_spans
265
- SET status = ?, end_time = ?, attributes = json_patch(COALESCE(attributes, '{}'), ?)
266
- WHERE id = ?
267
- `);
268
- changes = stmt.run(status, now, JSON.stringify(attributes), spanId);
269
- }
270
- else {
271
- stmt = this.db.prepare(`
272
- UPDATE trace_spans
273
- SET status = ?, end_time = ?
274
- WHERE id = ?
275
- `);
276
- changes = stmt.run(status, now, spanId);
250
+ let query = 'SELECT * FROM tasks';
251
+ const params = [];
252
+ if (options?.agentId) {
253
+ query += ' WHERE agent_id = ?';
254
+ params.push(options.agentId);
277
255
  }
278
- return changes.changes > 0;
256
+ query += ' ORDER BY started_at DESC LIMIT ? OFFSET ?';
257
+ params.push(limit, offset);
258
+ const stmt = this.db.prepare(query);
259
+ const rows = stmt.all(...params);
260
+ return rows.map((row) => ({
261
+ ...row,
262
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
263
+ }));
279
264
  }
280
265
  catch (error) {
281
- this.logger.error(`Failed to end span: ${error instanceof Error ? error.message : String(error)}`);
282
- return false;
266
+ this.logger.error(`Failed to list tasks: ${error instanceof Error ? error.message : String(error)}`);
267
+ return [];
283
268
  }
284
269
  }
285
- async addEvent(traceId, name, options = {}) {
286
- if (!(await this.ensureInitialized())) {
287
- return false;
270
+ createSpan(input) {
271
+ if (!this.db) {
272
+ return null;
288
273
  }
274
+ const id = this.generateId();
275
+ const now = this.now();
289
276
  try {
290
- const now = Date.now();
291
277
  const stmt = this.db.prepare(`
292
- INSERT INTO trace_events (trace_id, span_id, name, timestamp, attributes)
293
- VALUES (?, ?, ?, ?, ?)
278
+ INSERT INTO spans (id, task_id, parent_span_id, name, kind, status, started_at, input, attributes)
279
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
294
280
  `);
295
- stmt.run(traceId, options.spanId || null, name, now, options.attributes ? JSON.stringify(options.attributes) : null);
296
- return true;
281
+ stmt.run(id, input.task_id, input.parent_span_id ?? null, input.name, input.kind ?? SpanKind.INTERNAL, 'ok', now, input.input ?? null, input.attributes ? JSON.stringify(input.attributes) : null);
282
+ this.logger.debug(`Span created: ${id} for task ${input.task_id}`);
283
+ return id;
297
284
  }
298
285
  catch (error) {
299
- this.logger.error(`Failed to add event: ${error instanceof Error ? error.message : String(error)}`);
300
- return false;
286
+ this.logger.error(`Failed to create span: ${error instanceof Error ? error.message : String(error)}`);
287
+ return null;
301
288
  }
302
289
  }
303
- async getTrace(traceId) {
304
- if (!(await this.ensureInitialized())) {
305
- return null;
290
+ completeSpan(spanId, output) {
291
+ if (!this.db) {
292
+ return false;
306
293
  }
307
294
  try {
308
- const traceStmt = this.db.prepare(`
309
- SELECT * FROM traces WHERE id = ?
310
- `);
311
- const row = traceStmt.get(traceId);
312
- if (!row) {
313
- return null;
314
- }
315
- const spansStmt = this.db.prepare(`
316
- SELECT * FROM trace_spans WHERE trace_id = ? ORDER BY start_time
317
- `);
318
- const spanRows = spansStmt.all(traceId);
319
- const eventsStmt = this.db.prepare(`
320
- SELECT * FROM trace_events WHERE trace_id = ? ORDER BY timestamp
295
+ const now = this.now();
296
+ const stmt = this.db.prepare(`
297
+ UPDATE spans
298
+ SET status = 'ok', output = ?, completed_at = ?,
299
+ duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
300
+ WHERE id = ?
321
301
  `);
322
- const eventRows = eventsStmt.all(traceId);
323
- return this.rowToTrace(row, spanRows, eventRows);
302
+ const info = stmt.run(output ?? null, now, now, spanId);
303
+ return info.changes > 0;
324
304
  }
325
305
  catch (error) {
326
- this.logger.error(`Failed to get trace: ${error instanceof Error ? error.message : String(error)}`);
327
- return null;
306
+ this.logger.error(`Failed to complete span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
307
+ return false;
328
308
  }
329
309
  }
330
- async queryTraces(options = {}) {
331
- if (!(await this.ensureInitialized())) {
332
- return [];
310
+ failSpan(spanId, error) {
311
+ if (!this.db) {
312
+ return false;
333
313
  }
334
314
  try {
335
- const conditions = [];
336
- const params = [];
337
- if (options.agentId) {
338
- conditions.push('agent_id = ?');
339
- params.push(options.agentId);
340
- }
341
- if (options.sessionId) {
342
- conditions.push('session_id = ?');
343
- params.push(options.sessionId);
344
- }
345
- if (options.status) {
346
- conditions.push('status = ?');
347
- params.push(options.status);
348
- }
349
- if (options.startTimeFrom) {
350
- conditions.push('start_time >= ?');
351
- params.push(options.startTimeFrom);
352
- }
353
- if (options.startTimeTo) {
354
- conditions.push('start_time <= ?');
355
- params.push(options.startTimeTo);
356
- }
357
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
358
- const limit = options.limit || 100;
359
- const offset = options.offset || 0;
315
+ const now = this.now();
360
316
  const stmt = this.db.prepare(`
361
- SELECT * FROM traces
362
- ${whereClause}
363
- ORDER BY start_time DESC
364
- LIMIT ? OFFSET ?
317
+ UPDATE spans
318
+ SET status = 'error', error = ?, completed_at = ?,
319
+ duration_ms = CAST((julianday(?) - julianday(started_at)) * 86400000 AS INTEGER)
320
+ WHERE id = ?
365
321
  `);
366
- params.push(limit, offset);
367
- const rows = stmt.all(...params);
368
- return rows.map((row) => this.rowToTrace(row));
322
+ const info = stmt.run(error, now, now, spanId);
323
+ return info.changes > 0;
369
324
  }
370
325
  catch (error) {
371
- this.logger.error(`Failed to query traces: ${error instanceof Error ? error.message : String(error)}`);
372
- return [];
326
+ this.logger.error(`Failed to fail span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
327
+ return false;
373
328
  }
374
329
  }
375
- async queryTracesByThreadTs(threadTs, options = {}) {
376
- if (!(await this.ensureInitialized())) {
330
+ getSpansForTask(taskId) {
331
+ if (!this.db) {
377
332
  return [];
378
333
  }
379
334
  try {
380
- const conditions = [`json_extract(metadata, '$.thread_ts') = ?`];
381
- const params = [threadTs];
382
- if (options.agentId) {
383
- conditions.push('agent_id = ?');
384
- params.push(options.agentId);
385
- }
386
- const whereClause = `WHERE ${conditions.join(' AND ')}`;
387
- const limit = options.limit || 100;
388
- const stmt = this.db.prepare(`
389
- SELECT * FROM traces
390
- ${whereClause}
391
- ORDER BY start_time DESC
392
- LIMIT ?
393
- `);
394
- const rows = stmt.all(...params, limit);
395
- return rows.map((row) => this.rowToTrace(row));
335
+ const stmt = this.db.prepare('SELECT * FROM spans WHERE task_id = ? ORDER BY started_at ASC');
336
+ const rows = stmt.all(taskId);
337
+ return rows.map((row) => ({
338
+ ...row,
339
+ attributes: row.attributes ? JSON.parse(row.attributes) : undefined,
340
+ }));
396
341
  }
397
342
  catch (error) {
398
- this.logger.warn(`Failed to query traces by thread_ts (metadata.thread_ts): ${error instanceof Error ? error.message : String(error)}`);
343
+ this.logger.error(`Failed to get spans for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`);
399
344
  return [];
400
345
  }
401
346
  }
402
- async getStats(options = {}) {
403
- if (!(await this.ensureInitialized())) {
404
- return { total: 0, byStatus: {}, avgDuration: 0 };
347
+ getSpan(spanId) {
348
+ if (!this.db) {
349
+ return null;
405
350
  }
406
351
  try {
407
- const conditions = [];
408
- const params = [];
409
- if (options.agentId) {
410
- conditions.push('agent_id = ?');
411
- params.push(options.agentId);
412
- }
413
- if (options.since) {
414
- conditions.push('start_time >= ?');
415
- params.push(options.since);
352
+ const stmt = this.db.prepare('SELECT * FROM spans WHERE id = ?');
353
+ const row = stmt.get(spanId);
354
+ if (!row) {
355
+ return null;
416
356
  }
417
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
418
- const totalStmt = this.db.prepare(`SELECT COUNT(*) as count FROM traces ${whereClause}`);
419
- const total = totalStmt.get(...params).count;
420
- const statusStmt = this.db.prepare(`
421
- SELECT status, COUNT(*) as count
422
- FROM traces ${whereClause}
423
- GROUP BY status
424
- `);
425
- const statusRows = statusStmt.all(...params);
426
- const byStatus = statusRows.reduce((acc, row) => {
427
- acc[row.status] = row.count;
428
- return acc;
429
- }, {});
430
- const durationWhereClause = conditions.length > 0
431
- ? `${whereClause} AND end_time IS NOT NULL`
432
- : 'WHERE end_time IS NOT NULL';
433
- const avgStmt = this.db.prepare(`
434
- SELECT AVG(end_time - start_time) as avg_duration
435
- FROM traces ${durationWhereClause}
436
- `);
437
- const avgDuration = avgStmt.get(...params)?.avg_duration || 0;
438
- return { total, byStatus, avgDuration };
357
+ return {
358
+ ...row,
359
+ attributes: row.attributes ? JSON.parse(row.attributes) : undefined,
360
+ };
439
361
  }
440
362
  catch (error) {
441
- this.logger.error(`Failed to get stats: ${error instanceof Error ? error.message : String(error)}`);
442
- return { total: 0, byStatus: {}, avgDuration: 0 };
363
+ this.logger.error(`Failed to get span ${spanId}: ${error instanceof Error ? error.message : String(error)}`);
364
+ return null;
443
365
  }
444
366
  }
445
- async deleteOldTraces(olderThanMs) {
446
- if (!(await this.ensureInitialized())) {
367
+ cleanupOldTasks(daysToKeep = 30) {
368
+ if (!this.db) {
447
369
  return 0;
448
370
  }
449
371
  try {
450
- const cutoff = Date.now() - olderThanMs;
451
- const stmt = this.db.prepare('DELETE FROM traces WHERE start_time < ?');
452
- const result = stmt.run(cutoff);
453
- if (result.changes > 0) {
454
- this.logger.log(`Deleted ${result.changes} old traces`);
372
+ const cutoffDate = new Date();
373
+ cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
374
+ const cutoff = cutoffDate.toISOString();
375
+ const stmt = this.db.prepare('DELETE FROM tasks WHERE started_at < ?');
376
+ const info = stmt.run(cutoff);
377
+ if (info.changes > 0) {
378
+ this.logger.log(`Cleaned up ${info.changes} old tasks`);
455
379
  }
456
- return result.changes;
380
+ return info.changes;
457
381
  }
458
382
  catch (error) {
459
- this.logger.error(`Failed to delete old traces: ${error instanceof Error ? error.message : String(error)}`);
383
+ this.logger.error(`Failed to cleanup old tasks: ${error instanceof Error ? error.message : String(error)}`);
460
384
  return 0;
461
385
  }
462
386
  }
463
- rowToTrace(row, spanRows, eventRows) {
464
- const trace = {
465
- id: row.id,
466
- name: row.name,
467
- agentId: row.agent_id || undefined,
468
- sessionId: row.session_id || undefined,
469
- startTime: row.start_time,
470
- endTime: row.end_time || undefined,
471
- status: row.status,
472
- attributes: row.attributes ? JSON.parse(row.attributes) : undefined,
473
- metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
474
- };
475
- if (spanRows && spanRows.length > 0) {
476
- trace.spans = spanRows.map((spanRow) => ({
477
- id: spanRow.id,
478
- traceId: spanRow.trace_id,
479
- name: spanRow.name,
480
- parentSpanId: spanRow.parent_span_id || undefined,
481
- startTime: spanRow.start_time,
482
- endTime: spanRow.end_time || undefined,
483
- status: spanRow.status,
484
- attributes: spanRow.attributes ? JSON.parse(spanRow.attributes) : undefined,
485
- events: spanRow.events ? JSON.parse(spanRow.events) : undefined,
486
- }));
387
+ getStats() {
388
+ if (!this.db) {
389
+ return null;
487
390
  }
488
- return trace;
489
- }
490
- async isAvailable() {
491
- return this.ensureInitialized();
492
- }
493
- onModuleDestroy() {
494
- if (this.db) {
495
- try {
496
- this.db.close();
497
- this.logger.log('Tracing database connection closed');
498
- }
499
- catch (error) {
500
- this.logger.warn(`Error closing database: ${error instanceof Error ? error.message : String(error)}`);
391
+ try {
392
+ const taskCountStmt = this.db.prepare('SELECT COUNT(*) as count FROM tasks');
393
+ const spanCountStmt = this.db.prepare('SELECT COUNT(*) as count FROM spans');
394
+ const taskCount = taskCountStmt.get().count;
395
+ const spanCount = spanCountStmt.get().count;
396
+ let dbSizeBytes = 0;
397
+ if (fs.existsSync(this.dbPath)) {
398
+ const stats = fs.statSync(this.dbPath);
399
+ dbSizeBytes = stats.size;
501
400
  }
401
+ return { taskCount, spanCount, dbSizeBytes };
502
402
  }
403
+ catch (error) {
404
+ this.logger.error(`Failed to get stats: ${error instanceof Error ? error.message : String(error)}`);
405
+ return null;
406
+ }
407
+ }
408
+ isEnabled() {
409
+ return this.db !== null;
410
+ }
411
+ getDbPath() {
412
+ return this.dbPath;
503
413
  }
504
414
  };
505
415
  exports.TracingService = TracingService;
506
416
  exports.TracingService = TracingService = TracingService_1 = __decorate([
507
417
  (0, common_1.Injectable)(),
508
- __metadata("design:paramtypes", [])
418
+ __param(0, (0, common_1.Optional)()),
419
+ __param(0, (0, common_1.Inject)('TRACING_OPTIONS')),
420
+ __metadata("design:paramtypes", [Object])
509
421
  ], TracingService);
510
422
  //# sourceMappingURL=tracing.service.js.map