heyiam 0.1.7 → 0.1.9
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 -3
- package/dist/config.js +2 -4
- 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 +117 -0
- package/dist/db.js +444 -0
- package/dist/db.js.map +1 -0
- package/dist/export.d.ts +33 -0
- package/dist/export.js +463 -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.d.ts +3 -2
- 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 +104 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/liquid.d.ts +19 -0
- package/dist/render/liquid.js +149 -0
- package/dist/render/liquid.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 +76 -0
- package/dist/routes/context.js +452 -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 +229 -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 +181 -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/summarize.d.ts +0 -7
- package/dist/summarize.js +0 -20
- package/dist/summarize.js.map +1 -1
- package/dist/sync.d.ts +76 -0
- package/dist/sync.js +361 -0
- package/dist/sync.js.map +1 -0
- package/dist/transcript.d.ts +68 -0
- package/dist/transcript.js +275 -0
- package/dist/transcript.js.map +1 -0
- package/package.json +6 -2
- 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,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Liquid template engine for static HTML rendering.
|
|
3
|
+
*
|
|
4
|
+
* Replaces React SSR (ReactDOMServer.renderToStaticMarkup) with
|
|
5
|
+
* liquidjs sync rendering. Templates live in ./templates/*.liquid.
|
|
6
|
+
*/
|
|
7
|
+
import { Liquid } from 'liquidjs';
|
|
8
|
+
import { dirname, resolve } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const engine = new Liquid({
|
|
12
|
+
root: resolve(__dirname, 'templates'),
|
|
13
|
+
extname: '.liquid',
|
|
14
|
+
outputEscape: 'escape',
|
|
15
|
+
});
|
|
16
|
+
// ── Custom filters ───────────────────────────────────────────
|
|
17
|
+
engine.registerFilter('formatDuration', (minutes) => {
|
|
18
|
+
if (minutes < 60)
|
|
19
|
+
return `${Math.round(minutes)}m`;
|
|
20
|
+
const hours = minutes / 60;
|
|
21
|
+
return hours >= 10 ? `${Math.round(hours)}h` : `${hours.toFixed(1)}h`;
|
|
22
|
+
});
|
|
23
|
+
engine.registerFilter('formatLoc', (loc) => {
|
|
24
|
+
return loc >= 1000 ? `${(loc / 1000).toFixed(1)}k` : String(loc);
|
|
25
|
+
});
|
|
26
|
+
engine.registerFilter('formatDate', (iso) => {
|
|
27
|
+
if (!iso)
|
|
28
|
+
return '';
|
|
29
|
+
try {
|
|
30
|
+
return new Date(iso).toLocaleDateString('en-US', {
|
|
31
|
+
month: 'short',
|
|
32
|
+
day: 'numeric',
|
|
33
|
+
year: 'numeric',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return iso;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
engine.registerFilter('formatDateShort', (iso) => {
|
|
41
|
+
if (!iso)
|
|
42
|
+
return '';
|
|
43
|
+
try {
|
|
44
|
+
return new Date(iso).toLocaleDateString('en-US', {
|
|
45
|
+
month: 'short',
|
|
46
|
+
day: 'numeric',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return iso;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
engine.registerFilter('jsonAttr', (value) => {
|
|
54
|
+
return JSON.stringify(value);
|
|
55
|
+
});
|
|
56
|
+
engine.registerFilter('localeNumber', (value) => {
|
|
57
|
+
return value.toLocaleString();
|
|
58
|
+
});
|
|
59
|
+
engine.registerFilter('stripProtocol', (url) => {
|
|
60
|
+
return (url || '').replace(/^https?:\/\/(www\.)?/, '').replace(/\.git$/, '');
|
|
61
|
+
});
|
|
62
|
+
// Duration color cycling for session cards
|
|
63
|
+
const DURATION_COLORS = ['primary', 'green', 'violet'];
|
|
64
|
+
engine.registerFilter('durationColor', (index) => {
|
|
65
|
+
return DURATION_COLORS[index % DURATION_COLORS.length];
|
|
66
|
+
});
|
|
67
|
+
export function renderProject(data, extras) {
|
|
68
|
+
// Pre-compute derived data for the template
|
|
69
|
+
const allSessions = data.allSessions || data.sessions;
|
|
70
|
+
const sourceCounts = {};
|
|
71
|
+
for (const s of data.sessions) {
|
|
72
|
+
const src = s.sourceTool || 'unknown';
|
|
73
|
+
sourceCounts[src] = (sourceCounts[src] || 0) + 1;
|
|
74
|
+
}
|
|
75
|
+
// Use full session data when available (charts need complete Session objects).
|
|
76
|
+
// Strip rawLog and turnTimeline — huge, unused by charts, and could break HTML attributes.
|
|
77
|
+
const chartSessions = (extras?.fullSessions ?? allSessions.map((s) => ({
|
|
78
|
+
id: s.token, title: s.title, date: s.recordedAt,
|
|
79
|
+
durationMinutes: s.durationMinutes, turns: s.turns,
|
|
80
|
+
linesOfCode: s.locChanged, status: 'enhanced',
|
|
81
|
+
projectName: data.project.title, rawLog: [],
|
|
82
|
+
skills: s.skills, source: s.sourceTool,
|
|
83
|
+
filesChanged: s.filesChanged,
|
|
84
|
+
}))).map((s) => {
|
|
85
|
+
const { rawLog, turnTimeline, ...rest } = s;
|
|
86
|
+
return { ...rest, rawLog: [] };
|
|
87
|
+
});
|
|
88
|
+
// Encode JSON safe for single-quoted HTML attributes
|
|
89
|
+
const sessionsJson = JSON.stringify(chartSessions).replace(/'/g, ''');
|
|
90
|
+
const growthJson = sessionsJson; // same data for both charts
|
|
91
|
+
const durationLabel = data.project.totalAgentDurationMinutes ? 'You / Agents' : 'Time';
|
|
92
|
+
// Pick featured sessions — same logic as ProjectDetail.tsx
|
|
93
|
+
const featuredSessionIds = new Set();
|
|
94
|
+
for (const t of data.project.timeline || []) {
|
|
95
|
+
for (const s of t.sessions || []) {
|
|
96
|
+
if (s.featured) {
|
|
97
|
+
featuredSessionIds.add(s.sessionId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Use fullSessions (has status field) for selection, map back to SessionCard for display
|
|
102
|
+
const fullList = extras?.fullSessions ?? [];
|
|
103
|
+
const fullById = new Map(fullList.map((s) => [s.id, s]));
|
|
104
|
+
const cardById = new Map(data.sessions.map((s) => [s.token, s]));
|
|
105
|
+
// Same logic as ProjectDetail.tsx:
|
|
106
|
+
// 1. Featured flag from timeline
|
|
107
|
+
const featuredCards = data.sessions.filter((s) => featuredSessionIds.has(s.token));
|
|
108
|
+
if (featuredCards.length >= 6) {
|
|
109
|
+
// enough
|
|
110
|
+
}
|
|
111
|
+
// 2. Enhanced sessions (status !== 'draft'), sorted by LOC desc
|
|
112
|
+
const enhancedCards = data.sessions
|
|
113
|
+
.filter((s) => {
|
|
114
|
+
if (featuredSessionIds.has(s.token))
|
|
115
|
+
return false;
|
|
116
|
+
const full = fullById.get(s.token);
|
|
117
|
+
return full && (full.status === 'enhanced' || full.status === 'uploaded');
|
|
118
|
+
})
|
|
119
|
+
.sort((a, b) => b.locChanged - a.locChanged);
|
|
120
|
+
// 3. Draft sessions as fallback
|
|
121
|
+
const draftCards = data.sessions
|
|
122
|
+
.filter((s) => {
|
|
123
|
+
if (featuredSessionIds.has(s.token))
|
|
124
|
+
return false;
|
|
125
|
+
const full = fullById.get(s.token);
|
|
126
|
+
return !full || full.status === 'draft';
|
|
127
|
+
});
|
|
128
|
+
const combined = [...featuredCards, ...enhancedCards, ...draftCards];
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const featuredSessions = combined.filter((s) => {
|
|
131
|
+
if (seen.has(s.token))
|
|
132
|
+
return false;
|
|
133
|
+
seen.add(s.token);
|
|
134
|
+
return true;
|
|
135
|
+
}).slice(0, 6);
|
|
136
|
+
return engine.renderFileSync('project', {
|
|
137
|
+
...data,
|
|
138
|
+
arc: extras?.arc ?? [],
|
|
139
|
+
featuredSessions,
|
|
140
|
+
sourceCounts: Object.entries(sourceCounts).map(([tool, count]) => ({ tool, count })),
|
|
141
|
+
sessionsJson,
|
|
142
|
+
growthJson,
|
|
143
|
+
durationLabel,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
export function renderSession(data) {
|
|
147
|
+
return engine.renderFileSync('session', data);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=liquid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liquid.js","sourceRoot":"","sources":["../../src/render/liquid.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,76 @@
|
|
|
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
|
+
/**
|
|
62
|
+
* Build the canonical session list for a project from DB rows + enhanced data.
|
|
63
|
+
* Used by both the dashboard detail endpoint and the export routes so the two
|
|
64
|
+
* never diverge.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildSessionList(db: Database.Database, dirName: string, projectName: string): Session[];
|
|
67
|
+
/**
|
|
68
|
+
* Build the full project detail response — identical data for both the
|
|
69
|
+
* dashboard API endpoint and the HTML export. Single source of truth.
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildProjectDetail(db: Database.Database, proj: ProjectInfo): {
|
|
72
|
+
project: Record<string, unknown>;
|
|
73
|
+
sessions: Session[];
|
|
74
|
+
enhanceCache: unknown;
|
|
75
|
+
};
|
|
76
|
+
export declare function createRouteContext(sessionsBasePath?: string, dbPath?: string): RouteContext;
|