@bakers-build/addon-track 0.3.0 → 0.3.1-next.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/evidence.d.ts +7 -1
- package/dist/src/evidence.js +65 -29
- package/dist/src/index.js +11 -7
- package/package.json +3 -2
package/dist/src/evidence.d.ts
CHANGED
|
@@ -5,7 +5,13 @@ type Selection = {
|
|
|
5
5
|
to: string;
|
|
6
6
|
timezone?: string;
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
/** The slice of `@dropsh/plugin-tool`'s ToolClient this reader needs. */
|
|
9
|
+
export type WorklogTool = {
|
|
10
|
+
call(name: string, args: Record<string, unknown>): Promise<{
|
|
11
|
+
structuredContent?: unknown;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export declare function loadEvidence(client: Client, selection: Selection, worklogTool: WorklogTool): Promise<{
|
|
9
15
|
owner_id: string;
|
|
10
16
|
timezone: string;
|
|
11
17
|
from: string;
|
package/dist/src/evidence.js
CHANGED
|
@@ -18,14 +18,14 @@ function rows(value) {
|
|
|
18
18
|
typeof row.id === 'string' &&
|
|
19
19
|
typeof row.type === 'string'));
|
|
20
20
|
}
|
|
21
|
-
async function collect(client, type, params
|
|
21
|
+
async function collect(client, type, params) {
|
|
22
22
|
const data = new Map();
|
|
23
23
|
const included = new Map();
|
|
24
24
|
let complete = true;
|
|
25
25
|
let offset = 0;
|
|
26
26
|
while (true) {
|
|
27
27
|
params.addPageLimit(50).addPageOffset(offset);
|
|
28
|
-
const raw = await client.get(
|
|
28
|
+
const raw = await client.get(`${type}/${type}`, params);
|
|
29
29
|
if (!raw || typeof raw !== 'object' || !('data' in raw) || !rows(raw.data))
|
|
30
30
|
throw new Error(`Invalid ${type} collection response.`);
|
|
31
31
|
const page = raw;
|
|
@@ -62,13 +62,52 @@ async function collect(client, type, params, path = `${type}/${type}`) {
|
|
|
62
62
|
complete,
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
const ided = (value) => Array.isArray(value) &&
|
|
66
|
+
value.every((row) => row && typeof row === 'object' && typeof row.id === 'string');
|
|
67
|
+
/** Page `tool_api__bakers_worklog_list` (the owner's own worklogs) until next_offset is null. */
|
|
68
|
+
async function collectWorklogs(tool, from, to) {
|
|
69
|
+
const data = new Map();
|
|
70
|
+
const tickets = new Map();
|
|
71
|
+
const projects = new Map();
|
|
72
|
+
let complete = true;
|
|
73
|
+
let offset = 0;
|
|
74
|
+
while (true) {
|
|
75
|
+
const { structuredContent: page } = await tool.call('tool_api__bakers_worklog_list', { from, to, limit: 50, offset });
|
|
76
|
+
if (!page ||
|
|
77
|
+
typeof page !== 'object' ||
|
|
78
|
+
!ided(page.data) ||
|
|
79
|
+
!ided(page.tickets) ||
|
|
80
|
+
!ided(page.projects))
|
|
81
|
+
throw new Error('Invalid worklog tool response.');
|
|
82
|
+
const { data: rows, omitted, next_offset: next } = page;
|
|
83
|
+
if (omitted)
|
|
84
|
+
complete = false;
|
|
85
|
+
for (const row of rows)
|
|
86
|
+
data.set(row.id, row);
|
|
87
|
+
for (const row of page.tickets)
|
|
88
|
+
tickets.set(row.id, row);
|
|
89
|
+
for (const row of page.projects)
|
|
90
|
+
projects.set(row.id, row);
|
|
91
|
+
if (next === null || next === undefined)
|
|
92
|
+
break;
|
|
93
|
+
if (!Number.isSafeInteger(next) || next <= offset)
|
|
94
|
+
throw new Error('Non-advancing worklog pagination.');
|
|
95
|
+
offset = next;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
data: [...data.values()],
|
|
99
|
+
tickets: [...tickets.values()],
|
|
100
|
+
projects: [...projects.values()],
|
|
101
|
+
complete,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
65
104
|
function context(params) {
|
|
66
105
|
return params
|
|
67
106
|
.addInclude(['ticket_id.project_id'])
|
|
68
107
|
.addFields('bb_ticket--bb_ticket', ['identifier', 'title', 'project_id'])
|
|
69
108
|
.addFields('bb_project--bb_project', ['name']);
|
|
70
109
|
}
|
|
71
|
-
export async function loadEvidence(client, selection,
|
|
110
|
+
export async function loadEvidence(client, selection, worklogTool) {
|
|
72
111
|
const dates = selectedDates(selection.from, selection.to);
|
|
73
112
|
if (selection.timezone)
|
|
74
113
|
calendar(selection.timezone);
|
|
@@ -131,14 +170,10 @@ export async function loadEvidence(client, selection, worklogClient) {
|
|
|
131
170
|
])
|
|
132
171
|
.addFields('bb_comment--bb_comment', ['body'])
|
|
133
172
|
.addSort('drupal_internal__id');
|
|
134
|
-
const worklogParams = new DrupalJsonApiParams().addCustomParam({
|
|
135
|
-
from: weekStart,
|
|
136
|
-
to: weekEnd,
|
|
137
|
-
});
|
|
138
173
|
const [runPage, activityPage, worklogPage] = await Promise.all([
|
|
139
174
|
collect(client, 'bb_run', runParams),
|
|
140
175
|
collect(client, 'bb_activity', activityParams),
|
|
141
|
-
|
|
176
|
+
collectWorklogs(worklogTool, weekStart, weekEnd),
|
|
142
177
|
]);
|
|
143
178
|
for (const [name, page] of [
|
|
144
179
|
['runs', runPage],
|
|
@@ -151,11 +186,7 @@ export async function loadEvidence(client, selection, worklogClient) {
|
|
|
151
186
|
const tickets = {};
|
|
152
187
|
const projects = {};
|
|
153
188
|
const summaries = new Map();
|
|
154
|
-
for (const row of [
|
|
155
|
-
...runPage.included,
|
|
156
|
-
...activityPage.included,
|
|
157
|
-
...worklogPage.included,
|
|
158
|
-
]) {
|
|
189
|
+
for (const row of [...runPage.included, ...activityPage.included]) {
|
|
159
190
|
if (row.type === 'bb_ticket--bb_ticket')
|
|
160
191
|
tickets[row.id] = {
|
|
161
192
|
identifier: attr(row, 'identifier') ?? null,
|
|
@@ -170,6 +201,14 @@ export async function loadEvidence(client, selection, worklogClient) {
|
|
|
170
201
|
summaries.set(row.id, body.value);
|
|
171
202
|
}
|
|
172
203
|
}
|
|
204
|
+
for (const ticket of worklogPage.tickets)
|
|
205
|
+
tickets[ticket.id] = {
|
|
206
|
+
identifier: ticket.identifier ?? null,
|
|
207
|
+
title: ticket.title ?? null,
|
|
208
|
+
project_id: ticket.project_id ?? null,
|
|
209
|
+
};
|
|
210
|
+
for (const project of worklogPage.projects)
|
|
211
|
+
projects[project.id] = { name: project.name ?? null };
|
|
173
212
|
const unplaced = [];
|
|
174
213
|
for (const [id, ticket] of Object.entries(tickets))
|
|
175
214
|
if (ticket.project_id && !projects[ticket.project_id])
|
|
@@ -249,24 +288,21 @@ export async function loadEvidence(client, selection, worklogClient) {
|
|
|
249
288
|
summary_truncated: (summary?.length ?? 0) > 2000,
|
|
250
289
|
};
|
|
251
290
|
});
|
|
252
|
-
const worklogs = worklogPage.data
|
|
253
|
-
.filter((row) => relation(row, 'owner_user_id') === owner)
|
|
254
|
-
.map((row) => ({
|
|
291
|
+
const worklogs = worklogPage.data.map((row) => ({
|
|
255
292
|
id: row.id,
|
|
256
|
-
date:
|
|
257
|
-
state:
|
|
258
|
-
duration_seconds: seconds(
|
|
259
|
-
description:
|
|
260
|
-
ticket_id:
|
|
261
|
-
project_id:
|
|
262
|
-
submission_id:
|
|
263
|
-
source_keys:
|
|
293
|
+
date: row.work_date,
|
|
294
|
+
state: row.state,
|
|
295
|
+
duration_seconds: seconds(row.duration_seconds),
|
|
296
|
+
description: row.description,
|
|
297
|
+
ticket_id: row.ticket_id ?? null,
|
|
298
|
+
project_id: row.project_id ?? null,
|
|
299
|
+
submission_id: row.submission_id ?? null,
|
|
300
|
+
source_keys: row.source_keys ?? null,
|
|
264
301
|
}));
|
|
265
|
-
const worklogsValid =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
['recorded', 'approved', 'proposed'].includes(String(row.state)));
|
|
302
|
+
const worklogsValid = worklogs.every((row) => typeof row.date === 'string' &&
|
|
303
|
+
Number.isFinite(civilDate(row.date)) &&
|
|
304
|
+
row.duration_seconds !== null &&
|
|
305
|
+
['recorded', 'approved', 'proposed'].includes(String(row.state)));
|
|
270
306
|
if (!worklogsValid)
|
|
271
307
|
evidenceGaps.push('Some worklogs have invalid dates, states or durations.');
|
|
272
308
|
for (const row of [...activities, ...worklogs]) {
|
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createBbConnectionContext, loadReadyBbConnection, } from '@bakers-build/core';
|
|
2
|
+
import { createToolClient } from '@dropsh/plugin-tool';
|
|
2
3
|
import { loadEvidence } from './evidence.js';
|
|
3
4
|
export function registerTrack(program, read) {
|
|
4
5
|
program
|
|
@@ -26,12 +27,15 @@ const trackCommand = {
|
|
|
26
27
|
...(options.config ? { explicit: options.config } : {}),
|
|
27
28
|
...(options.connection ? { connection: options.connection } : {}),
|
|
28
29
|
});
|
|
29
|
-
const client = await
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const { client, auth, baseUrl } = await createBbConnectionContext(connection);
|
|
31
|
+
// Worklogs come from the `tool_api__bakers_worklog_list` MCP tool on the same auth profile.
|
|
32
|
+
const tools = createToolClient({ auth, baseUrl, endpoint: '/mcp' });
|
|
33
|
+
try {
|
|
34
|
+
return await loadEvidence(client, options, tools);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await tools.close();
|
|
38
|
+
}
|
|
35
39
|
});
|
|
36
40
|
},
|
|
37
41
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakers-build/addon-track",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1-next.308",
|
|
4
4
|
"description": "Read personal daily evidence for time preparation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
"directory": "bakers-cli/addons/track"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@dropsh/plugin-tool": "^0.8.2",
|
|
23
24
|
"commander": "^12.1.0",
|
|
24
25
|
"drupal-jsonapi-params": "^3.0.1"
|
|
25
26
|
},
|
|
26
27
|
"peerDependencies": {
|
|
27
|
-
"@bakers-build/core": "^0.3.
|
|
28
|
+
"@bakers-build/core": "^0.3.1-next.308"
|
|
28
29
|
}
|
|
29
30
|
}
|