heyiam 0.1.7 → 0.1.8
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/analyzer.d.ts +3 -3
- package/dist/archive.d.ts +14 -0
- package/dist/archive.js +125 -0
- package/dist/archive.js.map +1 -0
- package/dist/auth.d.ts +0 -6
- package/dist/auth.js +2 -4
- package/dist/auth.js.map +1 -1
- package/dist/autostart.d.ts +19 -0
- package/dist/autostart.js +103 -0
- package/dist/autostart.js.map +1 -0
- package/dist/bridge.d.ts +0 -2
- package/dist/bridge.js +33 -4
- package/dist/bridge.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/context-export.d.ts +22 -0
- package/dist/context-export.js +230 -0
- package/dist/context-export.js.map +1 -0
- package/dist/daemon-install.d.ts +23 -0
- package/dist/daemon-install.js +155 -0
- package/dist/daemon-install.js.map +1 -0
- package/dist/db.d.ts +118 -0
- package/dist/db.js +444 -0
- package/dist/db.js.map +1 -0
- package/dist/export.d.ts +30 -0
- package/dist/export.js +377 -0
- package/dist/export.js.map +1 -0
- package/dist/format-utils.d.ts +6 -0
- package/dist/format-utils.js +15 -0
- package/dist/format-utils.js.map +1 -0
- package/dist/index.js +474 -117
- package/dist/index.js.map +1 -1
- package/dist/llm/project-enhance.js +1 -1
- package/dist/parsers/claude.js +73 -0
- package/dist/parsers/claude.js.map +1 -1
- package/dist/parsers/codex.js +1 -1
- package/dist/parsers/codex.js.map +1 -1
- package/dist/parsers/cursor.d.ts +2 -0
- package/dist/parsers/cursor.js +14 -26
- package/dist/parsers/cursor.js.map +1 -1
- package/dist/parsers/gemini.d.ts +3 -2
- package/dist/parsers/gemini.js +198 -21
- package/dist/parsers/gemini.js.map +1 -1
- package/dist/parsers/index.d.ts +1 -1
- package/dist/parsers/index.js +23 -7
- package/dist/parsers/index.js.map +1 -1
- package/dist/parsers/types.d.ts +27 -1
- package/dist/render/build-render-data.d.ts +59 -0
- package/dist/render/build-render-data.js +101 -0
- package/dist/render/build-render-data.js.map +1 -0
- package/dist/render/components/PortfolioPage.d.ts +4 -0
- package/dist/render/components/PortfolioPage.js +16 -0
- package/dist/render/components/PortfolioPage.js.map +1 -0
- package/dist/render/components/ProjectPage.d.ts +4 -0
- package/dist/render/components/ProjectPage.js +101 -0
- package/dist/render/components/ProjectPage.js.map +1 -0
- package/dist/render/components/SessionPage.d.ts +4 -0
- package/dist/render/components/SessionPage.js +29 -0
- package/dist/render/components/SessionPage.js.map +1 -0
- package/dist/render/index.d.ts +37 -0
- package/dist/render/index.js +140 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/types.d.ts +121 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/routes/archive.d.ts +3 -0
- package/dist/routes/archive.js +56 -0
- package/dist/routes/archive.js.map +1 -0
- package/dist/routes/auth.d.ts +3 -0
- package/dist/routes/auth.js +116 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/context.d.ts +61 -0
- package/dist/routes/context.js +356 -0
- package/dist/routes/context.js.map +1 -0
- package/dist/routes/dashboard.d.ts +3 -0
- package/dist/routes/dashboard.js +103 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/enhance.d.ts +3 -0
- package/dist/routes/enhance.js +305 -0
- package/dist/routes/enhance.js.map +1 -0
- package/dist/routes/export.d.ts +3 -0
- package/dist/routes/export.js +145 -0
- package/dist/routes/export.js.map +1 -0
- package/dist/routes/index.d.ts +12 -0
- package/dist/routes/index.js +13 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/preview.d.ts +3 -0
- package/dist/routes/preview.js +191 -0
- package/dist/routes/preview.js.map +1 -0
- package/dist/routes/projects.d.ts +3 -0
- package/dist/routes/projects.js +356 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/publish.d.ts +3 -0
- package/dist/routes/publish.js +466 -0
- package/dist/routes/publish.js.map +1 -0
- package/dist/routes/search.d.ts +3 -0
- package/dist/routes/search.js +110 -0
- package/dist/routes/search.js.map +1 -0
- package/dist/routes/sessions.d.ts +3 -0
- package/dist/routes/sessions.js +103 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/settings.d.ts +3 -0
- package/dist/routes/settings.js +30 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/screenshot.d.ts +5 -2
- package/dist/screenshot.js +187 -13
- package/dist/screenshot.js.map +1 -1
- package/dist/search.d.ts +30 -0
- package/dist/search.js +153 -0
- package/dist/search.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +55 -1318
- package/dist/server.js.map +1 -1
- package/dist/settings.d.ts +23 -6
- package/dist/settings.js +36 -12
- package/dist/settings.js.map +1 -1
- package/dist/source-audit.d.ts +29 -0
- package/dist/source-audit.js +203 -0
- package/dist/source-audit.js.map +1 -0
- package/dist/sync.d.ts +74 -0
- package/dist/sync.js +358 -0
- package/dist/sync.js.map +1 -0
- package/dist/transcript.d.ts +68 -0
- package/dist/transcript.js +268 -0
- package/dist/transcript.js.map +1 -0
- package/package.json +5 -1
- package/app/dist/assets/html2canvas-Cwn_rrOw.js +0 -5
- package/app/dist/assets/index-CEQyTkgN.js +0 -14
- package/app/dist/assets/index-DLh5xRE8.css +0 -1
- package/app/dist/favicon.svg +0 -5
- package/app/dist/icons.svg +0 -24
- package/app/dist/index.html +0 -20
- package/dist/machine-key.d.ts +0 -10
- package/dist/machine-key.js +0 -51
- package/dist/machine-key.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render data types for static HTML generation.
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the data shapes that the CLI render functions
|
|
5
|
+
* accept. They are intentionally separate from the interactive UI types
|
|
6
|
+
* in `app/src/types.ts` — the render pipeline owns its own contract.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentSummary } from '../routes/context.js';
|
|
9
|
+
export interface UserInfo {
|
|
10
|
+
username: string;
|
|
11
|
+
accent: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PortfolioUser extends UserInfo {
|
|
14
|
+
displayName: string;
|
|
15
|
+
bio: string;
|
|
16
|
+
location: string;
|
|
17
|
+
status: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PortfolioProject {
|
|
20
|
+
slug: string;
|
|
21
|
+
title: string;
|
|
22
|
+
narrative: string;
|
|
23
|
+
totalSessions: number;
|
|
24
|
+
totalLoc: number;
|
|
25
|
+
totalDurationMinutes: number;
|
|
26
|
+
totalFilesChanged: number;
|
|
27
|
+
skills: string[];
|
|
28
|
+
publishedCount: number;
|
|
29
|
+
}
|
|
30
|
+
export interface PortfolioRenderData {
|
|
31
|
+
user: PortfolioUser;
|
|
32
|
+
projects: PortfolioProject[];
|
|
33
|
+
}
|
|
34
|
+
export interface ProjectTimeline {
|
|
35
|
+
period: string;
|
|
36
|
+
label: string;
|
|
37
|
+
sessions: Array<Record<string, unknown>>;
|
|
38
|
+
}
|
|
39
|
+
export interface ProjectDetail {
|
|
40
|
+
slug: string;
|
|
41
|
+
title: string;
|
|
42
|
+
narrative: string;
|
|
43
|
+
repoUrl?: string;
|
|
44
|
+
projectUrl?: string;
|
|
45
|
+
screenshotUrl?: string;
|
|
46
|
+
timeline: ProjectTimeline[];
|
|
47
|
+
skills: string[];
|
|
48
|
+
totalSessions: number;
|
|
49
|
+
totalLoc: number;
|
|
50
|
+
totalDurationMinutes: number;
|
|
51
|
+
totalAgentDurationMinutes?: number;
|
|
52
|
+
totalFilesChanged: number;
|
|
53
|
+
}
|
|
54
|
+
export interface SessionCard {
|
|
55
|
+
token: string;
|
|
56
|
+
slug: string;
|
|
57
|
+
title: string;
|
|
58
|
+
devTake: string;
|
|
59
|
+
durationMinutes: number;
|
|
60
|
+
turns: number;
|
|
61
|
+
locChanged: number;
|
|
62
|
+
filesChanged: number;
|
|
63
|
+
skills: string[];
|
|
64
|
+
recordedAt: string;
|
|
65
|
+
sourceTool: string;
|
|
66
|
+
agentSummary?: AgentSummary;
|
|
67
|
+
}
|
|
68
|
+
export interface ProjectRenderData {
|
|
69
|
+
user: UserInfo;
|
|
70
|
+
project: ProjectDetail;
|
|
71
|
+
/** Curated sessions for the card grid */
|
|
72
|
+
sessions: SessionCard[];
|
|
73
|
+
/** All sessions for work timeline and growth chart (falls back to sessions if not set) */
|
|
74
|
+
allSessions?: SessionCard[];
|
|
75
|
+
/** Base URL for session links. Defaults to /:username/:project */
|
|
76
|
+
sessionBaseUrl?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface Beat {
|
|
79
|
+
stepNumber: number;
|
|
80
|
+
title: string;
|
|
81
|
+
body: string;
|
|
82
|
+
}
|
|
83
|
+
export interface QaPair {
|
|
84
|
+
question: string;
|
|
85
|
+
answer: string;
|
|
86
|
+
}
|
|
87
|
+
export interface ToolBreakdownEntry {
|
|
88
|
+
tool: string;
|
|
89
|
+
count: number;
|
|
90
|
+
}
|
|
91
|
+
export interface FileEntry {
|
|
92
|
+
path: string;
|
|
93
|
+
additions: number;
|
|
94
|
+
deletions: number;
|
|
95
|
+
}
|
|
96
|
+
export interface SessionDetail {
|
|
97
|
+
token: string;
|
|
98
|
+
title: string;
|
|
99
|
+
devTake: string;
|
|
100
|
+
context?: string;
|
|
101
|
+
durationMinutes: number;
|
|
102
|
+
turns: number;
|
|
103
|
+
filesChanged: number;
|
|
104
|
+
locChanged: number;
|
|
105
|
+
skills: string[];
|
|
106
|
+
narrative?: string;
|
|
107
|
+
beats?: Beat[];
|
|
108
|
+
qaPairs?: QaPair[];
|
|
109
|
+
highlights?: string[];
|
|
110
|
+
toolBreakdown?: ToolBreakdownEntry[];
|
|
111
|
+
topFiles?: FileEntry[];
|
|
112
|
+
recordedAt: string;
|
|
113
|
+
sourceTool: string;
|
|
114
|
+
template: string;
|
|
115
|
+
agentSummary?: AgentSummary;
|
|
116
|
+
}
|
|
117
|
+
export interface SessionRenderData {
|
|
118
|
+
user: UserInfo;
|
|
119
|
+
projectSlug?: string;
|
|
120
|
+
session: SessionDetail;
|
|
121
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render data types for static HTML generation.
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the data shapes that the CLI render functions
|
|
5
|
+
* accept. They are intentionally separate from the interactive UI types
|
|
6
|
+
* in `app/src/types.ts` — the render pipeline owns its own contract.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/render/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { archiveSessionFiles } from '../archive.js';
|
|
3
|
+
import { getSourceAudit, getArchiveStats } from '../source-audit.js';
|
|
4
|
+
export function createArchiveRouter(ctx) {
|
|
5
|
+
const router = Router();
|
|
6
|
+
router.get('/api/source-audit', async (_req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const result = await getSourceAudit();
|
|
9
|
+
res.json(result);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
console.error('[source-audit]', err.message);
|
|
13
|
+
res.status(500).json({ error: 'Source audit failed' });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
router.get('/api/archive/stats', async (_req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const stats = await getArchiveStats();
|
|
19
|
+
res.json(stats);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error('[archive-stats]', err.message);
|
|
23
|
+
res.status(500).json({ error: 'Archive stats failed' });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
router.get('/api/archive/health', async (_req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const audit = await getSourceAudit();
|
|
29
|
+
const health = audit.sources.map((s) => ({
|
|
30
|
+
name: s.name,
|
|
31
|
+
health: s.health,
|
|
32
|
+
retentionRisk: s.retentionRisk ?? null,
|
|
33
|
+
}));
|
|
34
|
+
res.json({ sources: health });
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error('[archive-health]', err.message);
|
|
38
|
+
res.status(500).json({ error: 'Archive health failed' });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
router.post('/api/archive/sync', async (_req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
// Get session list from SQLite (fast), then archive their files
|
|
44
|
+
const projects = await ctx.getProjects();
|
|
45
|
+
const allSessions = projects.flatMap((p) => p.sessions);
|
|
46
|
+
const result = await archiveSessionFiles(allSessions);
|
|
47
|
+
res.json({ archived: result.archived, alreadyArchived: result.alreadyArchived });
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error('[archive-sync]', err.message);
|
|
51
|
+
res.status(500).json({ error: 'Archive sync failed' });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return router;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/routes/archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { checkAuthStatus, saveAuthToken } from '../auth.js';
|
|
3
|
+
import { API_URL } from '../config.js';
|
|
4
|
+
export function createAuthRouter(_ctx) {
|
|
5
|
+
const router = Router();
|
|
6
|
+
router.get('/api/auth/status', async (_req, res) => {
|
|
7
|
+
try {
|
|
8
|
+
const status = await checkAuthStatus(API_URL);
|
|
9
|
+
res.json(status);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
res.json({ authenticated: false });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
// Start device auth flow -- proxy to Phoenix
|
|
16
|
+
router.post('/api/auth/login', async (_req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(`${API_URL}/api/device/code`, { method: 'POST' });
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
res.status(response.status).json({ error: 'Failed to start device auth' });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
res.json(data);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error('[auth/login] EXCEPTION:', err);
|
|
28
|
+
res.status(500).json({ error: 'Device auth request failed' });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
// Check username availability — proxy to Phoenix
|
|
32
|
+
router.get('/api/auth/check-username', async (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const username = req.query.username;
|
|
35
|
+
if (!username || username.length < 2) {
|
|
36
|
+
res.json({ available: false, reason: 'Username must be at least 2 characters' });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/i.test(username)) {
|
|
40
|
+
res.json({ available: false, reason: 'Letters, numbers, hyphens, and underscores only' });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const response = await fetch(`${API_URL}/api/username/check?username=${encodeURIComponent(username)}`);
|
|
44
|
+
if (response.ok) {
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
res.json(data);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// If Phoenix doesn't have this endpoint yet, assume available
|
|
50
|
+
res.json({ available: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Phoenix not reachable — assume available for now, signup will validate
|
|
55
|
+
res.json({ available: true });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// Start device auth with a preferred username
|
|
59
|
+
router.post('/api/auth/signup', async (req, res) => {
|
|
60
|
+
try {
|
|
61
|
+
const username = req.body?.username;
|
|
62
|
+
const response = await fetch(`${API_URL}/api/device/code`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify(username ? { preferred_username: username } : {}),
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
res.status(response.status).json({ error: 'Failed to start device auth' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const data = await response.json();
|
|
72
|
+
// Build signup URL: registration page with device code + username pre-filled
|
|
73
|
+
// After registration, Phoenix should redirect to /device?code=xxx to authorize
|
|
74
|
+
const baseUrl = new URL(data.verification_uri).origin;
|
|
75
|
+
const params = new URLSearchParams();
|
|
76
|
+
if (data.user_code)
|
|
77
|
+
params.set('device_code', data.user_code);
|
|
78
|
+
if (username)
|
|
79
|
+
params.set('username', username);
|
|
80
|
+
const signupUri = `${baseUrl}/users/register?${params.toString()}`;
|
|
81
|
+
res.json({ ...data, verification_uri: signupUri });
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error('[auth/signup] EXCEPTION:', err);
|
|
85
|
+
res.status(500).json({ error: 'Signup request failed' });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// Poll for device authorization completion
|
|
89
|
+
router.post('/api/auth/poll', async (req, res) => {
|
|
90
|
+
try {
|
|
91
|
+
const deviceCode = req.body?.device_code;
|
|
92
|
+
if (!deviceCode) {
|
|
93
|
+
res.status(400).json({ error: 'Missing device_code' });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const response = await fetch(`${API_URL}/api/device/token`, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
body: JSON.stringify({ device_code: deviceCode }),
|
|
100
|
+
});
|
|
101
|
+
const data = await response.json();
|
|
102
|
+
if (response.ok && data.access_token) {
|
|
103
|
+
saveAuthToken(data.access_token, data.username);
|
|
104
|
+
res.json({ authenticated: true, username: data.username });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
res.status(response.status).json(data);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
res.status(500).json({ error: 'Poll failed' });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return router;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import { type SessionMeta } from '../parsers/index.js';
|
|
3
|
+
import { type Session } from '../analyzer.js';
|
|
4
|
+
import { displayNameFromDir } from '../sync.js';
|
|
5
|
+
export { displayNameFromDir };
|
|
6
|
+
export interface ProjectInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
dirName: string;
|
|
9
|
+
sessionCount: number;
|
|
10
|
+
sessions: SessionMeta[];
|
|
11
|
+
}
|
|
12
|
+
export interface SessionStats {
|
|
13
|
+
loc: number;
|
|
14
|
+
duration: number;
|
|
15
|
+
files: number;
|
|
16
|
+
turns: number;
|
|
17
|
+
skills: string[];
|
|
18
|
+
date: string;
|
|
19
|
+
/** End time as ISO string (for interval merging of concurrent sessions) */
|
|
20
|
+
endTime?: string;
|
|
21
|
+
}
|
|
22
|
+
import { escapeHtml } from '../format-utils.js';
|
|
23
|
+
export { escapeHtml };
|
|
24
|
+
export interface AgentSummary {
|
|
25
|
+
is_orchestrated: true;
|
|
26
|
+
agents: Array<{
|
|
27
|
+
role: string;
|
|
28
|
+
duration_minutes: number;
|
|
29
|
+
loc_changed: number;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build an agent summary from child session metas. Returns null when
|
|
34
|
+
* there are no children (or none produce valid stats).
|
|
35
|
+
*
|
|
36
|
+
* @param childMetas - Array of child SessionMeta objects
|
|
37
|
+
* @param resolveStats - Async function that returns { duration, loc } for a child meta
|
|
38
|
+
* @param options.deduplicate - When true, only the first occurrence of each role is kept
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildAgentSummary(childMetas: SessionMeta[], resolveStats: (child: SessionMeta) => Promise<{
|
|
41
|
+
duration: number;
|
|
42
|
+
loc: number;
|
|
43
|
+
}>, options?: {
|
|
44
|
+
deduplicate?: boolean;
|
|
45
|
+
}): Promise<AgentSummary | null>;
|
|
46
|
+
/**
|
|
47
|
+
* RouteContext bundles the database handle and helper closures that
|
|
48
|
+
* every router needs. Created once in createApp() and passed to each
|
|
49
|
+
* router factory.
|
|
50
|
+
*/
|
|
51
|
+
export interface RouteContext {
|
|
52
|
+
db: Database.Database;
|
|
53
|
+
sessionsBasePath: string | undefined;
|
|
54
|
+
getProjects: () => Promise<ProjectInfo[]>;
|
|
55
|
+
loadSession: (sessionPath: string, projectName: string, sessionId: string) => Promise<Session>;
|
|
56
|
+
getSessionStats: (meta: SessionMeta, projectName: string) => Promise<SessionStats>;
|
|
57
|
+
mergeSessionIntervals: (stats: SessionStats[]) => number;
|
|
58
|
+
getProjectWithStats: (proj: ProjectInfo) => Promise<Record<string, unknown>>;
|
|
59
|
+
buildPreviewPage: (title: string, bodyHtml: string, banner?: string) => string;
|
|
60
|
+
}
|
|
61
|
+
export declare function createRouteContext(sessionsBasePath?: string, dbPath?: string): RouteContext;
|