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.
Files changed (143) hide show
  1. package/dist/analyzer.d.ts +3 -3
  2. package/dist/archive.d.ts +14 -0
  3. package/dist/archive.js +125 -0
  4. package/dist/archive.js.map +1 -0
  5. package/dist/auth.d.ts +0 -6
  6. package/dist/auth.js +2 -4
  7. package/dist/auth.js.map +1 -1
  8. package/dist/autostart.d.ts +19 -0
  9. package/dist/autostart.js +103 -0
  10. package/dist/autostart.js.map +1 -0
  11. package/dist/bridge.d.ts +0 -2
  12. package/dist/bridge.js +33 -4
  13. package/dist/bridge.js.map +1 -1
  14. package/dist/config.d.ts +1 -3
  15. package/dist/config.js +2 -4
  16. package/dist/config.js.map +1 -1
  17. package/dist/context-export.d.ts +22 -0
  18. package/dist/context-export.js +230 -0
  19. package/dist/context-export.js.map +1 -0
  20. package/dist/daemon-install.d.ts +23 -0
  21. package/dist/daemon-install.js +155 -0
  22. package/dist/daemon-install.js.map +1 -0
  23. package/dist/db.d.ts +117 -0
  24. package/dist/db.js +444 -0
  25. package/dist/db.js.map +1 -0
  26. package/dist/export.d.ts +33 -0
  27. package/dist/export.js +463 -0
  28. package/dist/export.js.map +1 -0
  29. package/dist/format-utils.d.ts +6 -0
  30. package/dist/format-utils.js +15 -0
  31. package/dist/format-utils.js.map +1 -0
  32. package/dist/index.js +474 -117
  33. package/dist/index.js.map +1 -1
  34. package/dist/llm/project-enhance.d.ts +3 -2
  35. package/dist/llm/project-enhance.js +1 -1
  36. package/dist/parsers/claude.js +73 -0
  37. package/dist/parsers/claude.js.map +1 -1
  38. package/dist/parsers/codex.js +1 -1
  39. package/dist/parsers/codex.js.map +1 -1
  40. package/dist/parsers/cursor.d.ts +2 -0
  41. package/dist/parsers/cursor.js +14 -26
  42. package/dist/parsers/cursor.js.map +1 -1
  43. package/dist/parsers/gemini.d.ts +3 -2
  44. package/dist/parsers/gemini.js +198 -21
  45. package/dist/parsers/gemini.js.map +1 -1
  46. package/dist/parsers/index.d.ts +1 -1
  47. package/dist/parsers/index.js +23 -7
  48. package/dist/parsers/index.js.map +1 -1
  49. package/dist/parsers/types.d.ts +27 -1
  50. package/dist/render/build-render-data.d.ts +59 -0
  51. package/dist/render/build-render-data.js +101 -0
  52. package/dist/render/build-render-data.js.map +1 -0
  53. package/dist/render/components/PortfolioPage.d.ts +4 -0
  54. package/dist/render/components/PortfolioPage.js +16 -0
  55. package/dist/render/components/PortfolioPage.js.map +1 -0
  56. package/dist/render/components/ProjectPage.d.ts +4 -0
  57. package/dist/render/components/ProjectPage.js +101 -0
  58. package/dist/render/components/ProjectPage.js.map +1 -0
  59. package/dist/render/components/SessionPage.d.ts +4 -0
  60. package/dist/render/components/SessionPage.js +29 -0
  61. package/dist/render/components/SessionPage.js.map +1 -0
  62. package/dist/render/index.d.ts +37 -0
  63. package/dist/render/index.js +104 -0
  64. package/dist/render/index.js.map +1 -0
  65. package/dist/render/liquid.d.ts +19 -0
  66. package/dist/render/liquid.js +149 -0
  67. package/dist/render/liquid.js.map +1 -0
  68. package/dist/render/types.d.ts +121 -0
  69. package/dist/render/types.js +9 -0
  70. package/dist/render/types.js.map +1 -0
  71. package/dist/routes/archive.d.ts +3 -0
  72. package/dist/routes/archive.js +56 -0
  73. package/dist/routes/archive.js.map +1 -0
  74. package/dist/routes/auth.d.ts +3 -0
  75. package/dist/routes/auth.js +116 -0
  76. package/dist/routes/auth.js.map +1 -0
  77. package/dist/routes/context.d.ts +76 -0
  78. package/dist/routes/context.js +452 -0
  79. package/dist/routes/context.js.map +1 -0
  80. package/dist/routes/dashboard.d.ts +3 -0
  81. package/dist/routes/dashboard.js +103 -0
  82. package/dist/routes/dashboard.js.map +1 -0
  83. package/dist/routes/enhance.d.ts +3 -0
  84. package/dist/routes/enhance.js +305 -0
  85. package/dist/routes/enhance.js.map +1 -0
  86. package/dist/routes/export.d.ts +3 -0
  87. package/dist/routes/export.js +229 -0
  88. package/dist/routes/export.js.map +1 -0
  89. package/dist/routes/index.d.ts +12 -0
  90. package/dist/routes/index.js +13 -0
  91. package/dist/routes/index.js.map +1 -0
  92. package/dist/routes/preview.d.ts +3 -0
  93. package/dist/routes/preview.js +191 -0
  94. package/dist/routes/preview.js.map +1 -0
  95. package/dist/routes/projects.d.ts +3 -0
  96. package/dist/routes/projects.js +181 -0
  97. package/dist/routes/projects.js.map +1 -0
  98. package/dist/routes/publish.d.ts +3 -0
  99. package/dist/routes/publish.js +466 -0
  100. package/dist/routes/publish.js.map +1 -0
  101. package/dist/routes/search.d.ts +3 -0
  102. package/dist/routes/search.js +110 -0
  103. package/dist/routes/search.js.map +1 -0
  104. package/dist/routes/sessions.d.ts +3 -0
  105. package/dist/routes/sessions.js +103 -0
  106. package/dist/routes/sessions.js.map +1 -0
  107. package/dist/routes/settings.d.ts +3 -0
  108. package/dist/routes/settings.js +30 -0
  109. package/dist/routes/settings.js.map +1 -0
  110. package/dist/screenshot.d.ts +5 -2
  111. package/dist/screenshot.js +187 -13
  112. package/dist/screenshot.js.map +1 -1
  113. package/dist/search.d.ts +30 -0
  114. package/dist/search.js +153 -0
  115. package/dist/search.js.map +1 -0
  116. package/dist/server.d.ts +1 -1
  117. package/dist/server.js +55 -1318
  118. package/dist/server.js.map +1 -1
  119. package/dist/settings.d.ts +23 -6
  120. package/dist/settings.js +36 -12
  121. package/dist/settings.js.map +1 -1
  122. package/dist/source-audit.d.ts +29 -0
  123. package/dist/source-audit.js +203 -0
  124. package/dist/source-audit.js.map +1 -0
  125. package/dist/summarize.d.ts +0 -7
  126. package/dist/summarize.js +0 -20
  127. package/dist/summarize.js.map +1 -1
  128. package/dist/sync.d.ts +76 -0
  129. package/dist/sync.js +361 -0
  130. package/dist/sync.js.map +1 -0
  131. package/dist/transcript.d.ts +68 -0
  132. package/dist/transcript.js +275 -0
  133. package/dist/transcript.js.map +1 -0
  134. package/package.json +6 -2
  135. package/app/dist/assets/html2canvas-Cwn_rrOw.js +0 -5
  136. package/app/dist/assets/index-CEQyTkgN.js +0 -14
  137. package/app/dist/assets/index-DLh5xRE8.css +0 -1
  138. package/app/dist/favicon.svg +0 -5
  139. package/app/dist/icons.svg +0 -24
  140. package/app/dist/index.html +0 -20
  141. package/dist/machine-key.d.ts +0 -10
  142. package/dist/machine-key.js +0 -51
  143. 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, '&#39;');
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,3 @@
1
+ import { Router } from 'express';
2
+ import type { RouteContext } from './context.js';
3
+ export declare function createArchiveRouter(ctx: RouteContext): Router;
@@ -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,3 @@
1
+ import { Router } from 'express';
2
+ import type { RouteContext } from './context.js';
3
+ export declare function createAuthRouter(_ctx: RouteContext): Router;
@@ -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;