@tom2012/cc-web 1.5.10

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/backend/dist/auth.d.ts +15 -0
  4. package/backend/dist/auth.d.ts.map +1 -0
  5. package/backend/dist/auth.js +92 -0
  6. package/backend/dist/auth.js.map +1 -0
  7. package/backend/dist/config.d.ts +33 -0
  8. package/backend/dist/config.d.ts.map +1 -0
  9. package/backend/dist/config.js +155 -0
  10. package/backend/dist/config.js.map +1 -0
  11. package/backend/dist/index.d.ts +3 -0
  12. package/backend/dist/index.d.ts.map +1 -0
  13. package/backend/dist/index.js +499 -0
  14. package/backend/dist/index.js.map +1 -0
  15. package/backend/dist/routes/auth.d.ts +3 -0
  16. package/backend/dist/routes/auth.d.ts.map +1 -0
  17. package/backend/dist/routes/auth.js +108 -0
  18. package/backend/dist/routes/auth.js.map +1 -0
  19. package/backend/dist/routes/filesystem.d.ts +3 -0
  20. package/backend/dist/routes/filesystem.d.ts.map +1 -0
  21. package/backend/dist/routes/filesystem.js +243 -0
  22. package/backend/dist/routes/filesystem.js.map +1 -0
  23. package/backend/dist/routes/projects.d.ts +3 -0
  24. package/backend/dist/routes/projects.d.ts.map +1 -0
  25. package/backend/dist/routes/projects.js +235 -0
  26. package/backend/dist/routes/projects.js.map +1 -0
  27. package/backend/dist/routes/shortcuts.d.ts +3 -0
  28. package/backend/dist/routes/shortcuts.d.ts.map +1 -0
  29. package/backend/dist/routes/shortcuts.js +88 -0
  30. package/backend/dist/routes/shortcuts.js.map +1 -0
  31. package/backend/dist/routes/update.d.ts +3 -0
  32. package/backend/dist/routes/update.d.ts.map +1 -0
  33. package/backend/dist/routes/update.js +104 -0
  34. package/backend/dist/routes/update.js.map +1 -0
  35. package/backend/dist/session-manager.d.ts +47 -0
  36. package/backend/dist/session-manager.d.ts.map +1 -0
  37. package/backend/dist/session-manager.js +345 -0
  38. package/backend/dist/session-manager.js.map +1 -0
  39. package/backend/dist/terminal-manager.d.ts +27 -0
  40. package/backend/dist/terminal-manager.d.ts.map +1 -0
  41. package/backend/dist/terminal-manager.js +211 -0
  42. package/backend/dist/terminal-manager.js.map +1 -0
  43. package/backend/dist/types.d.ts +17 -0
  44. package/backend/dist/types.d.ts.map +1 -0
  45. package/backend/dist/types.js +3 -0
  46. package/backend/dist/types.js.map +1 -0
  47. package/backend/dist/usage-terminal.d.ts +18 -0
  48. package/backend/dist/usage-terminal.d.ts.map +1 -0
  49. package/backend/dist/usage-terminal.js +189 -0
  50. package/backend/dist/usage-terminal.js.map +1 -0
  51. package/backend/package-lock.json +1965 -0
  52. package/backend/package.json +31 -0
  53. package/bin/ccweb.js +478 -0
  54. package/electron/dist/main.js +455 -0
  55. package/frontend/dist/assets/index-CQjbS4zv.css +32 -0
  56. package/frontend/dist/assets/index-CtyR65A4.js +434 -0
  57. package/frontend/dist/index.html +14 -0
  58. package/frontend/dist/terminal.svg +4 -0
  59. package/package.json +88 -0
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const config_1 = require("../config");
5
+ const terminal_manager_1 = require("../terminal-manager");
6
+ const router = (0, express_1.Router)();
7
+ const MEMORY_SAVE_COMMAND = '请更新与本项目相关的全部记忆、工作计划、已完成工作、未完成工作和后台任务\n';
8
+ // Idle = no PTY output for this many ms
9
+ const IDLE_THRESHOLD_MS = 5000;
10
+ const POLL_INTERVAL_MS = 2000;
11
+ const MAX_WAIT_MS = 120000; // 2 minutes max per project
12
+ /**
13
+ * GET /api/update/check-running
14
+ * Returns list of running projects so the frontend can warn the user.
15
+ */
16
+ router.get('/check-running', (_req, res) => {
17
+ const projects = (0, config_1.getProjects)();
18
+ const running = projects.filter((p) => {
19
+ if (p.status !== 'running')
20
+ return false;
21
+ return terminal_manager_1.terminalManager.hasTerminal(p.id);
22
+ });
23
+ res.json({
24
+ runningCount: running.length,
25
+ projects: running.map((p) => ({ id: p.id, name: p.name, status: p.status })),
26
+ });
27
+ });
28
+ /**
29
+ * POST /api/update/prepare
30
+ * For each running project:
31
+ * 1. Send memory-save command to Claude
32
+ * 2. Wait until Claude goes idle (no PTY output for IDLE_THRESHOLD_MS)
33
+ * 3. Stop the terminal
34
+ * Returns per-project status.
35
+ */
36
+ router.post('/prepare', async (_req, res) => {
37
+ const projects = (0, config_1.getProjects)();
38
+ const running = projects.filter((p) => p.status === 'running' && terminal_manager_1.terminalManager.hasTerminal(p.id));
39
+ if (running.length === 0) {
40
+ res.json({ success: true, results: [], message: 'No running projects' });
41
+ return;
42
+ }
43
+ const results = [];
44
+ for (const project of running) {
45
+ const status = {
46
+ id: project.id,
47
+ name: project.name,
48
+ status: 'command_sent',
49
+ };
50
+ try {
51
+ // 1. Send the memory-save command
52
+ terminal_manager_1.terminalManager.writeRaw(project.id, MEMORY_SAVE_COMMAND);
53
+ status.status = 'waiting_idle';
54
+ // 2. Wait for Claude to finish processing (go idle)
55
+ const idle = await waitForIdle(project.id, IDLE_THRESHOLD_MS, MAX_WAIT_MS);
56
+ if (!idle) {
57
+ status.status = 'stopped';
58
+ status.message = 'Timed out waiting for idle, stopped anyway';
59
+ }
60
+ else {
61
+ status.status = 'stopped';
62
+ status.message = 'Memory saved and stopped';
63
+ }
64
+ // 3. Stop the terminal
65
+ terminal_manager_1.terminalManager.stop(project.id);
66
+ }
67
+ catch (err) {
68
+ status.status = 'error';
69
+ status.message = err instanceof Error ? err.message : 'Unknown error';
70
+ }
71
+ results.push(status);
72
+ }
73
+ res.json({ success: true, results });
74
+ });
75
+ /**
76
+ * Wait until a terminal has been idle (no PTY output) for `idleMs` milliseconds.
77
+ * Returns true if idle was detected, false if `timeoutMs` exceeded.
78
+ */
79
+ function waitForIdle(projectId, idleMs, timeoutMs) {
80
+ return new Promise((resolve) => {
81
+ const deadline = Date.now() + timeoutMs;
82
+ const check = () => {
83
+ if (Date.now() > deadline) {
84
+ resolve(false);
85
+ return;
86
+ }
87
+ if (!terminal_manager_1.terminalManager.hasTerminal(projectId)) {
88
+ // Terminal already exited
89
+ resolve(true);
90
+ return;
91
+ }
92
+ const lastActivity = terminal_manager_1.terminalManager.getLastActivityAt(projectId);
93
+ if (lastActivity !== null && Date.now() - lastActivity >= idleMs) {
94
+ resolve(true);
95
+ return;
96
+ }
97
+ setTimeout(check, POLL_INTERVAL_MS);
98
+ };
99
+ // Give Claude a moment to start processing before checking idle
100
+ setTimeout(check, 3000);
101
+ });
102
+ }
103
+ exports.default = router;
104
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/routes/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * SessionManager — reads conversation history directly from Claude Code's
3
+ * native JSONL files at ~/.claude/projects/{encoded-path}/{sessionId}.jsonl
4
+ *
5
+ * No PTY parsing, no ANSI stripping, no heuristics needed.
6
+ */
7
+ export interface SessionMessage {
8
+ role: 'user' | 'assistant';
9
+ content: string;
10
+ timestamp: string;
11
+ }
12
+ export interface Session {
13
+ id: string;
14
+ projectId: string;
15
+ startedAt: string;
16
+ messages: SessionMessage[];
17
+ }
18
+ declare class SessionManager {
19
+ private watchers;
20
+ /** Call when a new PTY starts for a project */
21
+ startSession(projectId: string, folderPath: string): void;
22
+ private pruneOldSessions;
23
+ /** Stop the session poller for a project (public for cleanup on terminal stop) */
24
+ stopWatcherForProject(projectId: string): void;
25
+ private stopWatcher;
26
+ private poll;
27
+ /** Find the newest JSONL created after startedAt in Claude's project dir */
28
+ private findJsonl;
29
+ /** Read any new lines from the JSONL file and extract messages */
30
+ private readNewLines;
31
+ private parseLine;
32
+ private appendMessages;
33
+ /** Resolve folderPath for a project — from active watcher or project config */
34
+ private resolveFolderPath;
35
+ /** Read session files from a directory */
36
+ private readSessionsFromDir;
37
+ listSessions(projectId: string): (Omit<Session, 'messages'> & {
38
+ messageCount: number;
39
+ isCurrent: boolean;
40
+ })[];
41
+ /** Validate sessionId to prevent path traversal */
42
+ private isValidSessionId;
43
+ getSession(projectId: string, sessionId: string): Session | null;
44
+ }
45
+ export declare const sessionManager: SessionManager;
46
+ export {};
47
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAiFD,cAAM,cAAc;IAClB,OAAO,CAAC,QAAQ,CAAiC;IAEjD,+CAA+C;IAC/C,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA8BzD,OAAO,CAAC,gBAAgB;IAkBxB,kFAAkF;IAClF,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9C,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,IAAI;IAeZ,4EAA4E;IAC5E,OAAO,CAAC,SAAS;IAiBjB,kEAAkE;IAClE,OAAO,CAAC,YAAY;IAsCpB,OAAO,CAAC,SAAS;IAqBjB,OAAO,CAAC,cAAc;IAetB,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB;IAOzB,0CAA0C;IAC1C,OAAO,CAAC,mBAAmB;IAqB3B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,EAAE;IAyB7G,mDAAmD;IACnD,OAAO,CAAC,gBAAgB;IAIxB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;CAmBjE;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ /**
3
+ * SessionManager — reads conversation history directly from Claude Code's
4
+ * native JSONL files at ~/.claude/projects/{encoded-path}/{sessionId}.jsonl
5
+ *
6
+ * No PTY parsing, no ANSI stripping, no heuristics needed.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.sessionManager = void 0;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ const uuid_1 = require("uuid");
47
+ const config_1 = require("./config");
48
+ // ── Path helpers ─────────────────────────────────────────────────────────────
49
+ const LEGACY_SESSIONS_DIR = path.join(config_1.DATA_DIR, 'sessions');
50
+ /** Convert /abs/path → -abs-path (Claude Code project dir naming) */
51
+ function encodeProjectPath(folderPath) {
52
+ // Claude Code replaces both '/' and spaces with '-'
53
+ return folderPath.replace(/[\/ ]/g, '-');
54
+ }
55
+ function claudeProjectDir(folderPath) {
56
+ return path.join(os.homedir(), '.claude', 'projects', encodeProjectPath(folderPath));
57
+ }
58
+ /** New location: {folderPath}/.ccweb/sessions/ */
59
+ function projectSessionsDir(folderPath) {
60
+ return (0, config_1.ccwebSessionsDir)(folderPath);
61
+ }
62
+ function projectSessionFile(folderPath, sessionId) {
63
+ return path.join(projectSessionsDir(folderPath), `${sessionId}.json`);
64
+ }
65
+ /** Legacy location: data/sessions/{projectId}/ */
66
+ function legacySessionsDir(projectId) {
67
+ return path.join(LEGACY_SESSIONS_DIR, projectId);
68
+ }
69
+ function legacySessionFile(projectId, sessionId) {
70
+ return path.join(legacySessionsDir(projectId), `${sessionId}.json`);
71
+ }
72
+ // ── Content extraction ────────────────────────────────────────────────────────
73
+ function extractText(content) {
74
+ if (!content)
75
+ return '';
76
+ if (typeof content === 'string')
77
+ return content.trim();
78
+ return content
79
+ .filter((b) => b.type === 'text' && b.text)
80
+ .map((b) => b.text.trim())
81
+ .join('\n')
82
+ .trim();
83
+ }
84
+ function isInternalUserMessage(content) {
85
+ // Skip slash commands and internal messages
86
+ return content.startsWith('<command-') || content.startsWith('/');
87
+ }
88
+ // ── SessionManager ────────────────────────────────────────────────────────────
89
+ class SessionManager {
90
+ constructor() {
91
+ this.watchers = new Map();
92
+ }
93
+ /** Call when a new PTY starts for a project */
94
+ startSession(projectId, folderPath) {
95
+ // Stop any previous watcher
96
+ this.stopWatcher(projectId);
97
+ const sessionId = `${Date.now()}-${(0, uuid_1.v4)().slice(0, 8)}`;
98
+ const startedAt = Date.now();
99
+ // Create our session file in .ccweb/sessions/
100
+ fs.mkdirSync(projectSessionsDir(folderPath), { recursive: true });
101
+ const session = { id: sessionId, projectId, startedAt: new Date(startedAt).toISOString(), messages: [] };
102
+ fs.writeFileSync(projectSessionFile(folderPath, sessionId), JSON.stringify(session, null, 2), 'utf-8');
103
+ const state = {
104
+ sessionId,
105
+ folderPath,
106
+ jsonlPath: null,
107
+ fileOffset: 0,
108
+ pollTimer: null,
109
+ startedAt,
110
+ };
111
+ this.watchers.set(projectId, state);
112
+ // Prune old sessions (keep latest 20)
113
+ this.pruneOldSessions(folderPath, projectId);
114
+ // Poll: find Claude's JSONL file, then tail it
115
+ state.pollTimer = setInterval(() => this.poll(projectId, folderPath), 2000);
116
+ console.log(`[SessionManager] Started session ${sessionId} for project ${projectId}`);
117
+ }
118
+ pruneOldSessions(folderPath, projectId, keep = 20) {
119
+ const dirs = [projectSessionsDir(folderPath), legacySessionsDir(projectId)];
120
+ for (const dir of dirs) {
121
+ if (!fs.existsSync(dir))
122
+ continue;
123
+ try {
124
+ const files = fs.readdirSync(dir)
125
+ .filter((f) => f.endsWith('.json'))
126
+ .sort(); // session filenames start with timestamp, so sort = chronological
127
+ if (files.length <= keep)
128
+ continue;
129
+ const toDelete = files.slice(0, files.length - keep);
130
+ for (const f of toDelete) {
131
+ try {
132
+ fs.unlinkSync(path.join(dir, f));
133
+ }
134
+ catch { /**/ }
135
+ }
136
+ console.log(`[SessionManager] Pruned ${toDelete.length} old sessions in ${dir}`);
137
+ }
138
+ catch { /**/ }
139
+ }
140
+ }
141
+ /** Stop the session poller for a project (public for cleanup on terminal stop) */
142
+ stopWatcherForProject(projectId) {
143
+ this.stopWatcher(projectId);
144
+ }
145
+ stopWatcher(projectId) {
146
+ const state = this.watchers.get(projectId);
147
+ if (state?.pollTimer)
148
+ clearInterval(state.pollTimer);
149
+ this.watchers.delete(projectId);
150
+ }
151
+ poll(projectId, folderPath) {
152
+ const state = this.watchers.get(projectId);
153
+ if (!state)
154
+ return;
155
+ // If we haven't found the JSONL yet, look for it
156
+ if (!state.jsonlPath) {
157
+ state.jsonlPath = this.findJsonl(folderPath, state.startedAt);
158
+ if (!state.jsonlPath)
159
+ return; // not yet created
160
+ state.fileOffset = 0;
161
+ console.log(`[SessionManager] Watching Claude JSONL: ${state.jsonlPath}`);
162
+ }
163
+ this.readNewLines(projectId, state);
164
+ }
165
+ /** Find the newest JSONL created after startedAt in Claude's project dir */
166
+ findJsonl(folderPath, startedAt) {
167
+ const dir = claudeProjectDir(folderPath);
168
+ if (!fs.existsSync(dir))
169
+ return null;
170
+ try {
171
+ const files = fs.readdirSync(dir)
172
+ .filter((f) => f.endsWith('.jsonl'))
173
+ .map((f) => ({ f, mtime: fs.statSync(path.join(dir, f)).mtimeMs }))
174
+ .filter(({ mtime }) => mtime >= startedAt - 5000) // 5s grace
175
+ .sort((a, b) => b.mtime - a.mtime);
176
+ return files.length > 0 ? path.join(dir, files[0].f) : null;
177
+ }
178
+ catch {
179
+ return null;
180
+ }
181
+ }
182
+ /** Read any new lines from the JSONL file and extract messages */
183
+ readNewLines(projectId, state) {
184
+ if (!state.jsonlPath)
185
+ return;
186
+ let fd = null;
187
+ try {
188
+ const stat = fs.statSync(state.jsonlPath);
189
+ if (stat.size <= state.fileOffset)
190
+ return; // nothing new
191
+ fd = fs.openSync(state.jsonlPath, 'r');
192
+ const toRead = stat.size - state.fileOffset;
193
+ const buf = Buffer.alloc(toRead);
194
+ fs.readSync(fd, buf, 0, toRead, state.fileOffset);
195
+ state.fileOffset = stat.size;
196
+ const lines = buf.toString('utf-8').split('\n').filter((l) => l.trim());
197
+ let changed = false;
198
+ const newMsgs = [];
199
+ for (const line of lines) {
200
+ const msg = this.parseLine(line);
201
+ if (msg)
202
+ newMsgs.push(msg);
203
+ }
204
+ if (newMsgs.length > 0) {
205
+ this.appendMessages(state.folderPath, state.sessionId, newMsgs);
206
+ changed = true;
207
+ }
208
+ if (changed) {
209
+ console.log(`[SessionManager] Updated session ${state.sessionId}`);
210
+ }
211
+ }
212
+ catch {
213
+ // file may be temporarily locked or missing — try again next poll
214
+ }
215
+ finally {
216
+ if (fd !== null)
217
+ try {
218
+ fs.closeSync(fd);
219
+ }
220
+ catch { /**/ }
221
+ }
222
+ }
223
+ parseLine(line) {
224
+ let record;
225
+ try {
226
+ record = JSON.parse(line);
227
+ }
228
+ catch {
229
+ return null;
230
+ }
231
+ // User message
232
+ if (record.type === 'user' && record.message?.role === 'user') {
233
+ const text = extractText(record.message.content);
234
+ if (!text || isInternalUserMessage(text))
235
+ return null;
236
+ return { role: 'user', content: text, timestamp: record.timestamp ?? new Date().toISOString() };
237
+ }
238
+ // Assistant message — only keep text blocks, skip thinking/tool_use
239
+ if (record.type === 'assistant' && record.message?.role === 'assistant') {
240
+ const text = extractText(record.message.content);
241
+ if (!text || text.length < 5)
242
+ return null;
243
+ return { role: 'assistant', content: text, timestamp: record.timestamp ?? new Date().toISOString() };
244
+ }
245
+ return null;
246
+ }
247
+ appendMessages(folderPath, sessionId, msgs) {
248
+ const file = projectSessionFile(folderPath, sessionId);
249
+ try {
250
+ const session = JSON.parse(fs.readFileSync(file, 'utf-8'));
251
+ session.messages.push(...msgs);
252
+ const tmpPath = file + `.tmp.${process.pid}`;
253
+ fs.writeFileSync(tmpPath, JSON.stringify(session, null, 2), 'utf-8');
254
+ fs.renameSync(tmpPath, file);
255
+ }
256
+ catch (err) {
257
+ console.error(`[SessionManager] Failed to append messages to session ${sessionId}:`, err);
258
+ }
259
+ }
260
+ // ── Query API ──────────────────────────────────────────────────────────────
261
+ /** Resolve folderPath for a project — from active watcher or project config */
262
+ resolveFolderPath(projectId) {
263
+ const watcher = this.watchers.get(projectId);
264
+ if (watcher)
265
+ return watcher.folderPath;
266
+ const project = (0, config_1.getProject)(projectId);
267
+ return project?.folderPath ?? null;
268
+ }
269
+ /** Read session files from a directory */
270
+ readSessionsFromDir(dir, currentId) {
271
+ if (!fs.existsSync(dir))
272
+ return [];
273
+ try {
274
+ return fs.readdirSync(dir)
275
+ .filter((f) => f.endsWith('.json'))
276
+ .map((f) => {
277
+ try {
278
+ const s = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
279
+ return {
280
+ id: s.id,
281
+ projectId: s.projectId,
282
+ startedAt: s.startedAt,
283
+ messageCount: s.messages.length,
284
+ isCurrent: s.id === currentId,
285
+ };
286
+ }
287
+ catch {
288
+ return null;
289
+ }
290
+ })
291
+ .filter((s) => s !== null);
292
+ }
293
+ catch {
294
+ return [];
295
+ }
296
+ }
297
+ listSessions(projectId) {
298
+ const currentId = this.watchers.get(projectId)?.sessionId;
299
+ const folderPath = this.resolveFolderPath(projectId);
300
+ // Collect from .ccweb/sessions/ (primary) and legacy data/sessions/ (fallback)
301
+ const results = [];
302
+ const seenIds = new Set();
303
+ if (folderPath) {
304
+ for (const s of this.readSessionsFromDir(projectSessionsDir(folderPath), currentId)) {
305
+ results.push(s);
306
+ seenIds.add(s.id);
307
+ }
308
+ }
309
+ // Legacy fallback — include sessions not already in .ccweb/
310
+ for (const s of this.readSessionsFromDir(legacySessionsDir(projectId), currentId)) {
311
+ if (!seenIds.has(s.id)) {
312
+ results.push(s);
313
+ }
314
+ }
315
+ return results.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
316
+ }
317
+ /** Validate sessionId to prevent path traversal */
318
+ isValidSessionId(sessionId) {
319
+ return /^[\w-]+$/.test(sessionId) && !sessionId.includes('..');
320
+ }
321
+ getSession(projectId, sessionId) {
322
+ if (!this.isValidSessionId(sessionId))
323
+ return null;
324
+ const folderPath = this.resolveFolderPath(projectId);
325
+ // Try .ccweb/ first
326
+ if (folderPath) {
327
+ const file = projectSessionFile(folderPath, sessionId);
328
+ try {
329
+ if (fs.existsSync(file)) {
330
+ return JSON.parse(fs.readFileSync(file, 'utf-8'));
331
+ }
332
+ }
333
+ catch { /* fall through */ }
334
+ }
335
+ // Legacy fallback
336
+ try {
337
+ return JSON.parse(fs.readFileSync(legacySessionFile(projectId, sessionId), 'utf-8'));
338
+ }
339
+ catch {
340
+ return null;
341
+ }
342
+ }
343
+ }
344
+ exports.sessionManager = new SessionManager();
345
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,+BAAoC;AACpC,qCAAkE;AAkClE,gFAAgF;AAEhF,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAQ,EAAE,UAAU,CAAC,CAAC;AAE5D,qEAAqE;AACrE,SAAS,iBAAiB,CAAC,UAAkB;IAC3C,oDAAoD;IACpD,OAAO,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,kDAAkD;AAClD,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,OAAO,IAAA,yBAAgB,EAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,kBAAkB,CAAC,UAAkB,EAAE,SAAiB;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,kDAAkD;AAClD,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB,EAAE,SAAiB;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,iFAAiF;AAEjF,SAAS,WAAW,CAAC,OAA4C;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IACvD,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,IAAI,CAAC;SACV,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAe;IAC5C,4CAA4C;IAC5C,OAAO,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACpE,CAAC;AAaD,iFAAiF;AAEjF,MAAM,cAAc;IAApB;QACU,aAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;IAyPnD,CAAC;IAvPC,+CAA+C;IAC/C,YAAY,CAAC,SAAiB,EAAE,UAAkB;QAChD,4BAA4B;QAC5B,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAE5B,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAA,SAAM,GAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,8CAA8C;QAC9C,EAAE,CAAC,SAAS,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,OAAO,GAAY,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAClH,EAAE,CAAC,aAAa,CAAC,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAEvG,MAAM,KAAK,GAAe;YACxB,SAAS;YACT,UAAU;YACV,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,IAAI;YACf,SAAS;SACV,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAEpC,sCAAsC;QACtC,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAE7C,+CAA+C;QAC/C,KAAK,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;QAC5E,OAAO,CAAC,GAAG,CAAC,oCAAoC,SAAS,gBAAgB,SAAS,EAAE,CAAC,CAAC;IACxF,CAAC;IAEO,gBAAgB,CAAC,UAAkB,EAAE,SAAiB,EAAE,IAAI,GAAG,EAAE;QACvE,MAAM,IAAI,GAAG,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;QAC5E,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAClC,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC;qBAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;qBAClC,IAAI,EAAE,CAAC,CAAC,kEAAkE;gBAC7E,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI;oBAAE,SAAS;gBACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;gBACrD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;oBACzB,IAAI,CAAC;wBAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,2BAA2B,QAAQ,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC,CAAC;YACnF,CAAC;YAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,qBAAqB,CAAC,SAAiB;QACrC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAEO,WAAW,CAAC,SAAiB;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,KAAK,EAAE,SAAS;YAAE,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAEO,IAAI,CAAC,SAAiB,EAAE,UAAkB;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,iDAAiD;QACjD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAC9D,IAAI,CAAC,KAAK,CAAC,SAAS;gBAAE,OAAO,CAAC,kBAAkB;YAChD,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,4EAA4E;IACpE,SAAS,CAAC,UAAkB,EAAE,SAAiB;QACrD,MAAM,GAAG,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAErC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC;iBAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;iBACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;iBAClE,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,GAAG,IAAI,CAAC,CAAC,WAAW;iBAC5D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;YAErC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,kEAAkE;IAC1D,YAAY,CAAC,SAAiB,EAAE,KAAiB;QACvD,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,OAAO;QAE7B,IAAI,EAAE,GAAkB,IAAI,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC1C,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,UAAU;gBAAE,OAAO,CAAC,cAAc;YAEzD,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC;YAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACjC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;YAClD,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;YAE7B,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACxE,IAAI,OAAO,GAAG,KAAK,CAAC;YAEpB,MAAM,OAAO,GAAqB,EAAE,CAAC;YACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBACjC,IAAI,GAAG;oBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAChE,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YAED,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,GAAG,CAAC,oCAAoC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;gBAAS,CAAC;YACT,IAAI,EAAE,KAAK,IAAI;gBAAE,IAAI,CAAC;oBAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,IAAY;QAC5B,IAAI,MAAoB,CAAC;QACzB,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAEzE,eAAe;QACf,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,IAAI,qBAAqB,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAClG,CAAC;QAED,oEAAoE;QACpE,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;YACxE,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC1C,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACvG,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,cAAc,CAAC,UAAkB,EAAE,SAAiB,EAAE,IAAsB;QAClF,MAAM,IAAI,GAAG,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YACpE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;YAC7C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACrE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,yDAAyD,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,8EAA8E;IAE9E,+EAA+E;IACvE,iBAAiB,CAAC,SAAiB;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,OAAO,GAAG,IAAA,mBAAU,EAAC,SAAS,CAAC,CAAC;QACtC,OAAO,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,0CAA0C;IAClC,mBAAmB,CAAC,GAAW,EAAE,SAA6B;QACpE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC;iBACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;iBAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACT,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAY,CAAC;oBAC7E,OAAO;wBACL,EAAE,EAAE,CAAC,CAAC,EAAE;wBACR,SAAS,EAAE,CAAC,CAAC,SAAS;wBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;wBACtB,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM;wBAC/B,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS;qBAC9B,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBAAC,OAAO,IAAI,CAAC;gBAAC,CAAC;YAC1B,CAAC,CAAC;iBACD,MAAM,CAAC,CAAC,CAAC,EAAiF,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAC9G,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,EAAE,CAAC;QAAC,CAAC;IACxB,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAErD,+EAA+E;QAC/E,MAAM,OAAO,GAAiF,EAAE,CAAC;QACjG,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;gBACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QAED,4DAA4D;QAC5D,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;YAClF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,mDAAmD;IAC3C,gBAAgB,CAAC,SAAiB;QACxC,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB;QAC7C,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QACnD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAErD,oBAAoB;QACpB,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAY,CAAC;gBAC/D,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;QAChC,CAAC;QAED,kBAAkB;QAClB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAY,CAAC;QAClG,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IAC1B,CAAC;CACF;AAEY,QAAA,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Project } from './types';
2
+ type RawBroadcastFn = (data: string) => void;
3
+ declare class TerminalManager {
4
+ private terminals;
5
+ /** Pending auto-restart timers, keyed by projectId. Tracked separately so stop() can cancel them. */
6
+ private restartTimers;
7
+ getOrCreate(project: Project, rawBroadcast?: RawBroadcastFn): void;
8
+ updateBroadcast(projectId: string, rawBroadcast: RawBroadcastFn): void;
9
+ /** Write raw keystrokes directly to the PTY. */
10
+ writeRaw(projectId: string, data: string): void;
11
+ /** Resize the PTY to match the browser terminal. */
12
+ resize(projectId: string, cols: number, rows: number): void;
13
+ stop(projectId: string): void;
14
+ hasTerminal(projectId: string): boolean;
15
+ /** Return the accumulated raw scrollback for a project (for replay on reconnect). */
16
+ getScrollback(projectId: string): string;
17
+ /** Return epoch ms of last PTY data, or null if no terminal / never had data. */
18
+ getLastActivityAt(projectId: string): number | null;
19
+ /** Return activity map for all running terminals: projectId → lastActivityAt ms. */
20
+ getAllActivity(): Record<string, number>;
21
+ resumeAll(): void;
22
+ private startTerminal;
23
+ private handleExit;
24
+ }
25
+ export declare const terminalManager: TerminalManager;
26
+ export {};
27
+ //# sourceMappingURL=terminal-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-manager.d.ts","sourceRoot":"","sources":["../src/terminal-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAW,MAAM,SAAS,CAAC;AAkB3C,KAAK,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAgB7C,cAAM,eAAe;IACnB,OAAO,CAAC,SAAS,CAAuC;IACxD,qGAAqG;IACrG,OAAO,CAAC,aAAa,CAAoD;IAEzE,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,GAAE,cAAyB,GAAG,IAAI;IAS5E,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,GAAG,IAAI;IAKtE,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI/C,oDAAoD;IACpD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAU3D,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAkB7B,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIvC,qFAAqF;IACrF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIxC,iFAAiF;IACjF,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKnD,oFAAoF;IACpF,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAUxC,SAAS,IAAI,IAAI;IASjB,OAAO,CAAC,aAAa;IAuDrB,OAAO,CAAC,UAAU;CAwBnB;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}