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,94 @@
|
|
|
1
|
+
import { MIN_TIMEOUT_MINUTES, MAX_TIMEOUT_MINUTES, EMA_WINDOW_MINUTES, ACTIVITY_RATIO, MAGNITUDE_SCALE, MAGNITUDE_BONUS_MAX, COMMIT_BONUS_SECONDS, BASE_DECAY, } from './constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pure computational class — no I/O.
|
|
4
|
+
* Maintains per-session activity score and EMA, determines cross-repo leadership.
|
|
5
|
+
*/
|
|
6
|
+
export class ActivityEvaluator {
|
|
7
|
+
minTicks;
|
|
8
|
+
maxTicks;
|
|
9
|
+
emaAlpha;
|
|
10
|
+
commitBonus;
|
|
11
|
+
state = new Map();
|
|
12
|
+
constructor(diffPollSeconds) {
|
|
13
|
+
this.minTicks = MIN_TIMEOUT_MINUTES * 60 / diffPollSeconds;
|
|
14
|
+
this.maxTicks = MAX_TIMEOUT_MINUTES * 60 / diffPollSeconds;
|
|
15
|
+
const emaWindowTicks = EMA_WINDOW_MINUTES * 60 / diffPollSeconds;
|
|
16
|
+
this.emaAlpha = 1 / emaWindowTicks;
|
|
17
|
+
this.commitBonus = COMMIT_BONUS_SECONDS / diffPollSeconds;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Process one tick for all sessions. Returns scores and leader.
|
|
21
|
+
* Manually paused sessions must NOT be included in ticks (caller responsibility).
|
|
22
|
+
*
|
|
23
|
+
* Scoring algorithm:
|
|
24
|
+
* 1. EMA — binary moving average of activity (1 if dynamics/commit, 0 otherwise)
|
|
25
|
+
* 2. dynamicMaxScore — adaptive ceiling [minTicks..maxTicks], shrinks when EMA high
|
|
26
|
+
* 3. Dynamics contribution — ACTIVITY_RATIO * dynamicMaxScore * magnitude bonus
|
|
27
|
+
* 4. Magnitude bonus — log2(1 + deltaMagnitude) / MAGNITUDE_SCALE, capped at MAGNITUDE_BONUS_MAX
|
|
28
|
+
* 5. Commit bonus — instant COMMIT_BONUS_SECONDS / diffPollSeconds ticks
|
|
29
|
+
* 6. Cap score at dynamicMaxScore, then decay by BASE_DECAY per tick
|
|
30
|
+
* 7. score == 0 → idle timeout → eligible for auto-pause
|
|
31
|
+
* Leader = highest normalizedScore (score/maxScore) among sessions with score > 0
|
|
32
|
+
*/
|
|
33
|
+
processAllTicks(ticks) {
|
|
34
|
+
const scores = new Map();
|
|
35
|
+
// Update state for each session
|
|
36
|
+
for (const tick of ticks) {
|
|
37
|
+
const st = this.getOrCreateState(tick.sessionId);
|
|
38
|
+
const hasActivity = tick.signals.hasDynamics || tick.signals.hasCommit;
|
|
39
|
+
// 1. EMA update (binary input)
|
|
40
|
+
st.ema = this.emaAlpha * (hasActivity ? 1 : 0) + (1 - this.emaAlpha) * st.ema;
|
|
41
|
+
// 2. Adaptive max score
|
|
42
|
+
const dynamicMaxScore = this.maxTicks - (this.maxTicks - this.minTicks) * Math.min(1, st.ema);
|
|
43
|
+
// 3. Activity points from dynamics
|
|
44
|
+
if (tick.signals.hasDynamics) {
|
|
45
|
+
const magnitudeBonus = 1 + Math.min(1, Math.log2(1 + tick.signals.deltaMagnitude) / MAGNITUDE_SCALE) * MAGNITUDE_BONUS_MAX;
|
|
46
|
+
st.score += dynamicMaxScore * ACTIVITY_RATIO * magnitudeBonus;
|
|
47
|
+
}
|
|
48
|
+
// 4. Commit bonus
|
|
49
|
+
if (tick.signals.hasCommit) {
|
|
50
|
+
st.score += this.commitBonus;
|
|
51
|
+
}
|
|
52
|
+
// 5. Cap at adaptive ceiling
|
|
53
|
+
st.score = Math.min(st.score, dynamicMaxScore);
|
|
54
|
+
// 6. Decay
|
|
55
|
+
st.score = Math.max(0, st.score - BASE_DECAY);
|
|
56
|
+
// Build result for this session
|
|
57
|
+
const normalizedScore = dynamicMaxScore > 0 ? st.score / dynamicMaxScore : 0;
|
|
58
|
+
scores.set(tick.sessionId, {
|
|
59
|
+
score: st.score,
|
|
60
|
+
maxScore: dynamicMaxScore,
|
|
61
|
+
normalizedScore,
|
|
62
|
+
ema: st.ema,
|
|
63
|
+
isIdleTimeout: st.score === 0,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Determine leader: highest normalizedScore with score > 0
|
|
67
|
+
let leaderId = null;
|
|
68
|
+
let bestNorm = 0;
|
|
69
|
+
for (const [sessionId, sessionScore] of scores) {
|
|
70
|
+
if (sessionScore.score > 0 && sessionScore.normalizedScore > bestNorm) {
|
|
71
|
+
bestNorm = sessionScore.normalizedScore;
|
|
72
|
+
leaderId = sessionId;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { scores, leaderId };
|
|
76
|
+
}
|
|
77
|
+
/** Remove session state on close */
|
|
78
|
+
removeSession(sessionId) {
|
|
79
|
+
this.state.delete(sessionId);
|
|
80
|
+
}
|
|
81
|
+
/** Clear all state (day boundary, daemon stop) */
|
|
82
|
+
clear() {
|
|
83
|
+
this.state.clear();
|
|
84
|
+
}
|
|
85
|
+
getOrCreateState(sessionId) {
|
|
86
|
+
let st = this.state.get(sessionId);
|
|
87
|
+
if (!st) {
|
|
88
|
+
st = { score: 0, ema: 0 };
|
|
89
|
+
this.state.set(sessionId, st);
|
|
90
|
+
}
|
|
91
|
+
return st;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=activity-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-evaluator.js","sourceRoot":"","sources":["../../src/core/activity-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AppConfig, Secrets } from './types.js';
|
|
2
|
+
export declare function loadConfig(): AppConfig;
|
|
3
|
+
export declare function loadSecrets(): Secrets;
|
|
4
|
+
export declare function getWorkdayHome(): string;
|
|
5
|
+
export declare function getPackageRoot(): string;
|
|
6
|
+
export declare function getDataDir(): string;
|
|
7
|
+
/** Format timestamp as "YYYY-MM-DD" in specified IANA timezone */
|
|
8
|
+
export declare function formatDate(timestamp: number, timezone: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Compute working date in the configured timezone.
|
|
11
|
+
* If current hour < dayBoundaryHour, attribute activity to previous calendar day.
|
|
12
|
+
* dayBoundaryHour is 24h format (e.g. 4 = 04:00 AM).
|
|
13
|
+
*/
|
|
14
|
+
export declare function computeWorkingDate(timestamp: number, dayBoundaryHour: number, timezone: string): string;
|
|
15
|
+
/** Build ISO timestamp from date + hour:minute in timezone */
|
|
16
|
+
export declare function buildTimestamp(date: string, hour: number, minute: number, timezone: string): string;
|
|
17
|
+
/** Extract task key from branch name. Returns null for generic/foreign branches. */
|
|
18
|
+
export declare function extractTask(branch: string, taskPattern: string, developer: string, genericBranches: readonly string[]): string | null;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { CONFIG_FILE_NAME, SECRETS_FILE_NAME, DATA_DIR_NAME, DEFAULT_API_PORT } from './constants.js';
|
|
6
|
+
/** Find the directory containing this package's package.json */
|
|
7
|
+
function findPackageRoot() {
|
|
8
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
while (dir !== dirname(dir)) {
|
|
10
|
+
if (existsSync(join(dir, 'package.json')))
|
|
11
|
+
return dir;
|
|
12
|
+
dir = dirname(dir);
|
|
13
|
+
}
|
|
14
|
+
throw new Error('Could not find package root (no package.json found)');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve workday home directory (where config, secrets, data live).
|
|
18
|
+
* 1. WORKDAY_HOME env — explicit override
|
|
19
|
+
* 2. Local mode — config.json next to package.json (dev / local install)
|
|
20
|
+
* 3. ~/.workday/ — global npm install
|
|
21
|
+
*/
|
|
22
|
+
function resolveWorkdayHome() {
|
|
23
|
+
if (process.env.WORKDAY_HOME)
|
|
24
|
+
return process.env.WORKDAY_HOME;
|
|
25
|
+
const pkgRoot = findPackageRoot();
|
|
26
|
+
if (existsSync(join(pkgRoot, CONFIG_FILE_NAME)))
|
|
27
|
+
return pkgRoot;
|
|
28
|
+
return join(homedir(), '.workday');
|
|
29
|
+
}
|
|
30
|
+
const PACKAGE_ROOT = findPackageRoot();
|
|
31
|
+
const WORKDAY_HOME = resolveWorkdayHome();
|
|
32
|
+
function readJson(filePath) {
|
|
33
|
+
if (!existsSync(filePath)) {
|
|
34
|
+
throw new Error(`File not found: ${filePath}`);
|
|
35
|
+
}
|
|
36
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
37
|
+
return JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
function isValidTimezone(tz) {
|
|
40
|
+
try {
|
|
41
|
+
Intl.DateTimeFormat(undefined, { timeZone: tz });
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function validateConfig(config) {
|
|
49
|
+
if (!config.repos || config.repos.length === 0) {
|
|
50
|
+
throw new Error('config.json: repos must be a non-empty array');
|
|
51
|
+
}
|
|
52
|
+
for (const repo of config.repos) {
|
|
53
|
+
if (!existsSync(repo)) {
|
|
54
|
+
console.warn(`WARNING: repo path not found: ${repo}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!config.taskPattern) {
|
|
58
|
+
throw new Error('config.json: taskPattern is required');
|
|
59
|
+
}
|
|
60
|
+
if (!Number.isInteger(config.dayBoundaryHour) || config.dayBoundaryHour < 0 || config.dayBoundaryHour > 23) {
|
|
61
|
+
throw new Error('config.json: dayBoundaryHour must be an integer 0-23');
|
|
62
|
+
}
|
|
63
|
+
if (!isValidTimezone(config.timezone)) {
|
|
64
|
+
throw new Error(`config.json: invalid timezone "${config.timezone}"`);
|
|
65
|
+
}
|
|
66
|
+
if (!config.session?.diffPollSeconds || config.session.diffPollSeconds < 5) {
|
|
67
|
+
throw new Error('config.json: session.diffPollSeconds must be >= 5');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function validateSecrets(secrets) {
|
|
71
|
+
if (!secrets.Developer) {
|
|
72
|
+
throw new Error('secrets.json: Developer is required');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function loadConfig() {
|
|
76
|
+
const configPath = join(WORKDAY_HOME, CONFIG_FILE_NAME);
|
|
77
|
+
const raw = readJson(configPath);
|
|
78
|
+
const systemTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
79
|
+
const config = {
|
|
80
|
+
...raw,
|
|
81
|
+
apiPort: raw.apiPort ?? DEFAULT_API_PORT,
|
|
82
|
+
timezone: raw.timezone ?? systemTimezone,
|
|
83
|
+
};
|
|
84
|
+
validateConfig(config);
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
export function loadSecrets() {
|
|
88
|
+
const secretsPath = join(WORKDAY_HOME, SECRETS_FILE_NAME);
|
|
89
|
+
const secrets = readJson(secretsPath);
|
|
90
|
+
validateSecrets(secrets);
|
|
91
|
+
return secrets;
|
|
92
|
+
}
|
|
93
|
+
export function getWorkdayHome() {
|
|
94
|
+
return WORKDAY_HOME;
|
|
95
|
+
}
|
|
96
|
+
export function getPackageRoot() {
|
|
97
|
+
return PACKAGE_ROOT;
|
|
98
|
+
}
|
|
99
|
+
export function getDataDir() {
|
|
100
|
+
return join(WORKDAY_HOME, DATA_DIR_NAME);
|
|
101
|
+
}
|
|
102
|
+
/** Get hour (0-23) in specified IANA timezone */
|
|
103
|
+
function getHourInTimezone(timestamp, timezone) {
|
|
104
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
105
|
+
timeZone: timezone,
|
|
106
|
+
hour: 'numeric',
|
|
107
|
+
hour12: false,
|
|
108
|
+
}).formatToParts(new Date(timestamp));
|
|
109
|
+
const hourPart = parts.find(p => p.type === 'hour');
|
|
110
|
+
if (!hourPart)
|
|
111
|
+
throw new Error(`Failed to parse hour in timezone ${timezone}`);
|
|
112
|
+
const hour = parseInt(hourPart.value);
|
|
113
|
+
// Some ICU implementations return hour=24 for midnight; normalize to 0
|
|
114
|
+
return hour === 24 ? 0 : hour;
|
|
115
|
+
}
|
|
116
|
+
/** Format timestamp as "YYYY-MM-DD" in specified IANA timezone */
|
|
117
|
+
export function formatDate(timestamp, timezone) {
|
|
118
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
119
|
+
timeZone: timezone,
|
|
120
|
+
year: 'numeric',
|
|
121
|
+
month: '2-digit',
|
|
122
|
+
day: '2-digit',
|
|
123
|
+
}).formatToParts(new Date(timestamp));
|
|
124
|
+
const yearPart = parts.find(p => p.type === 'year');
|
|
125
|
+
const monthPart = parts.find(p => p.type === 'month');
|
|
126
|
+
const dayPart = parts.find(p => p.type === 'day');
|
|
127
|
+
if (!yearPart || !monthPart || !dayPart)
|
|
128
|
+
throw new Error(`Failed to parse date in timezone ${timezone}`);
|
|
129
|
+
return `${yearPart.value}-${monthPart.value}-${dayPart.value}`;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Compute working date in the configured timezone.
|
|
133
|
+
* If current hour < dayBoundaryHour, attribute activity to previous calendar day.
|
|
134
|
+
* dayBoundaryHour is 24h format (e.g. 4 = 04:00 AM).
|
|
135
|
+
*/
|
|
136
|
+
export function computeWorkingDate(timestamp, dayBoundaryHour, timezone) {
|
|
137
|
+
const hour = getHourInTimezone(timestamp, timezone);
|
|
138
|
+
if (hour < dayBoundaryHour) {
|
|
139
|
+
return formatDate(timestamp - 86_400_000, timezone);
|
|
140
|
+
}
|
|
141
|
+
return formatDate(timestamp, timezone);
|
|
142
|
+
}
|
|
143
|
+
/** Build ISO timestamp from date + hour:minute in timezone */
|
|
144
|
+
export function buildTimestamp(date, hour, minute, timezone) {
|
|
145
|
+
const [year, month, day] = date.split('-').map(Number);
|
|
146
|
+
const guess = new Date(Date.UTC(year, month - 1, day, hour, minute, 0));
|
|
147
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
148
|
+
timeZone: timezone,
|
|
149
|
+
hour: 'numeric',
|
|
150
|
+
hour12: false,
|
|
151
|
+
minute: 'numeric',
|
|
152
|
+
}).formatToParts(guess);
|
|
153
|
+
const hourPart = parts.find(p => p.type === 'hour');
|
|
154
|
+
const minutePart = parts.find(p => p.type === 'minute');
|
|
155
|
+
if (!hourPart || !minutePart)
|
|
156
|
+
throw new Error(`Failed to parse time in timezone ${timezone}`);
|
|
157
|
+
const actualHour = parseInt(hourPart.value);
|
|
158
|
+
const actualMinute = parseInt(minutePart.value);
|
|
159
|
+
const h = actualHour === 24 ? 0 : actualHour;
|
|
160
|
+
const diffMs = ((hour - h) * 60 + (minute - actualMinute)) * 60_000;
|
|
161
|
+
return new Date(guess.getTime() + diffMs).toISOString();
|
|
162
|
+
}
|
|
163
|
+
/** Extract task key from branch name. Returns null for generic/foreign branches. */
|
|
164
|
+
export function extractTask(branch, taskPattern, developer, genericBranches) {
|
|
165
|
+
if (/^[0-9a-f]{7,40}$/.test(branch))
|
|
166
|
+
return null;
|
|
167
|
+
if (genericBranches.includes(branch))
|
|
168
|
+
return null;
|
|
169
|
+
if (!branch.includes(developer))
|
|
170
|
+
return null;
|
|
171
|
+
const match = branch.match(new RegExp(taskPattern));
|
|
172
|
+
return match ? match[0] : null;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const CONFIG_FILE_NAME = "config.json";
|
|
2
|
+
export declare const SECRETS_FILE_NAME = "secrets.json";
|
|
3
|
+
export declare const PID_FILE_NAME = "workday.pid";
|
|
4
|
+
export declare const DATA_DIR_NAME = "data";
|
|
5
|
+
export declare const TMP_EXTENSION = ".tmp";
|
|
6
|
+
export declare const BACKUP_EXTENSION = ".bak";
|
|
7
|
+
export declare const DAEMON_SCRIPT_TS = "daemon.ts";
|
|
8
|
+
export declare const DAEMON_SCRIPT_JS = "daemon.js";
|
|
9
|
+
export declare const DEFAULT_API_PORT = 9213;
|
|
10
|
+
export declare const GIT_BATCH_SEPARATOR = "---WORKDAY-SEP---";
|
|
11
|
+
export declare const GIT_MAX_BUFFER_BYTES: number;
|
|
12
|
+
export declare const MAX_ADJUSTMENT_MINUTES = 480;
|
|
13
|
+
export declare const ISSUE_CACHE_FILE = "issue-cache.json";
|
|
14
|
+
export declare const PUSH_LOG_FILE = "push-log.json";
|
|
15
|
+
export declare const TEMPO_REPORT_DIR = "tempo";
|
|
16
|
+
export declare const TEMPO_BASE_URL = "https://api.tempo.io";
|
|
17
|
+
export declare const TEMPO_RATE_LIMIT_MS = 210;
|
|
18
|
+
export declare const TEMPO_TOLERANCE_SECONDS = 60;
|
|
19
|
+
export declare const CRASH_RECOVERY_LOOKBACK_DAYS = 7;
|
|
20
|
+
export declare const MAX_BODY_BYTES = 4096;
|
|
21
|
+
export declare const DAEMON_START_MAX_ATTEMPTS = 25;
|
|
22
|
+
export declare const DAEMON_START_POLL_MS = 200;
|
|
23
|
+
export declare const MS_PER_MINUTE = 60000;
|
|
24
|
+
/** Min inactivity timeout (when developer is frequently active) */
|
|
25
|
+
export declare const MIN_TIMEOUT_MINUTES = 15;
|
|
26
|
+
/** Max inactivity timeout (when developer is rarely active) */
|
|
27
|
+
export declare const MAX_TIMEOUT_MINUTES = 45;
|
|
28
|
+
/** Smoothing window for activity frequency */
|
|
29
|
+
export declare const EMA_WINDOW_MINUTES = 10;
|
|
30
|
+
/** Fraction of max score awarded per active tick */
|
|
31
|
+
export declare const ACTIVITY_RATIO = 0.5;
|
|
32
|
+
/** log2 divisor; ~128 changed lines = full bonus */
|
|
33
|
+
export declare const MAGNITUDE_SCALE = 7;
|
|
34
|
+
/** Max extra multiplier on dynamics contribution */
|
|
35
|
+
export declare const MAGNITUDE_BONUS_MAX = 0.5;
|
|
36
|
+
/** "Free" score on commit (in seconds, converted to ticks) */
|
|
37
|
+
export declare const COMMIT_BONUS_SECONDS = 150;
|
|
38
|
+
/** Constant per-tick score drain */
|
|
39
|
+
export declare const BASE_DECAY = 1;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ─── File names ─────────────────────────────────────────────────────────
|
|
2
|
+
export const CONFIG_FILE_NAME = 'config.json';
|
|
3
|
+
export const SECRETS_FILE_NAME = 'secrets.json';
|
|
4
|
+
export const PID_FILE_NAME = 'workday.pid';
|
|
5
|
+
export const DATA_DIR_NAME = 'data';
|
|
6
|
+
export const TMP_EXTENSION = '.tmp';
|
|
7
|
+
export const BACKUP_EXTENSION = '.bak';
|
|
8
|
+
// ─── Daemon script resolution ───────────────────────────────────────────
|
|
9
|
+
export const DAEMON_SCRIPT_TS = 'daemon.ts';
|
|
10
|
+
export const DAEMON_SCRIPT_JS = 'daemon.js';
|
|
11
|
+
// ─── HTTP API ──────────────────────────────────────────────────────────
|
|
12
|
+
export const DEFAULT_API_PORT = 9213;
|
|
13
|
+
// ─── Git internals ──────────────────────────────────────────────────────
|
|
14
|
+
export const GIT_BATCH_SEPARATOR = '---WORKDAY-SEP---';
|
|
15
|
+
export const GIT_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
16
|
+
// ─── Budget / Manual adjustment ─────────────────────────────────────────
|
|
17
|
+
export const MAX_ADJUSTMENT_MINUTES = 480;
|
|
18
|
+
// ─── Push / Tempo ───────────────────────────────────────────────────────
|
|
19
|
+
export const ISSUE_CACHE_FILE = 'issue-cache.json';
|
|
20
|
+
export const PUSH_LOG_FILE = 'push-log.json';
|
|
21
|
+
export const TEMPO_REPORT_DIR = 'tempo';
|
|
22
|
+
export const TEMPO_BASE_URL = 'https://api.tempo.io';
|
|
23
|
+
export const TEMPO_RATE_LIMIT_MS = 210;
|
|
24
|
+
export const TEMPO_TOLERANCE_SECONDS = 60;
|
|
25
|
+
// ─── Daemon crash recovery ──────────────────────────────────────────────
|
|
26
|
+
export const CRASH_RECOVERY_LOOKBACK_DAYS = 7;
|
|
27
|
+
// ─── HTTP body size limit ───────────────────────────────────────────────
|
|
28
|
+
export const MAX_BODY_BYTES = 4096;
|
|
29
|
+
// ─── CLI daemon startup polling ─────────────────────────────────────────
|
|
30
|
+
export const DAEMON_START_MAX_ATTEMPTS = 25;
|
|
31
|
+
export const DAEMON_START_POLL_MS = 200;
|
|
32
|
+
// ─── Time conversions ──────────────────────────────────────────────────
|
|
33
|
+
export const MS_PER_MINUTE = 60_000;
|
|
34
|
+
// ─── Activity Evaluator algorithm constants ─────────────────────────────
|
|
35
|
+
/** Min inactivity timeout (when developer is frequently active) */
|
|
36
|
+
export const MIN_TIMEOUT_MINUTES = 15;
|
|
37
|
+
/** Max inactivity timeout (when developer is rarely active) */
|
|
38
|
+
export const MAX_TIMEOUT_MINUTES = 45;
|
|
39
|
+
/** Smoothing window for activity frequency */
|
|
40
|
+
export const EMA_WINDOW_MINUTES = 10;
|
|
41
|
+
/** Fraction of max score awarded per active tick */
|
|
42
|
+
export const ACTIVITY_RATIO = 0.5;
|
|
43
|
+
/** log2 divisor; ~128 changed lines = full bonus */
|
|
44
|
+
export const MAGNITUDE_SCALE = 7;
|
|
45
|
+
/** Max extra multiplier on dynamics contribution */
|
|
46
|
+
export const MAGNITUDE_BONUS_MAX = 0.5;
|
|
47
|
+
/** "Free" score on commit (in seconds, converted to ticks) */
|
|
48
|
+
export const COMMIT_BONUS_SECONDS = 150;
|
|
49
|
+
/** Constant per-tick score drain */
|
|
50
|
+
export const BASE_DECAY = 1;
|
|
51
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/core/constants.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAChD,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;AAC3C,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AACpC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AACpC,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE5C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AACvD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAC7C,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,sBAAsB,CAAC;AACrD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAE9C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAC5C,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAEpC,2EAA2E;AAC3E,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAClC,oDAAoD;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AACjC,oDAAoD;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AACvC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACxC,oCAAoC;AACpC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type DailyLog, type Session, type Signal, type Evidence, type AppConfig, type Pause } from './types.js';
|
|
2
|
+
/** Generate short unique session id */
|
|
3
|
+
export declare function generateSessionId(): string;
|
|
4
|
+
/** Get data file path for a given date: data/YYYY-MM/MM-DD.json */
|
|
5
|
+
export declare function getDailyLogPath(date: string): string;
|
|
6
|
+
/** Create empty daily log for a given date */
|
|
7
|
+
export declare function createEmptyLog(date: string, config: AppConfig): DailyLog;
|
|
8
|
+
/** Create empty evidence object */
|
|
9
|
+
export declare function createEmptyEvidence(): Evidence;
|
|
10
|
+
/** Read daily log from disk. Falls back to .bak if main file is corrupted. */
|
|
11
|
+
export declare function readDailyLog(date: string): DailyLog | null;
|
|
12
|
+
/** Write daily log to disk using atomic write pattern with backup */
|
|
13
|
+
export declare function writeDailyLog(log: DailyLog): void;
|
|
14
|
+
/** Get or create today's daily log */
|
|
15
|
+
export declare function getOrCreateTodayLog(config: AppConfig): DailyLog;
|
|
16
|
+
/** Find session by id */
|
|
17
|
+
export declare function findSession(log: DailyLog, sessionId: string): Session | undefined;
|
|
18
|
+
/** Find open (unclosed) pause in a session */
|
|
19
|
+
export declare function getOpenPause(session: Session): Pause | null;
|
|
20
|
+
/** Total pause duration for a session in milliseconds */
|
|
21
|
+
export declare function computeTotalPauseDuration(session: Session): number;
|
|
22
|
+
/** Effective working duration: (end - activatedAt) - pauses, in milliseconds. Returns 0 for PENDING sessions. */
|
|
23
|
+
export declare function computeEffectiveDuration(session: Session): number;
|
|
24
|
+
/**
|
|
25
|
+
* Compute actual work/downtime using interval merge across all sessions.
|
|
26
|
+
* Downtime = periods when NO session was actively working (all paused or no sessions).
|
|
27
|
+
*/
|
|
28
|
+
export declare function computeDaySummary(sessions: readonly Session[]): {
|
|
29
|
+
readonly workMs: number;
|
|
30
|
+
readonly downtimeMs: number;
|
|
31
|
+
readonly spanMs: number;
|
|
32
|
+
};
|
|
33
|
+
/** Resolve session by 1-based index or hex id */
|
|
34
|
+
export declare function resolveSessionTarget(log: DailyLog, target: string): Session | null;
|
|
35
|
+
/** Sum of manual adjustment minutes for a session */
|
|
36
|
+
export declare function computeManualMinutes(session: Session): number;
|
|
37
|
+
/** Effective duration including manual adjustments (ms) */
|
|
38
|
+
export declare function computeFullEffectiveDuration(session: Session): number;
|
|
39
|
+
/** Resolve dayStart timestamp using priority chain: manualStart → dayStartedAt → first session */
|
|
40
|
+
export declare function computeDayStart(log: DailyLog, config: AppConfig): number;
|
|
41
|
+
/** Compute day end timestamp (next day boundary) */
|
|
42
|
+
export declare function computeDayEnd(date: string, dayBoundaryHour: number, timezone: string): number;
|
|
43
|
+
/** Compute total budget in ms */
|
|
44
|
+
export declare function computeBudgetMs(log: DailyLog, config: AppConfig): number;
|
|
45
|
+
/** Sum of all sessions' full effective duration (ms) */
|
|
46
|
+
export declare function computeTotalClaimedMs(log: DailyLog): number;
|
|
47
|
+
/** Check if day budget is exhausted */
|
|
48
|
+
export declare function isBudgetExhausted(log: DailyLog, config: AppConfig): boolean;
|
|
49
|
+
/** Remaining budget in ms, clamped >= 0 */
|
|
50
|
+
export declare function getRemainingBudgetMs(log: DailyLog, config: AppConfig): number;
|
|
51
|
+
/** Add manual adjustment to a session. Throws on validation failure. */
|
|
52
|
+
export declare function addManualAdjustment(log: DailyLog, sessionId: string, minutes: number, reason: string, config: AppConfig): void;
|
|
53
|
+
/** Set manual day start. Can only shift earlier. */
|
|
54
|
+
export declare function setDayManualStart(log: DailyLog, isoTimestamp: string, config: AppConfig): void;
|
|
55
|
+
/** Add signal with deduplication for diff_dynamics (same repo, accumulate deltas) */
|
|
56
|
+
export declare function addSignal(log: DailyLog, signal: Signal, deduplicationSeconds: number): void;
|