workday-daemon 0.1.0
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/README.md +95 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +710 -0
- package/dist/cli.js.map +1 -0
- package/dist/collectors/git-client.d.ts +16 -0
- package/dist/collectors/git-client.js +58 -0
- package/dist/collectors/git-client.js.map +1 -0
- package/dist/collectors/git-tracker.d.ts +43 -0
- package/dist/collectors/git-tracker.js +168 -0
- package/dist/collectors/git-tracker.js.map +1 -0
- package/dist/collectors/reflog-parser.d.ts +30 -0
- package/dist/collectors/reflog-parser.js +71 -0
- package/dist/collectors/reflog-parser.js.map +1 -0
- package/dist/collectors/snapshot-parser.d.ts +34 -0
- package/dist/collectors/snapshot-parser.js +79 -0
- package/dist/collectors/snapshot-parser.js.map +1 -0
- package/dist/core/activity-evaluator.d.ts +33 -0
- package/dist/core/activity-evaluator.js +94 -0
- package/dist/core/activity-evaluator.js.map +1 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +174 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/constants.d.ts +39 -0
- package/dist/core/constants.js +51 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/daily-log.d.ts +56 -0
- package/dist/core/daily-log.js +366 -0
- package/dist/core/daily-log.js.map +1 -0
- package/dist/core/session-tracker.d.ts +110 -0
- package/dist/core/session-tracker.js +460 -0
- package/dist/core/session-tracker.js.map +1 -0
- package/dist/core/status-renderer.d.ts +20 -0
- package/dist/core/status-renderer.js +178 -0
- package/dist/core/status-renderer.js.map +1 -0
- package/dist/core/types.d.ts +320 -0
- package/dist/core/types.js +52 -0
- package/dist/core/types.js.map +1 -0
- package/dist/daemon.d.ts +31 -0
- package/dist/daemon.js +266 -0
- package/dist/daemon.js.map +1 -0
- package/dist/http-server.d.ts +30 -0
- package/dist/http-server.js +342 -0
- package/dist/http-server.js.map +1 -0
- package/dist/push/jira-client.d.ts +5 -0
- package/dist/push/jira-client.js +84 -0
- package/dist/push/jira-client.js.map +1 -0
- package/dist/push/report-builder.d.ts +9 -0
- package/dist/push/report-builder.js +77 -0
- package/dist/push/report-builder.js.map +1 -0
- package/dist/push/tempo-client.d.ts +27 -0
- package/dist/push/tempo-client.js +92 -0
- package/dist/push/tempo-client.js.map +1 -0
- package/dist/push/tempo-pusher.d.ts +17 -0
- package/dist/push/tempo-pusher.js +317 -0
- package/dist/push/tempo-pusher.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, copyFileSync, renameSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { getDataDir, computeWorkingDate } from './config.js';
|
|
5
|
+
import { DayStatus, DayType, SignalType } from './types.js';
|
|
6
|
+
import { TMP_EXTENSION, BACKUP_EXTENSION, MAX_ADJUSTMENT_MINUTES, MS_PER_MINUTE } from './constants.js';
|
|
7
|
+
/** Generate short unique session id */
|
|
8
|
+
export function generateSessionId() {
|
|
9
|
+
return randomBytes(4).toString('hex');
|
|
10
|
+
}
|
|
11
|
+
/** Get data file path for a given date: data/YYYY-MM/MM-DD.json */
|
|
12
|
+
export function getDailyLogPath(date) {
|
|
13
|
+
const [year, month, day] = date.split('-');
|
|
14
|
+
const monthDir = `${year}-${month}`;
|
|
15
|
+
const fileName = `${month}-${day}.json`;
|
|
16
|
+
return join(getDataDir(), monthDir, fileName);
|
|
17
|
+
}
|
|
18
|
+
/** Ensure the data directory for a given date exists */
|
|
19
|
+
function ensureDataDir(date) {
|
|
20
|
+
const filePath = getDailyLogPath(date);
|
|
21
|
+
const dir = dirname(filePath);
|
|
22
|
+
if (!existsSync(dir)) {
|
|
23
|
+
mkdirSync(dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Determine day type based on config */
|
|
27
|
+
function determineDayType(date, config) {
|
|
28
|
+
if (config.holidays.includes(date)) {
|
|
29
|
+
return DayType.Holiday;
|
|
30
|
+
}
|
|
31
|
+
const dt = new Date(date + 'T12:00:00');
|
|
32
|
+
const dayOfWeek = dt.getDay();
|
|
33
|
+
// JS: 0=Sun, 1=Mon..6=Sat. Config uses ISO: 1=Mon..7=Sun
|
|
34
|
+
const isoDay = dayOfWeek === 0 ? 7 : dayOfWeek;
|
|
35
|
+
if (!config.workDays.includes(isoDay)) {
|
|
36
|
+
return DayType.Weekend;
|
|
37
|
+
}
|
|
38
|
+
return DayType.Workday;
|
|
39
|
+
}
|
|
40
|
+
/** Create empty daily log for a given date */
|
|
41
|
+
export function createEmptyLog(date, config) {
|
|
42
|
+
return {
|
|
43
|
+
date,
|
|
44
|
+
status: DayStatus.Draft,
|
|
45
|
+
dayType: determineDayType(date, config),
|
|
46
|
+
manualStart: null,
|
|
47
|
+
dayStartedAt: null,
|
|
48
|
+
sessions: [],
|
|
49
|
+
signals: [],
|
|
50
|
+
pushedAt: null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Create empty evidence object */
|
|
54
|
+
export function createEmptyEvidence() {
|
|
55
|
+
return {
|
|
56
|
+
commits: 0,
|
|
57
|
+
reflogEvents: 0,
|
|
58
|
+
linesAdded: 0,
|
|
59
|
+
linesRemoved: 0,
|
|
60
|
+
filesChanged: 0,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Try parsing JSON from a file path. Returns parsed object or null. */
|
|
64
|
+
function tryParseLogFile(filePath) {
|
|
65
|
+
if (!existsSync(filePath))
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
69
|
+
return JSON.parse(raw);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Read daily log from disk. Falls back to .bak if main file is corrupted. */
|
|
76
|
+
export function readDailyLog(date) {
|
|
77
|
+
const filePath = getDailyLogPath(date);
|
|
78
|
+
const log = tryParseLogFile(filePath);
|
|
79
|
+
if (log)
|
|
80
|
+
return log;
|
|
81
|
+
// Main file missing or corrupted — try backup
|
|
82
|
+
const bakPath = filePath + BACKUP_EXTENSION;
|
|
83
|
+
const backup = tryParseLogFile(bakPath);
|
|
84
|
+
if (backup) {
|
|
85
|
+
console.warn(`[daily-log] Restored ${date} from backup (main file corrupted or missing)`);
|
|
86
|
+
// Promote backup to main file
|
|
87
|
+
writeFileSync(filePath, JSON.stringify(backup, null, 2), 'utf-8');
|
|
88
|
+
return backup;
|
|
89
|
+
}
|
|
90
|
+
if (existsSync(filePath)) {
|
|
91
|
+
console.error(`[daily-log] Corrupted JSON in ${filePath}, no backup available`);
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/** Write daily log to disk using atomic write pattern with backup */
|
|
96
|
+
export function writeDailyLog(log) {
|
|
97
|
+
ensureDataDir(log.date);
|
|
98
|
+
const filePath = getDailyLogPath(log.date);
|
|
99
|
+
const tmpPath = filePath + TMP_EXTENSION;
|
|
100
|
+
const bakPath = filePath + BACKUP_EXTENSION;
|
|
101
|
+
// Backup current valid file before overwriting (copy, not rename — safe if crash mid-write)
|
|
102
|
+
if (tryParseLogFile(filePath)) {
|
|
103
|
+
try {
|
|
104
|
+
copyFileSync(filePath, bakPath);
|
|
105
|
+
}
|
|
106
|
+
catch { /* best effort */ }
|
|
107
|
+
}
|
|
108
|
+
writeFileSync(tmpPath, JSON.stringify(log, null, 2), 'utf-8');
|
|
109
|
+
renameSync(tmpPath, filePath);
|
|
110
|
+
}
|
|
111
|
+
/** Get or create today's daily log */
|
|
112
|
+
export function getOrCreateTodayLog(config) {
|
|
113
|
+
const today = computeWorkingDate(Date.now(), config.dayBoundaryHour, config.timezone);
|
|
114
|
+
const existing = readDailyLog(today);
|
|
115
|
+
if (existing) {
|
|
116
|
+
return existing;
|
|
117
|
+
}
|
|
118
|
+
const newLog = createEmptyLog(today, config);
|
|
119
|
+
writeDailyLog(newLog);
|
|
120
|
+
return newLog;
|
|
121
|
+
}
|
|
122
|
+
/** Find session by id */
|
|
123
|
+
export function findSession(log, sessionId) {
|
|
124
|
+
return log.sessions.find(s => s.id === sessionId);
|
|
125
|
+
}
|
|
126
|
+
// ─── Pause helpers ──────────────────────────────────────────────────────
|
|
127
|
+
/** Find open (unclosed) pause in a session */
|
|
128
|
+
export function getOpenPause(session) {
|
|
129
|
+
return session.pauses.find(p => p.to === null) ?? null;
|
|
130
|
+
}
|
|
131
|
+
// ─── Duration helpers ───────────────────────────────────────────────────
|
|
132
|
+
/** Total pause duration for a session in milliseconds */
|
|
133
|
+
export function computeTotalPauseDuration(session) {
|
|
134
|
+
let total = 0;
|
|
135
|
+
for (const pause of session.pauses ?? []) {
|
|
136
|
+
const from = new Date(pause.from).getTime();
|
|
137
|
+
const to = pause.to ? new Date(pause.to).getTime() : Date.now();
|
|
138
|
+
total += to - from;
|
|
139
|
+
}
|
|
140
|
+
return total;
|
|
141
|
+
}
|
|
142
|
+
/** Effective working duration: (end - activatedAt) - pauses, in milliseconds. Returns 0 for PENDING sessions. */
|
|
143
|
+
export function computeEffectiveDuration(session) {
|
|
144
|
+
if (!session.activatedAt)
|
|
145
|
+
return 0;
|
|
146
|
+
const start = new Date(session.activatedAt).getTime();
|
|
147
|
+
const end = session.closedBy ? new Date(session.lastSeenAt).getTime() : Date.now();
|
|
148
|
+
const gross = end - start;
|
|
149
|
+
return Math.max(0, gross - computeTotalPauseDuration(session));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Compute actual work/downtime using interval merge across all sessions.
|
|
153
|
+
* Downtime = periods when NO session was actively working (all paused or no sessions).
|
|
154
|
+
*/
|
|
155
|
+
export function computeDaySummary(sessions) {
|
|
156
|
+
const workIntervals = [];
|
|
157
|
+
for (const session of sessions) {
|
|
158
|
+
if (!session.activatedAt)
|
|
159
|
+
continue;
|
|
160
|
+
const sessionStart = new Date(session.activatedAt).getTime();
|
|
161
|
+
const sessionEnd = session.closedBy
|
|
162
|
+
? new Date(session.lastSeenAt).getTime()
|
|
163
|
+
: Date.now();
|
|
164
|
+
// Build working intervals by subtracting pauses from active range
|
|
165
|
+
const sortedPauses = [...session.pauses]
|
|
166
|
+
.map(p => ({
|
|
167
|
+
from: Math.max(new Date(p.from).getTime(), sessionStart),
|
|
168
|
+
to: Math.min(p.to ? new Date(p.to).getTime() : Date.now(), sessionEnd),
|
|
169
|
+
}))
|
|
170
|
+
.filter(p => p.from < p.to)
|
|
171
|
+
.sort((a, b) => a.from - b.from);
|
|
172
|
+
let cursor = sessionStart;
|
|
173
|
+
for (const pause of sortedPauses) {
|
|
174
|
+
if (pause.from > cursor) {
|
|
175
|
+
workIntervals.push({ from: cursor, to: pause.from });
|
|
176
|
+
}
|
|
177
|
+
cursor = Math.max(cursor, pause.to);
|
|
178
|
+
}
|
|
179
|
+
if (cursor < sessionEnd) {
|
|
180
|
+
workIntervals.push({ from: cursor, to: sessionEnd });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (workIntervals.length === 0) {
|
|
184
|
+
return { workMs: 0, downtimeMs: 0, spanMs: 0 };
|
|
185
|
+
}
|
|
186
|
+
// Merge overlapping work intervals (union)
|
|
187
|
+
workIntervals.sort((a, b) => a.from - b.from);
|
|
188
|
+
const merged = [{ ...workIntervals[0] }];
|
|
189
|
+
for (let i = 1; i < workIntervals.length; i++) {
|
|
190
|
+
const last = merged[merged.length - 1];
|
|
191
|
+
const curr = workIntervals[i];
|
|
192
|
+
if (curr.from <= last.to) {
|
|
193
|
+
last.to = Math.max(last.to, curr.to);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
merged.push({ ...curr });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const spanMs = merged[merged.length - 1].to - merged[0].from;
|
|
200
|
+
const workMs = merged.reduce((sum, iv) => sum + (iv.to - iv.from), 0);
|
|
201
|
+
return { workMs, downtimeMs: spanMs - workMs, spanMs };
|
|
202
|
+
}
|
|
203
|
+
// ─── Session target resolution ───────────────────────────────────────────
|
|
204
|
+
/** Resolve session by 1-based index or hex id */
|
|
205
|
+
export function resolveSessionTarget(log, target) {
|
|
206
|
+
const index = parseInt(target.replace('#', ''), 10);
|
|
207
|
+
if (!isNaN(index) && index >= 1 && index <= log.sessions.length) {
|
|
208
|
+
return log.sessions[index - 1];
|
|
209
|
+
}
|
|
210
|
+
return log.sessions.find(s => s.id === target) ?? null;
|
|
211
|
+
}
|
|
212
|
+
// ─── Budget computation ─────────────────────────────────────────────────
|
|
213
|
+
/** Sum of manual adjustment minutes for a session */
|
|
214
|
+
export function computeManualMinutes(session) {
|
|
215
|
+
if (!session.manualAdjustments || session.manualAdjustments.length === 0)
|
|
216
|
+
return 0;
|
|
217
|
+
return session.manualAdjustments.reduce((sum, a) => sum + a.minutes, 0);
|
|
218
|
+
}
|
|
219
|
+
/** Effective duration including manual adjustments (ms) */
|
|
220
|
+
export function computeFullEffectiveDuration(session) {
|
|
221
|
+
return computeEffectiveDuration(session) + computeManualMinutes(session) * MS_PER_MINUTE;
|
|
222
|
+
}
|
|
223
|
+
/** Resolve dayStart timestamp using priority chain: manualStart → dayStartedAt → first session */
|
|
224
|
+
export function computeDayStart(log, config) {
|
|
225
|
+
if (log.manualStart) {
|
|
226
|
+
return new Date(log.manualStart).getTime();
|
|
227
|
+
}
|
|
228
|
+
if (log.dayStartedAt) {
|
|
229
|
+
return new Date(log.dayStartedAt).getTime();
|
|
230
|
+
}
|
|
231
|
+
// Fallback: first activated session
|
|
232
|
+
for (const s of log.sessions) {
|
|
233
|
+
if (s.activatedAt)
|
|
234
|
+
return new Date(s.activatedAt).getTime();
|
|
235
|
+
}
|
|
236
|
+
// No sessions yet — use day boundary start (date + dayBoundaryHour in timezone)
|
|
237
|
+
return parseDateWithHour(log.date, config.dayBoundaryHour, config.timezone);
|
|
238
|
+
}
|
|
239
|
+
/** Compute day end timestamp (next day boundary) */
|
|
240
|
+
export function computeDayEnd(date, dayBoundaryHour, timezone) {
|
|
241
|
+
// Day end = next calendar day at dayBoundaryHour
|
|
242
|
+
const nextDay = new Date(date + 'T12:00:00Z');
|
|
243
|
+
nextDay.setUTCDate(nextDay.getUTCDate() + 1);
|
|
244
|
+
const nextDateStr = `${nextDay.getUTCFullYear()}-${String(nextDay.getUTCMonth() + 1).padStart(2, '0')}-${String(nextDay.getUTCDate()).padStart(2, '0')}`;
|
|
245
|
+
return parseDateWithHour(nextDateStr, dayBoundaryHour, timezone);
|
|
246
|
+
}
|
|
247
|
+
/** Compute total budget in ms */
|
|
248
|
+
export function computeBudgetMs(log, config) {
|
|
249
|
+
const dayStart = computeDayStart(log, config);
|
|
250
|
+
const dayEnd = computeDayEnd(log.date, config.dayBoundaryHour, config.timezone);
|
|
251
|
+
return Math.max(0, dayEnd - dayStart);
|
|
252
|
+
}
|
|
253
|
+
/** Sum of all sessions' full effective duration (ms) */
|
|
254
|
+
export function computeTotalClaimedMs(log) {
|
|
255
|
+
return log.sessions.reduce((sum, s) => sum + computeFullEffectiveDuration(s), 0);
|
|
256
|
+
}
|
|
257
|
+
/** Check if day budget is exhausted */
|
|
258
|
+
export function isBudgetExhausted(log, config) {
|
|
259
|
+
return computeTotalClaimedMs(log) >= computeBudgetMs(log, config);
|
|
260
|
+
}
|
|
261
|
+
/** Remaining budget in ms, clamped >= 0 */
|
|
262
|
+
export function getRemainingBudgetMs(log, config) {
|
|
263
|
+
return Math.max(0, computeBudgetMs(log, config) - computeTotalClaimedMs(log));
|
|
264
|
+
}
|
|
265
|
+
/** Add manual adjustment to a session. Throws on validation failure. */
|
|
266
|
+
export function addManualAdjustment(log, sessionId, minutes, reason, config) {
|
|
267
|
+
if (log.status !== DayStatus.Draft) {
|
|
268
|
+
throw new Error('Cannot adjust confirmed/pushed day');
|
|
269
|
+
}
|
|
270
|
+
const session = log.sessions.find(s => s.id === sessionId);
|
|
271
|
+
if (!session) {
|
|
272
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
273
|
+
}
|
|
274
|
+
if (minutes <= 0) {
|
|
275
|
+
throw new Error('Minutes must be positive');
|
|
276
|
+
}
|
|
277
|
+
if (minutes > MAX_ADJUSTMENT_MINUTES) {
|
|
278
|
+
throw new Error(`Max adjustment is ${MAX_ADJUSTMENT_MINUTES} minutes (8h)`);
|
|
279
|
+
}
|
|
280
|
+
// Check budget
|
|
281
|
+
const currentClaimed = computeTotalClaimedMs(log);
|
|
282
|
+
const addMs = minutes * MS_PER_MINUTE;
|
|
283
|
+
const budget = computeBudgetMs(log, config);
|
|
284
|
+
if (currentClaimed + addMs > budget) {
|
|
285
|
+
const remainMinutes = Math.floor(getRemainingBudgetMs(log, config) / MS_PER_MINUTE);
|
|
286
|
+
throw new Error(`Exceeds day budget. Remaining: ${remainMinutes}m. Use set-start to extend.`);
|
|
287
|
+
}
|
|
288
|
+
if (!session.manualAdjustments) {
|
|
289
|
+
session.manualAdjustments = [];
|
|
290
|
+
}
|
|
291
|
+
session.manualAdjustments.push({
|
|
292
|
+
minutes,
|
|
293
|
+
reason,
|
|
294
|
+
addedAt: new Date().toISOString(),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
/** Set manual day start. Can only shift earlier. */
|
|
298
|
+
export function setDayManualStart(log, isoTimestamp, config) {
|
|
299
|
+
const newStart = new Date(isoTimestamp).getTime();
|
|
300
|
+
const currentStart = computeDayStart(log, config);
|
|
301
|
+
if (newStart > currentStart) {
|
|
302
|
+
throw new Error('Can only shift day start earlier');
|
|
303
|
+
}
|
|
304
|
+
// Must be >= previous day boundary
|
|
305
|
+
const prevBoundary = parseDateWithHour(log.date, config.dayBoundaryHour, config.timezone);
|
|
306
|
+
if (newStart < prevBoundary) {
|
|
307
|
+
throw new Error(`Cannot start before previous day boundary (${String(config.dayBoundaryHour).padStart(2, '0')}:00)`);
|
|
308
|
+
}
|
|
309
|
+
log.manualStart = isoTimestamp;
|
|
310
|
+
}
|
|
311
|
+
/** Parse a date string + hour into a timestamp in the given timezone */
|
|
312
|
+
function parseDateWithHour(date, hour, timezone) {
|
|
313
|
+
// Build a date at noon UTC, then adjust by finding the offset
|
|
314
|
+
const [year, month, day] = date.split('-').map(Number);
|
|
315
|
+
// Try the target hour in UTC first, then adjust for timezone
|
|
316
|
+
const guess = new Date(Date.UTC(year, month - 1, day, hour, 0, 0));
|
|
317
|
+
// Get the actual hour in the target timezone for this guess
|
|
318
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
319
|
+
timeZone: timezone,
|
|
320
|
+
hour: 'numeric',
|
|
321
|
+
hour12: false,
|
|
322
|
+
year: 'numeric',
|
|
323
|
+
month: '2-digit',
|
|
324
|
+
day: '2-digit',
|
|
325
|
+
}).formatToParts(guess);
|
|
326
|
+
const hourPart = parts.find(p => p.type === 'hour');
|
|
327
|
+
if (!hourPart)
|
|
328
|
+
throw new Error(`Failed to parse hour in timezone ${timezone}`);
|
|
329
|
+
const actualHour = parseInt(hourPart.value);
|
|
330
|
+
const h = actualHour === 24 ? 0 : actualHour;
|
|
331
|
+
// Offset correction
|
|
332
|
+
const diff = hour - h;
|
|
333
|
+
return guess.getTime() + diff * 3_600_000;
|
|
334
|
+
}
|
|
335
|
+
// ─── Signals ────────────────────────────────────────────────────────────
|
|
336
|
+
/** Add signal with deduplication for diff_dynamics (same repo, accumulate deltas) */
|
|
337
|
+
export function addSignal(log, signal, deduplicationSeconds) {
|
|
338
|
+
if (signal.type === SignalType.DiffDynamics && log.signals.length > 0) {
|
|
339
|
+
// Search backward for last diff_dynamics from the same repo
|
|
340
|
+
for (let i = log.signals.length - 1; i >= 0; i--) {
|
|
341
|
+
const prev = log.signals[i];
|
|
342
|
+
if (prev.type !== SignalType.DiffDynamics)
|
|
343
|
+
continue;
|
|
344
|
+
if (prev.repo !== signal.repo)
|
|
345
|
+
continue;
|
|
346
|
+
// Found same-repo signal — check dedup window
|
|
347
|
+
if (signal.ts - prev.ts < deduplicationSeconds * 1000) {
|
|
348
|
+
// Accumulate deltas and update timestamp
|
|
349
|
+
log.signals[i] = {
|
|
350
|
+
ts: signal.ts,
|
|
351
|
+
type: SignalType.DiffDynamics,
|
|
352
|
+
repo: signal.repo,
|
|
353
|
+
delta: {
|
|
354
|
+
added: prev.delta.added + signal.delta.added,
|
|
355
|
+
removed: prev.delta.removed + signal.delta.removed,
|
|
356
|
+
untracked: (prev.delta.untracked ?? 0) + (signal.delta.untracked ?? 0),
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
break; // outside window — append new
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
log.signals.push(signal);
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=daily-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daily-log.js","sourceRoot":"","sources":["../../src/core/daily-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ClosedBy } from './types.js';
|
|
2
|
+
import type { AppConfig, DailyLog, Session, PollResult, TickInput, EvaluatorResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages session lifecycle within a DailyLog.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Opens/closes/updates sessions based on PollResult from GitTracker
|
|
8
|
+
* - Handles task switches (close old → open new)
|
|
9
|
+
* - Handles day boundary (close all → start fresh)
|
|
10
|
+
* - Logs signals (diff_dynamics, commit, checkout)
|
|
11
|
+
* - Credits evidence counters to sessions
|
|
12
|
+
* - Supports crash recovery (resume open sessions from disk)
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const tracker = new SessionTracker(config);
|
|
16
|
+
* // or with crash recovery:
|
|
17
|
+
* const tracker = new SessionTracker(config, existingLog);
|
|
18
|
+
*
|
|
19
|
+
* // each poll tick:
|
|
20
|
+
* for (const result of pollResults) {
|
|
21
|
+
* tracker.processPollResult(result);
|
|
22
|
+
* }
|
|
23
|
+
* tracker.flush();
|
|
24
|
+
*/
|
|
25
|
+
export declare class SessionTracker {
|
|
26
|
+
private dailyLog;
|
|
27
|
+
private readonly config;
|
|
28
|
+
private readonly autoPauseDisabledSessions;
|
|
29
|
+
private lastEvaluatorResult;
|
|
30
|
+
onSessionClosed: ((sessionId: string) => void) | null;
|
|
31
|
+
constructor(config: AppConfig, initialLog?: DailyLog);
|
|
32
|
+
getDailyLog(): DailyLog;
|
|
33
|
+
/**
|
|
34
|
+
* Process one poll tick for a single repo.
|
|
35
|
+
*
|
|
36
|
+
* Flow:
|
|
37
|
+
* 1. Credit reflog evidence to current open session (before any close)
|
|
38
|
+
* 2. Log signals (dynamics, commits, checkouts)
|
|
39
|
+
* 3. Handle session lifecycle (close/open/switch)
|
|
40
|
+
* 4. Update session tick (lastSeenAt, evidence, promote PENDING→ACTIVE)
|
|
41
|
+
*/
|
|
42
|
+
processPollResult(result: PollResult): void;
|
|
43
|
+
/**
|
|
44
|
+
* Close orphaned sessions from a previous daemon crash.
|
|
45
|
+
* Preserves saved lastSeenAt (last known poll time, at most ~30s before crash).
|
|
46
|
+
*/
|
|
47
|
+
closeCrashedSessions(): number;
|
|
48
|
+
/** Close all open sessions with given reason */
|
|
49
|
+
closeAllSessions(reason: ClosedBy): void;
|
|
50
|
+
/**
|
|
51
|
+
* Handle day boundary: close all sessions, return completed log, start fresh.
|
|
52
|
+
* Caller should flush the returned log to disk.
|
|
53
|
+
*/
|
|
54
|
+
handleDayBoundary(): DailyLog;
|
|
55
|
+
/** Mark manual start of workday */
|
|
56
|
+
setManualStart(timestamp: string): void;
|
|
57
|
+
/** Set dayStartedAt (called by daemon on startup) */
|
|
58
|
+
setDayStartedAt(timestamp: string): void;
|
|
59
|
+
/** Check if budget is exhausted */
|
|
60
|
+
isBudgetExhausted(): boolean;
|
|
61
|
+
/** Close all open sessions with BudgetExhausted */
|
|
62
|
+
closeBudgetExhausted(): void;
|
|
63
|
+
/** Add manual time adjustment to a session */
|
|
64
|
+
addAdjustment(target: string, minutes: number, reason: string): {
|
|
65
|
+
ok: boolean;
|
|
66
|
+
error?: string;
|
|
67
|
+
sessionId?: string;
|
|
68
|
+
};
|
|
69
|
+
/** Set manual day start */
|
|
70
|
+
setManualDayStart(isoTimestamp: string): {
|
|
71
|
+
ok: boolean;
|
|
72
|
+
error?: string;
|
|
73
|
+
};
|
|
74
|
+
/** Get remaining budget in ms */
|
|
75
|
+
getRemainingBudgetMs(): number;
|
|
76
|
+
/** Get manual minutes for a session */
|
|
77
|
+
getManualMinutes(session: Session): number;
|
|
78
|
+
/** Write current daily log to disk (atomic) */
|
|
79
|
+
flush(): void;
|
|
80
|
+
/** Get summary of open sessions (for status display) */
|
|
81
|
+
getOpenSessions(): readonly Session[];
|
|
82
|
+
/** Build TickInput[] for all open sessions (except manually paused) */
|
|
83
|
+
buildTickInputs(pollResults: readonly PollResult[]): readonly TickInput[];
|
|
84
|
+
/** Apply evaluator results: auto-pause, auto-resume, Pending→Active promotion */
|
|
85
|
+
applyEvaluatorResult(result: EvaluatorResult): void;
|
|
86
|
+
getLastEvaluatorResult(): EvaluatorResult | null;
|
|
87
|
+
/** Toggle autopause for a specific repo or all repos. Returns affected repo names. */
|
|
88
|
+
setAutoPauseDisabled(disabled: boolean, repoName?: string): string[];
|
|
89
|
+
isAutoPauseDisabled(sessionId: string): boolean;
|
|
90
|
+
/** Pause all open sessions */
|
|
91
|
+
pauseAllSessions(): void;
|
|
92
|
+
/** Pause a specific repo's open session. Returns true if a session was paused. */
|
|
93
|
+
pauseRepoSession(repoName: string): boolean;
|
|
94
|
+
/** Resume all paused sessions */
|
|
95
|
+
resumeAllSessions(): void;
|
|
96
|
+
/** Check if a session is currently paused */
|
|
97
|
+
hasOpenPause(session: Session): boolean;
|
|
98
|
+
private findOpenSession;
|
|
99
|
+
private openSession;
|
|
100
|
+
private closeSession;
|
|
101
|
+
private closeOpenPause;
|
|
102
|
+
private getOpenPauseSource;
|
|
103
|
+
/** Apply auto-pause if not already paused with the same source */
|
|
104
|
+
private applyAutoPause;
|
|
105
|
+
/** Close auto-pause (IdleTimeout or Superseded) if present */
|
|
106
|
+
private closeAutoPause;
|
|
107
|
+
private updateSessionTick;
|
|
108
|
+
private creditReflogEvidence;
|
|
109
|
+
private logSignals;
|
|
110
|
+
}
|