@mcoda/core 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/api/AgentsApi.d.ts +36 -0
- package/dist/api/AgentsApi.d.ts.map +1 -0
- package/dist/api/AgentsApi.js +176 -0
- package/dist/api/QaTasksApi.d.ts +8 -0
- package/dist/api/QaTasksApi.d.ts.map +1 -0
- package/dist/api/QaTasksApi.js +36 -0
- package/dist/api/TasksApi.d.ts +7 -0
- package/dist/api/TasksApi.d.ts.map +1 -0
- package/dist/api/TasksApi.js +34 -0
- package/dist/config/ConfigService.d.ts +3 -0
- package/dist/config/ConfigService.d.ts.map +1 -0
- package/dist/config/ConfigService.js +2 -0
- package/dist/domain/dependencies/Dependency.d.ts +3 -0
- package/dist/domain/dependencies/Dependency.d.ts.map +1 -0
- package/dist/domain/dependencies/Dependency.js +2 -0
- package/dist/domain/epics/Epic.d.ts +3 -0
- package/dist/domain/epics/Epic.d.ts.map +1 -0
- package/dist/domain/epics/Epic.js +2 -0
- package/dist/domain/projects/Project.d.ts +3 -0
- package/dist/domain/projects/Project.d.ts.map +1 -0
- package/dist/domain/projects/Project.js +2 -0
- package/dist/domain/tasks/Task.d.ts +3 -0
- package/dist/domain/tasks/Task.d.ts.map +1 -0
- package/dist/domain/tasks/Task.js +2 -0
- package/dist/domain/userStories/UserStory.d.ts +3 -0
- package/dist/domain/userStories/UserStory.d.ts.map +1 -0
- package/dist/domain/userStories/UserStory.js +2 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/prompts/PdrPrompts.d.ts +4 -0
- package/dist/prompts/PdrPrompts.d.ts.map +1 -0
- package/dist/prompts/PdrPrompts.js +21 -0
- package/dist/prompts/PromptLoader.d.ts +3 -0
- package/dist/prompts/PromptLoader.d.ts.map +1 -0
- package/dist/prompts/PromptLoader.js +2 -0
- package/dist/prompts/SdsPrompts.d.ts +5 -0
- package/dist/prompts/SdsPrompts.d.ts.map +1 -0
- package/dist/prompts/SdsPrompts.js +44 -0
- package/dist/services/agents/AgentManagementService.d.ts +3 -0
- package/dist/services/agents/AgentManagementService.d.ts.map +1 -0
- package/dist/services/agents/AgentManagementService.js +2 -0
- package/dist/services/agents/GatewayAgentService.d.ts +92 -0
- package/dist/services/agents/GatewayAgentService.d.ts.map +1 -0
- package/dist/services/agents/GatewayAgentService.js +870 -0
- package/dist/services/agents/RoutingApiClient.d.ts +23 -0
- package/dist/services/agents/RoutingApiClient.d.ts.map +1 -0
- package/dist/services/agents/RoutingApiClient.js +62 -0
- package/dist/services/agents/RoutingService.d.ts +50 -0
- package/dist/services/agents/RoutingService.d.ts.map +1 -0
- package/dist/services/agents/RoutingService.js +386 -0
- package/dist/services/agents/generated/RoutingApiClient.d.ts +21 -0
- package/dist/services/agents/generated/RoutingApiClient.d.ts.map +1 -0
- package/dist/services/agents/generated/RoutingApiClient.js +68 -0
- package/dist/services/backlog/BacklogService.d.ts +98 -0
- package/dist/services/backlog/BacklogService.d.ts.map +1 -0
- package/dist/services/backlog/BacklogService.js +453 -0
- package/dist/services/backlog/TaskOrderingService.d.ts +88 -0
- package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -0
- package/dist/services/backlog/TaskOrderingService.js +675 -0
- package/dist/services/docs/DocsService.d.ts +82 -0
- package/dist/services/docs/DocsService.d.ts.map +1 -0
- package/dist/services/docs/DocsService.js +1631 -0
- package/dist/services/estimate/EstimateService.d.ts +12 -0
- package/dist/services/estimate/EstimateService.d.ts.map +1 -0
- package/dist/services/estimate/EstimateService.js +103 -0
- package/dist/services/estimate/VelocityService.d.ts +19 -0
- package/dist/services/estimate/VelocityService.d.ts.map +1 -0
- package/dist/services/estimate/VelocityService.js +237 -0
- package/dist/services/estimate/types.d.ts +30 -0
- package/dist/services/estimate/types.d.ts.map +1 -0
- package/dist/services/estimate/types.js +1 -0
- package/dist/services/execution/ExecutionService.d.ts +3 -0
- package/dist/services/execution/ExecutionService.d.ts.map +1 -0
- package/dist/services/execution/ExecutionService.js +2 -0
- package/dist/services/execution/QaFollowupService.d.ts +38 -0
- package/dist/services/execution/QaFollowupService.d.ts.map +1 -0
- package/dist/services/execution/QaFollowupService.js +236 -0
- package/dist/services/execution/QaProfileService.d.ts +22 -0
- package/dist/services/execution/QaProfileService.d.ts.map +1 -0
- package/dist/services/execution/QaProfileService.js +142 -0
- package/dist/services/execution/QaTasksService.d.ts +101 -0
- package/dist/services/execution/QaTasksService.d.ts.map +1 -0
- package/dist/services/execution/QaTasksService.js +1117 -0
- package/dist/services/execution/TaskSelectionService.d.ts +50 -0
- package/dist/services/execution/TaskSelectionService.d.ts.map +1 -0
- package/dist/services/execution/TaskSelectionService.js +281 -0
- package/dist/services/execution/TaskStateService.d.ts +19 -0
- package/dist/services/execution/TaskStateService.d.ts.map +1 -0
- package/dist/services/execution/TaskStateService.js +59 -0
- package/dist/services/execution/WorkOnTasksService.d.ts +80 -0
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -0
- package/dist/services/execution/WorkOnTasksService.js +1833 -0
- package/dist/services/jobs/JobInsightsService.d.ts +97 -0
- package/dist/services/jobs/JobInsightsService.d.ts.map +1 -0
- package/dist/services/jobs/JobInsightsService.js +263 -0
- package/dist/services/jobs/JobResumeService.d.ts +16 -0
- package/dist/services/jobs/JobResumeService.d.ts.map +1 -0
- package/dist/services/jobs/JobResumeService.js +113 -0
- package/dist/services/jobs/JobService.d.ts +149 -0
- package/dist/services/jobs/JobService.d.ts.map +1 -0
- package/dist/services/jobs/JobService.js +490 -0
- package/dist/services/jobs/JobsApiClient.d.ts +73 -0
- package/dist/services/jobs/JobsApiClient.d.ts.map +1 -0
- package/dist/services/jobs/JobsApiClient.js +67 -0
- package/dist/services/openapi/OpenApiService.d.ts +54 -0
- package/dist/services/openapi/OpenApiService.d.ts.map +1 -0
- package/dist/services/openapi/OpenApiService.js +503 -0
- package/dist/services/planning/CreateTasksService.d.ts +68 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -0
- package/dist/services/planning/CreateTasksService.js +989 -0
- package/dist/services/planning/KeyHelpers.d.ts +5 -0
- package/dist/services/planning/KeyHelpers.d.ts.map +1 -0
- package/dist/services/planning/KeyHelpers.js +62 -0
- package/dist/services/planning/PlanningService.d.ts +3 -0
- package/dist/services/planning/PlanningService.d.ts.map +1 -0
- package/dist/services/planning/PlanningService.js +2 -0
- package/dist/services/planning/RefineTasksService.d.ts +56 -0
- package/dist/services/planning/RefineTasksService.d.ts.map +1 -0
- package/dist/services/planning/RefineTasksService.js +1328 -0
- package/dist/services/review/CodeReviewService.d.ts +103 -0
- package/dist/services/review/CodeReviewService.d.ts.map +1 -0
- package/dist/services/review/CodeReviewService.js +1187 -0
- package/dist/services/system/SystemUpdateService.d.ts +55 -0
- package/dist/services/system/SystemUpdateService.d.ts.map +1 -0
- package/dist/services/system/SystemUpdateService.js +136 -0
- package/dist/services/tasks/TaskApiResolver.d.ts +7 -0
- package/dist/services/tasks/TaskApiResolver.d.ts.map +1 -0
- package/dist/services/tasks/TaskApiResolver.js +41 -0
- package/dist/services/tasks/TaskDetailService.d.ts +106 -0
- package/dist/services/tasks/TaskDetailService.d.ts.map +1 -0
- package/dist/services/tasks/TaskDetailService.js +332 -0
- package/dist/services/telemetry/TelemetryService.d.ts +53 -0
- package/dist/services/telemetry/TelemetryService.d.ts.map +1 -0
- package/dist/services/telemetry/TelemetryService.js +434 -0
- package/dist/workspace/WorkspaceManager.d.ts +35 -0
- package/dist/workspace/WorkspaceManager.d.ts.map +1 -0
- package/dist/workspace/WorkspaceManager.js +201 -0
- package/package.json +45 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { Connection, WorkspaceRepository, GlobalRepository } from "@mcoda/db";
|
|
3
|
+
import { PathHelper } from "@mcoda/shared";
|
|
4
|
+
import { TaskApiResolver } from "./TaskApiResolver.js";
|
|
5
|
+
const hasTables = async (db, required) => {
|
|
6
|
+
const placeholders = required.map(() => "?").join(", ");
|
|
7
|
+
const rows = await db.all(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders})`, required);
|
|
8
|
+
return rows.length === required.length;
|
|
9
|
+
};
|
|
10
|
+
export class TaskDetailService {
|
|
11
|
+
constructor(workspace, db, repo, globalRepo, apiResolver = new TaskApiResolver()) {
|
|
12
|
+
this.workspace = workspace;
|
|
13
|
+
this.db = db;
|
|
14
|
+
this.repo = repo;
|
|
15
|
+
this.globalRepo = globalRepo;
|
|
16
|
+
this.apiResolver = apiResolver;
|
|
17
|
+
}
|
|
18
|
+
static async create(workspace) {
|
|
19
|
+
const dbPath = PathHelper.getWorkspaceDbPath(workspace.workspaceRoot);
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(dbPath);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`No workspace DB found at ${dbPath}. Run mcoda create-tasks first or ensure it exists.`);
|
|
25
|
+
}
|
|
26
|
+
const connection = await Connection.open(dbPath);
|
|
27
|
+
const ok = await hasTables(connection.db, ["projects", "epics", "user_stories", "tasks"]);
|
|
28
|
+
if (!ok) {
|
|
29
|
+
await connection.close();
|
|
30
|
+
throw new Error(`Workspace DB at ${dbPath} is missing required tables. Re-run create-tasks to seed it.`);
|
|
31
|
+
}
|
|
32
|
+
const repo = new WorkspaceRepository(connection.db, connection);
|
|
33
|
+
return new TaskDetailService(workspace, connection.db, repo, undefined, new TaskApiResolver());
|
|
34
|
+
}
|
|
35
|
+
async close() {
|
|
36
|
+
await this.repo.close();
|
|
37
|
+
if (this.globalRepo) {
|
|
38
|
+
await this.globalRepo.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
getRepository() {
|
|
42
|
+
return this.repo;
|
|
43
|
+
}
|
|
44
|
+
async getTaskDetail(options) {
|
|
45
|
+
if (!options.taskKey) {
|
|
46
|
+
throw new Error("taskKey is required");
|
|
47
|
+
}
|
|
48
|
+
const task = await this.resolveTask(options.taskKey, options.projectKey);
|
|
49
|
+
const dependencies = await this.getDependencies(task.id);
|
|
50
|
+
const comments = await this.repo.listTaskComments(task.id, { limit: options.commentsLimit ?? 20 });
|
|
51
|
+
const logs = options.includeLogs ? await this.getLogs(task.id, options.logsLimit ?? 20) : undefined;
|
|
52
|
+
const history = options.includeHistory ? await this.getHistory(task.id, options.historyLimit ?? 20) : undefined;
|
|
53
|
+
return {
|
|
54
|
+
task,
|
|
55
|
+
dependencies,
|
|
56
|
+
comments,
|
|
57
|
+
logs,
|
|
58
|
+
history,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async resolveTask(taskKey, projectKey) {
|
|
62
|
+
const openApiTaskId = await this.apiResolver.resolveTaskId(taskKey, projectKey);
|
|
63
|
+
if (openApiTaskId) {
|
|
64
|
+
return this.resolveTaskById(openApiTaskId);
|
|
65
|
+
}
|
|
66
|
+
const clauses = ["t.key = ?"];
|
|
67
|
+
const params = [taskKey];
|
|
68
|
+
if (projectKey) {
|
|
69
|
+
clauses.push("p.key = ?");
|
|
70
|
+
params.push(projectKey);
|
|
71
|
+
}
|
|
72
|
+
const where = `WHERE ${clauses.join(" AND ")}`;
|
|
73
|
+
const rows = await this.db.all(`
|
|
74
|
+
SELECT
|
|
75
|
+
t.id AS task_id,
|
|
76
|
+
t.key AS task_key,
|
|
77
|
+
t.title AS task_title,
|
|
78
|
+
t.description AS task_description,
|
|
79
|
+
t.type AS task_type,
|
|
80
|
+
t.status AS task_status,
|
|
81
|
+
t.story_points AS task_story_points,
|
|
82
|
+
t.priority AS task_priority,
|
|
83
|
+
t.assigned_agent_id AS assigned_agent_id,
|
|
84
|
+
t.assignee_human AS assignee_human,
|
|
85
|
+
t.vcs_branch AS vcs_branch,
|
|
86
|
+
t.vcs_base_branch AS vcs_base_branch,
|
|
87
|
+
t.vcs_last_commit_sha AS vcs_last_commit_sha,
|
|
88
|
+
t.metadata_json AS metadata_json,
|
|
89
|
+
t.created_at AS created_at,
|
|
90
|
+
t.updated_at AS updated_at,
|
|
91
|
+
p.id AS project_id,
|
|
92
|
+
p.key AS project_key,
|
|
93
|
+
p.name AS project_name,
|
|
94
|
+
e.id AS epic_id,
|
|
95
|
+
e.key AS epic_key,
|
|
96
|
+
e.title AS epic_title,
|
|
97
|
+
us.id AS story_id,
|
|
98
|
+
us.key AS story_key,
|
|
99
|
+
us.title AS story_title
|
|
100
|
+
FROM tasks t
|
|
101
|
+
INNER JOIN projects p ON p.id = t.project_id
|
|
102
|
+
INNER JOIN epics e ON e.id = t.epic_id
|
|
103
|
+
INNER JOIN user_stories us ON us.id = t.user_story_id
|
|
104
|
+
${where}
|
|
105
|
+
`, params);
|
|
106
|
+
if (!rows || rows.length === 0) {
|
|
107
|
+
const suffix = projectKey ? ` in project "${projectKey}"` : "";
|
|
108
|
+
throw new Error(`No task with key "${taskKey}"${suffix}.`);
|
|
109
|
+
}
|
|
110
|
+
if (rows.length > 1 && !projectKey) {
|
|
111
|
+
throw new Error(`Multiple tasks found with key "${taskKey}"; please specify --project.`);
|
|
112
|
+
}
|
|
113
|
+
const row = rows[0];
|
|
114
|
+
const assignedAgentSlug = await this.resolveAgentSlug(row.assigned_agent_id);
|
|
115
|
+
return {
|
|
116
|
+
id: row.task_id,
|
|
117
|
+
key: row.task_key,
|
|
118
|
+
title: row.task_title,
|
|
119
|
+
description: row.task_description,
|
|
120
|
+
type: row.task_type,
|
|
121
|
+
status: row.task_status,
|
|
122
|
+
storyPoints: row.task_story_points,
|
|
123
|
+
priority: row.task_priority,
|
|
124
|
+
assignedAgentId: row.assigned_agent_id,
|
|
125
|
+
assignedAgentSlug: assignedAgentSlug ?? null,
|
|
126
|
+
assigneeHuman: row.assignee_human,
|
|
127
|
+
vcsBranch: row.vcs_branch,
|
|
128
|
+
vcsBaseBranch: row.vcs_base_branch,
|
|
129
|
+
vcsLastCommitSha: row.vcs_last_commit_sha,
|
|
130
|
+
metadata: row.metadata_json ? JSON.parse(row.metadata_json) : null,
|
|
131
|
+
project: {
|
|
132
|
+
id: row.project_id,
|
|
133
|
+
key: row.project_key,
|
|
134
|
+
name: row.project_name,
|
|
135
|
+
},
|
|
136
|
+
epic: {
|
|
137
|
+
id: row.epic_id,
|
|
138
|
+
key: row.epic_key,
|
|
139
|
+
title: row.epic_title,
|
|
140
|
+
},
|
|
141
|
+
story: {
|
|
142
|
+
id: row.story_id,
|
|
143
|
+
key: row.story_key,
|
|
144
|
+
title: row.story_title,
|
|
145
|
+
},
|
|
146
|
+
createdAt: row.created_at,
|
|
147
|
+
updatedAt: row.updated_at,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async getDependencies(taskId) {
|
|
151
|
+
const upstream = await this.db.all(`
|
|
152
|
+
SELECT td.depends_on_task_id AS taskId, dep.key AS key, dep.status AS status, td.relation_type AS relationType
|
|
153
|
+
FROM task_dependencies td
|
|
154
|
+
INNER JOIN tasks dep ON dep.id = td.depends_on_task_id
|
|
155
|
+
WHERE td.task_id = ?
|
|
156
|
+
ORDER BY dep.key
|
|
157
|
+
`, taskId);
|
|
158
|
+
const downstream = await this.db.all(`
|
|
159
|
+
SELECT td.task_id AS taskId, t.key AS key, t.status AS status, td.relation_type AS relationType
|
|
160
|
+
FROM task_dependencies td
|
|
161
|
+
INNER JOIN tasks t ON t.id = td.task_id
|
|
162
|
+
WHERE td.depends_on_task_id = ?
|
|
163
|
+
ORDER BY t.key
|
|
164
|
+
`, taskId);
|
|
165
|
+
return { upstream: upstream ?? [], downstream: downstream ?? [] };
|
|
166
|
+
}
|
|
167
|
+
async resolveTaskById(taskId) {
|
|
168
|
+
const rows = await this.db.all(`
|
|
169
|
+
SELECT
|
|
170
|
+
t.id AS task_id,
|
|
171
|
+
t.key AS task_key,
|
|
172
|
+
t.title AS task_title,
|
|
173
|
+
t.description AS task_description,
|
|
174
|
+
t.type AS task_type,
|
|
175
|
+
t.status AS task_status,
|
|
176
|
+
t.story_points AS task_story_points,
|
|
177
|
+
t.priority AS task_priority,
|
|
178
|
+
t.assigned_agent_id AS assigned_agent_id,
|
|
179
|
+
t.assignee_human AS assignee_human,
|
|
180
|
+
t.vcs_branch AS vcs_branch,
|
|
181
|
+
t.vcs_base_branch AS vcs_base_branch,
|
|
182
|
+
t.vcs_last_commit_sha AS vcs_last_commit_sha,
|
|
183
|
+
t.metadata_json AS metadata_json,
|
|
184
|
+
t.created_at AS created_at,
|
|
185
|
+
t.updated_at AS updated_at,
|
|
186
|
+
p.id AS project_id,
|
|
187
|
+
p.key AS project_key,
|
|
188
|
+
p.name AS project_name,
|
|
189
|
+
e.id AS epic_id,
|
|
190
|
+
e.key AS epic_key,
|
|
191
|
+
e.title AS epic_title,
|
|
192
|
+
us.id AS story_id,
|
|
193
|
+
us.key AS story_key,
|
|
194
|
+
us.title AS story_title
|
|
195
|
+
FROM tasks t
|
|
196
|
+
INNER JOIN projects p ON p.id = t.project_id
|
|
197
|
+
INNER JOIN epics e ON e.id = t.epic_id
|
|
198
|
+
INNER JOIN user_stories us ON us.id = t.user_story_id
|
|
199
|
+
WHERE t.id = ?
|
|
200
|
+
`, [taskId]);
|
|
201
|
+
if (!rows || rows.length === 0) {
|
|
202
|
+
throw new Error(`Task with id "${taskId}" not found in local DB.`);
|
|
203
|
+
}
|
|
204
|
+
const row = rows[0];
|
|
205
|
+
const assignedAgentSlug = await this.resolveAgentSlug(row.assigned_agent_id);
|
|
206
|
+
return {
|
|
207
|
+
id: row.task_id,
|
|
208
|
+
key: row.task_key,
|
|
209
|
+
title: row.task_title,
|
|
210
|
+
description: row.task_description,
|
|
211
|
+
type: row.task_type,
|
|
212
|
+
status: row.task_status,
|
|
213
|
+
storyPoints: row.task_story_points,
|
|
214
|
+
priority: row.task_priority,
|
|
215
|
+
assignedAgentId: row.assigned_agent_id,
|
|
216
|
+
assignedAgentSlug: assignedAgentSlug ?? null,
|
|
217
|
+
assigneeHuman: row.assignee_human,
|
|
218
|
+
vcsBranch: row.vcs_branch,
|
|
219
|
+
vcsBaseBranch: row.vcs_base_branch,
|
|
220
|
+
vcsLastCommitSha: row.vcs_last_commit_sha,
|
|
221
|
+
metadata: row.metadata_json ? JSON.parse(row.metadata_json) : null,
|
|
222
|
+
project: {
|
|
223
|
+
id: row.project_id,
|
|
224
|
+
key: row.project_key,
|
|
225
|
+
name: row.project_name,
|
|
226
|
+
},
|
|
227
|
+
epic: {
|
|
228
|
+
id: row.epic_id,
|
|
229
|
+
key: row.epic_key,
|
|
230
|
+
title: row.epic_title,
|
|
231
|
+
},
|
|
232
|
+
story: {
|
|
233
|
+
id: row.story_id,
|
|
234
|
+
key: row.story_key,
|
|
235
|
+
title: row.story_title,
|
|
236
|
+
},
|
|
237
|
+
createdAt: row.created_at,
|
|
238
|
+
updatedAt: row.updated_at,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async getLogs(taskId, limit) {
|
|
242
|
+
const rows = await this.db.all(`
|
|
243
|
+
SELECT l.task_run_id, l.timestamp, l.level, l.source, l.message, l.details_json, r.command, r.status
|
|
244
|
+
FROM task_logs l
|
|
245
|
+
INNER JOIN task_runs r ON r.id = l.task_run_id
|
|
246
|
+
WHERE r.task_id = ?
|
|
247
|
+
ORDER BY datetime(l.timestamp) DESC
|
|
248
|
+
LIMIT ?
|
|
249
|
+
`, taskId, limit);
|
|
250
|
+
return (rows ?? []).map((row) => ({
|
|
251
|
+
taskRunId: row.task_run_id,
|
|
252
|
+
timestamp: row.timestamp,
|
|
253
|
+
level: row.level,
|
|
254
|
+
source: row.source,
|
|
255
|
+
message: row.message,
|
|
256
|
+
command: row.command,
|
|
257
|
+
status: row.status,
|
|
258
|
+
details: row.details_json ? JSON.parse(row.details_json) : null,
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
async resolveAgentSlug(agentId) {
|
|
262
|
+
if (!agentId)
|
|
263
|
+
return undefined;
|
|
264
|
+
if (!this.globalRepo) {
|
|
265
|
+
this.globalRepo = await GlobalRepository.create();
|
|
266
|
+
}
|
|
267
|
+
const agent = await this.globalRepo.getAgentById(agentId);
|
|
268
|
+
return agent?.slug ?? undefined;
|
|
269
|
+
}
|
|
270
|
+
async getHistory(taskId, limit) {
|
|
271
|
+
const rows = await this.db.all(`
|
|
272
|
+
SELECT id, job_id, command_run_id, snapshot_before_json, snapshot_after_json, created_at
|
|
273
|
+
FROM task_revisions
|
|
274
|
+
WHERE task_id = ?
|
|
275
|
+
ORDER BY datetime(created_at) DESC
|
|
276
|
+
LIMIT ?
|
|
277
|
+
`, taskId, limit);
|
|
278
|
+
return (rows ?? []).map((row) => {
|
|
279
|
+
const before = row.snapshot_before_json ? this.safeParse(row.snapshot_before_json) : null;
|
|
280
|
+
const after = row.snapshot_after_json ? this.safeParse(row.snapshot_after_json) : null;
|
|
281
|
+
const statusBefore = this.pickField(before, ["status", "task_status"]);
|
|
282
|
+
const statusAfter = this.pickField(after, ["status", "task_status"]);
|
|
283
|
+
const spBefore = this.pickField(before, ["story_points", "storyPoints", "task_story_points"]);
|
|
284
|
+
const spAfter = this.pickField(after, ["story_points", "storyPoints", "task_story_points"]);
|
|
285
|
+
const changedFields = this.diffFields(before, after);
|
|
286
|
+
return {
|
|
287
|
+
id: row.id,
|
|
288
|
+
jobId: row.job_id,
|
|
289
|
+
commandRunId: row.command_run_id,
|
|
290
|
+
changedAt: row.created_at,
|
|
291
|
+
statusBefore: statusBefore ?? undefined,
|
|
292
|
+
statusAfter: statusAfter ?? undefined,
|
|
293
|
+
storyPointsBefore: spBefore ?? null,
|
|
294
|
+
storyPointsAfter: spAfter ?? null,
|
|
295
|
+
changedFields,
|
|
296
|
+
snapshotBefore: before,
|
|
297
|
+
snapshotAfter: after,
|
|
298
|
+
};
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
safeParse(raw) {
|
|
302
|
+
try {
|
|
303
|
+
return JSON.parse(raw);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
pickField(obj, keys) {
|
|
310
|
+
if (!obj)
|
|
311
|
+
return undefined;
|
|
312
|
+
for (const key of keys) {
|
|
313
|
+
if (obj[key] !== undefined)
|
|
314
|
+
return obj[key];
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
diffFields(before, after) {
|
|
319
|
+
if (!before || !after)
|
|
320
|
+
return undefined;
|
|
321
|
+
const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
322
|
+
const changed = [];
|
|
323
|
+
for (const key of keys) {
|
|
324
|
+
const lhs = before[key];
|
|
325
|
+
const rhs = after[key];
|
|
326
|
+
const same = JSON.stringify(lhs) === JSON.stringify(rhs);
|
|
327
|
+
if (!same)
|
|
328
|
+
changed.push(key);
|
|
329
|
+
}
|
|
330
|
+
return changed.length ? changed : undefined;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TokenUsageRow, TokenUsageSummaryRow, type TelemetryConfig as ApiTelemetryConfig } from "@mcoda/integrations";
|
|
2
|
+
import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
|
|
3
|
+
export type { TokenUsageRow, TokenUsageSummaryRow } from "@mcoda/integrations";
|
|
4
|
+
type GroupByDimension = "project" | "agent" | "command" | "day" | "model" | "job" | "action";
|
|
5
|
+
export interface TelemetryConfigState extends ApiTelemetryConfig {
|
|
6
|
+
configPath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TelemetrySummaryOptions {
|
|
9
|
+
projectKey?: string;
|
|
10
|
+
agent?: string;
|
|
11
|
+
command?: string;
|
|
12
|
+
jobId?: string;
|
|
13
|
+
since?: string;
|
|
14
|
+
until?: string;
|
|
15
|
+
groupBy?: GroupByDimension[];
|
|
16
|
+
}
|
|
17
|
+
export interface TokenUsageQueryOptions {
|
|
18
|
+
projectKey?: string;
|
|
19
|
+
agent?: string;
|
|
20
|
+
command?: string;
|
|
21
|
+
jobId?: string;
|
|
22
|
+
since?: string;
|
|
23
|
+
until?: string;
|
|
24
|
+
page?: number;
|
|
25
|
+
pageSize?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare class TelemetryService {
|
|
28
|
+
private workspace;
|
|
29
|
+
private globalRepo?;
|
|
30
|
+
private client?;
|
|
31
|
+
private db?;
|
|
32
|
+
private connection?;
|
|
33
|
+
private constructor();
|
|
34
|
+
static create(workspace: WorkspaceResolution, options?: {
|
|
35
|
+
allowMissingTelemetry?: boolean;
|
|
36
|
+
requireApi?: boolean;
|
|
37
|
+
}): Promise<TelemetryService>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
private get configPath();
|
|
40
|
+
private readConfigFile;
|
|
41
|
+
private writeConfigFile;
|
|
42
|
+
private resolveProjectId;
|
|
43
|
+
private getGlobalRepo;
|
|
44
|
+
private resolveAgentId;
|
|
45
|
+
private filterRows;
|
|
46
|
+
private mapConfig;
|
|
47
|
+
getSummary(options?: TelemetrySummaryOptions): Promise<TokenUsageSummaryRow[]>;
|
|
48
|
+
getTokenUsage(options?: TokenUsageQueryOptions): Promise<TokenUsageRow[]>;
|
|
49
|
+
getConfig(): Promise<TelemetryConfigState>;
|
|
50
|
+
optOut(strict?: boolean): Promise<TelemetryConfigState>;
|
|
51
|
+
optIn(): Promise<TelemetryConfigState>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=TelemetryService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelemetryService.d.ts","sourceRoot":"","sources":["../../../src/services/telemetry/TelemetryService.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,aAAa,EAAE,oBAAoB,EAAE,KAAK,eAAe,IAAI,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACvI,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE1E,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,KAAK,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE7F,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0FD,qBAAa,gBAAgB;IAMP,OAAO,CAAC,SAAS;IALrC,OAAO,CAAC,UAAU,CAAC,CAAmB;IACtC,OAAO,CAAC,MAAM,CAAC,CAAkB;IACjC,OAAO,CAAC,EAAE,CAAC,CAAW;IACtB,OAAO,CAAC,UAAU,CAAC,CAAa;IAEhC,OAAO;WAMM,MAAM,CACjB,SAAS,EAAE,mBAAmB,EAC9B,OAAO,GAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GACtE,OAAO,CAAC,gBAAgB,CAAC;IA6BtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,KAAK,UAAU,GAErB;YAEa,cAAc;YASd,eAAe;YAKf,gBAAgB;YAMhB,aAAa;YAOb,cAAc;IAc5B,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,SAAS;IAWX,UAAU,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IA6GlF,aAAa,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAuG7E,SAAS,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAqB1C,MAAM,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAerD,KAAK,IAAI,OAAO,CAAC,oBAAoB,CAAC;CAc7C"}
|