@tom2012/cc-web 2026.4.22-c → 2026.4.23-b

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 (39) hide show
  1. package/README.md +1 -1
  2. package/backend/dist/adapters/claude/scheduled.d.ts +34 -0
  3. package/backend/dist/adapters/claude/scheduled.d.ts.map +1 -0
  4. package/backend/dist/adapters/claude/scheduled.js +161 -0
  5. package/backend/dist/adapters/claude/scheduled.js.map +1 -0
  6. package/backend/dist/chat-backup.d.ts +55 -0
  7. package/backend/dist/chat-backup.d.ts.map +1 -0
  8. package/backend/dist/chat-backup.js +310 -0
  9. package/backend/dist/chat-backup.js.map +1 -0
  10. package/backend/dist/index.d.ts.map +1 -1
  11. package/backend/dist/index.js +2 -0
  12. package/backend/dist/index.js.map +1 -1
  13. package/backend/dist/routes/claude.d.ts.map +1 -1
  14. package/backend/dist/routes/claude.js +36 -0
  15. package/backend/dist/routes/claude.js.map +1 -1
  16. package/backend/dist/routes/projects.d.ts.map +1 -1
  17. package/backend/dist/routes/projects.js +48 -0
  18. package/backend/dist/routes/projects.js.map +1 -1
  19. package/backend/package-lock.json +22 -0
  20. package/backend/package.json +1 -0
  21. package/frontend/dist/assets/{AssistantMessageContent-D9UiFFE2.js → AssistantMessageContent-DApD33qP.js} +2 -2
  22. package/frontend/dist/assets/{GraphPreview-MfS2qFbu.js → GraphPreview-Bonr817I.js} +2 -2
  23. package/frontend/dist/assets/{MobilePage-C32pb6m8.js → MobilePage-58JbHNVy.js} +4 -4
  24. package/frontend/dist/assets/{OfficePreview-BTOmTUcw.js → OfficePreview-DjloQ0BK.js} +2 -2
  25. package/frontend/dist/assets/{ProjectPage-Dhxpa8db.js → ProjectPage-Bpv6w4Zv.js} +5 -5
  26. package/frontend/dist/assets/{SettingsPage-ChzjDTNX.js → SettingsPage-BwBjcLWF.js} +2 -2
  27. package/frontend/dist/assets/{SkillHubPage-DKtZOrUk.js → SkillHubPage-CCpcyy29.js} +3 -3
  28. package/frontend/dist/assets/{chevron-down-CDoIuDnn.js → chevron-down-53DGQjpa.js} +1 -1
  29. package/frontend/dist/assets/{chevron-up-jGWs6GMh.js → chevron-up-IjgQFBZH.js} +1 -1
  30. package/frontend/dist/assets/{index-CPwZI53Z.js → index-5s_PZCHD.js} +9 -9
  31. package/frontend/dist/assets/{index-D7HRanYr.js → index-B2CIF5t5.js} +1 -1
  32. package/frontend/dist/assets/index-C331kTug.css +1 -0
  33. package/frontend/dist/assets/index-CgCyENi7.js +1 -0
  34. package/frontend/dist/assets/{jszip.min-BRHGpQBe.js → jszip.min-5nsNwR7-.js} +1 -1
  35. package/frontend/dist/assets/{search-DM1GyKez.js → search-DFzN56u0.js} +1 -1
  36. package/frontend/dist/index.html +2 -2
  37. package/package.json +1 -1
  38. package/frontend/dist/assets/index-DqgIuQ6k.js +0 -1
  39. package/frontend/dist/assets/index-t3Zp9UzI.css +0 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A self-hosted web application (distributed as npm package) that provides a browser-based interface for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI sessions. Create projects, each with a persistent terminal running Claude Code, and interact with them through a real-time terminal UI.
4
4
 
5
- **Current version**: v2026.4.22-c | [GitHub](https://github.com/zbc0315/cc-web) | MIT License
5
+ **Current version**: v2026.4.23-b | [GitHub](https://github.com/zbc0315/cc-web) | MIT License
6
6
 
7
7
  ## Features
8
8
 
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Claude Code CLI scheduled-tasks reader.
3
+ *
4
+ * Reads `~/.claude/scheduled_tasks.json` (durable /loop tasks Claude wrote
5
+ * via CronCreate / ScheduleWakeup with `durable: true`) and filters to the
6
+ * tasks whose originating session's cwd matches a given project folderPath.
7
+ *
8
+ * Claude CLI path / schema reverse-engineered from v2.1.117 Mach-O binary
9
+ * (see research/scheduled-wakeup-panel-plan.md). Session-only tasks are
10
+ * invisible here — they live in CLI process memory, never hit disk.
11
+ */
12
+ export interface RawScheduledTask {
13
+ id: string;
14
+ cron: string;
15
+ prompt: string;
16
+ createdAt: number;
17
+ recurring?: boolean;
18
+ agentId?: string;
19
+ createdBySessionId?: string;
20
+ createdByPid?: number;
21
+ lastFiredAt?: number;
22
+ }
23
+ export interface ScheduledTask extends RawScheduledTask {
24
+ nextFireAt: string | null;
25
+ }
26
+ export declare function loadScheduledTasks(): RawScheduledTask[];
27
+ /**
28
+ * Return scheduled tasks whose originating session's cwd === folderPath.
29
+ * Tasks without `createdBySessionId` (agent/headless-created, rare) are
30
+ * excluded — we can't attribute them. Tasks whose session has exited
31
+ * (stale sessions file cleanup) are also excluded, by design.
32
+ */
33
+ export declare function tasksForProject(folderPath: string): ScheduledTask[];
34
+ //# sourceMappingURL=scheduled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../../../src/adapters/claude/scheduled.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,IAAI,gBAAgB,EAAE,CAmBvD;AA8DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CAgBnE"}
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Code CLI scheduled-tasks reader.
4
+ *
5
+ * Reads `~/.claude/scheduled_tasks.json` (durable /loop tasks Claude wrote
6
+ * via CronCreate / ScheduleWakeup with `durable: true`) and filters to the
7
+ * tasks whose originating session's cwd matches a given project folderPath.
8
+ *
9
+ * Claude CLI path / schema reverse-engineered from v2.1.117 Mach-O binary
10
+ * (see research/scheduled-wakeup-panel-plan.md). Session-only tasks are
11
+ * invisible here — they live in CLI process memory, never hit disk.
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.loadScheduledTasks = loadScheduledTasks;
48
+ exports.tasksForProject = tasksForProject;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const os = __importStar(require("os"));
52
+ const cron_parser_1 = require("cron-parser");
53
+ const logger_1 = require("../../logger");
54
+ const log = (0, logger_1.modLogger)('scheduled');
55
+ const SCHEDULED_FILE = path.join(os.homedir(), '.claude', 'scheduled_tasks.json');
56
+ const SESSIONS_DIR = path.join(os.homedir(), '.claude', 'sessions');
57
+ function loadScheduledTasks() {
58
+ try {
59
+ if (!fs.existsSync(SCHEDULED_FILE))
60
+ return [];
61
+ const raw = fs.readFileSync(SCHEDULED_FILE, 'utf-8');
62
+ const parsed = JSON.parse(raw);
63
+ if (!Array.isArray(parsed)) {
64
+ log.warn({ file: SCHEDULED_FILE }, 'scheduled_tasks.json is not an array');
65
+ return [];
66
+ }
67
+ return parsed.filter((t) => !!t && typeof t === 'object' && typeof t.id === 'string' &&
68
+ typeof t.cron === 'string' &&
69
+ typeof t.prompt === 'string');
70
+ }
71
+ catch (err) {
72
+ log.warn({ err, file: SCHEDULED_FILE }, 'failed to read scheduled_tasks.json');
73
+ return [];
74
+ }
75
+ }
76
+ /**
77
+ * Canonicalize an on-disk path for equality comparison: resolve symlinks +
78
+ * normalize. Falls back to `path.resolve` if realpath fails (e.g., the path
79
+ * no longer exists because the session's cwd was deleted). Without this
80
+ * `Project.folderPath` (stored raw at create-time) and the CLI's captured
81
+ * `cwd` can differ by trailing slash / symlink even when semantically equal.
82
+ */
83
+ function canonicalizePath(p) {
84
+ try {
85
+ return fs.realpathSync(p);
86
+ }
87
+ catch {
88
+ return path.resolve(p);
89
+ }
90
+ }
91
+ /**
92
+ * Build a sessionId → canonical-cwd index by scanning `~/.claude/sessions/*.json`.
93
+ * Files are named by PID, so we have to open each one. Cheap in practice
94
+ * (O(few dozen), small JSON), and this is called at most once per request.
95
+ */
96
+ function buildSessionCwdIndex() {
97
+ const index = new Map();
98
+ try {
99
+ if (!fs.existsSync(SESSIONS_DIR))
100
+ return index;
101
+ const files = fs.readdirSync(SESSIONS_DIR);
102
+ for (const f of files) {
103
+ if (!f.endsWith('.json'))
104
+ continue;
105
+ try {
106
+ const raw = fs.readFileSync(path.join(SESSIONS_DIR, f), 'utf-8');
107
+ const meta = JSON.parse(raw);
108
+ if (meta.sessionId && meta.cwd)
109
+ index.set(meta.sessionId, canonicalizePath(meta.cwd));
110
+ }
111
+ catch {
112
+ // per-file parse failure is non-fatal; skip
113
+ }
114
+ }
115
+ }
116
+ catch (err) {
117
+ log.warn({ err, dir: SESSIONS_DIR }, 'failed to scan sessions dir');
118
+ }
119
+ return index;
120
+ }
121
+ function computeNextFire(cron, fromHint) {
122
+ // We want the next FUTURE fire. If the hint (lastFiredAt or createdAt) is
123
+ // in the past (because the creator process died or the file is stale),
124
+ // starting cron-parser from it would return an already-past date. Clamp
125
+ // to now so we always show a forward-looking timestamp.
126
+ const start = Math.max(fromHint, Date.now());
127
+ try {
128
+ const it = cron_parser_1.CronExpressionParser.parse(cron, { currentDate: new Date(start) });
129
+ return it.next().toDate().toISOString();
130
+ }
131
+ catch {
132
+ return null;
133
+ }
134
+ }
135
+ /**
136
+ * Return scheduled tasks whose originating session's cwd === folderPath.
137
+ * Tasks without `createdBySessionId` (agent/headless-created, rare) are
138
+ * excluded — we can't attribute them. Tasks whose session has exited
139
+ * (stale sessions file cleanup) are also excluded, by design.
140
+ */
141
+ function tasksForProject(folderPath) {
142
+ const all = loadScheduledTasks();
143
+ if (all.length === 0)
144
+ return [];
145
+ const idx = buildSessionCwdIndex();
146
+ const targetCwd = canonicalizePath(folderPath);
147
+ const out = [];
148
+ for (const t of all) {
149
+ if (!t.createdBySessionId)
150
+ continue;
151
+ const cwd = idx.get(t.createdBySessionId);
152
+ if (cwd !== targetCwd)
153
+ continue;
154
+ out.push({
155
+ ...t,
156
+ nextFireAt: computeNextFire(t.cron, t.lastFiredAt ?? t.createdAt),
157
+ });
158
+ }
159
+ return out;
160
+ }
161
+ //# sourceMappingURL=scheduled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../../../src/adapters/claude/scheduled.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Chat-history backup: mirror CLI-native session files (e.g. `~/.claude/
3
+ * projects/<enc>/*.jsonl`, `~/.codex/sessions/YYYY/MM/DD/*.jsonl`, Gemini's
4
+ * `*.json`, etc.) into `<projectFolder>/.ccweb/sessions/<cliTool>/` so that
5
+ * whole-project rsync / tar of the project folder captures the chat log
6
+ * alongside source code. READ PATH IS UNCHANGED — the daemon still reads
7
+ * chat-history from the CLI-native location (see session-manager.ts). This
8
+ * module is write-only and out-of-band with the hot path.
9
+ *
10
+ * Mirror semantics: a source file deleted upstream is deleted downstream on
11
+ * the next pass. Only touches files **inside** the per-tool subdir
12
+ * (`.ccweb/sessions/<cliTool>/`); other files under `.ccweb/` (project.json,
13
+ * shortcuts.json, other tools' stale subdirs) are never enumerated.
14
+ */
15
+ import type { Project, CliTool } from './types';
16
+ export interface BackupMeta {
17
+ lastBackupAt: number;
18
+ lastError?: string;
19
+ }
20
+ export interface BackupFileInfo {
21
+ name: string;
22
+ mtime: number;
23
+ bytes: number;
24
+ }
25
+ export interface BackupStatus {
26
+ supported: boolean;
27
+ cliTool: CliTool;
28
+ backupDir: string;
29
+ files: BackupFileInfo[];
30
+ meta: BackupMeta | null;
31
+ }
32
+ export interface BackupResult {
33
+ projectId: string;
34
+ copied: number;
35
+ deleted: number;
36
+ skipped: number;
37
+ errors: string[];
38
+ }
39
+ /**
40
+ * Mirror the CLI-native session files of `project` into
41
+ * `<folder>/.ccweb/sessions/<cliTool>/`. Idempotent: files whose size+mtime
42
+ * match the source are skipped; downstream files with no matching source are
43
+ * deleted.
44
+ */
45
+ export declare function backupProjectSessions(project: Project): BackupResult;
46
+ /**
47
+ * Status for the UI panel: list the files currently present in the backup
48
+ * dir + last backup timestamp. Zero I/O into source dir.
49
+ */
50
+ export declare function getBackupStatus(project: Project): BackupStatus;
51
+ /** Run backup across every project. Non-throwing: per-project failures are logged. */
52
+ export declare function backupAllProjects(): BackupResult[];
53
+ export declare function startBackupScheduler(): void;
54
+ export declare function stopBackupScheduler(): void;
55
+ //# sourceMappingURL=chat-backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-backup.d.ts","sourceRoot":"","sources":["../src/chat-backup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAShD,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AA8ED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAoFpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAwB9D;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAWlD;AAID,wBAAgB,oBAAoB,IAAI,IAAI,CAW3C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C"}
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ /**
3
+ * Chat-history backup: mirror CLI-native session files (e.g. `~/.claude/
4
+ * projects/<enc>/*.jsonl`, `~/.codex/sessions/YYYY/MM/DD/*.jsonl`, Gemini's
5
+ * `*.json`, etc.) into `<projectFolder>/.ccweb/sessions/<cliTool>/` so that
6
+ * whole-project rsync / tar of the project folder captures the chat log
7
+ * alongside source code. READ PATH IS UNCHANGED — the daemon still reads
8
+ * chat-history from the CLI-native location (see session-manager.ts). This
9
+ * module is write-only and out-of-band with the hot path.
10
+ *
11
+ * Mirror semantics: a source file deleted upstream is deleted downstream on
12
+ * the next pass. Only touches files **inside** the per-tool subdir
13
+ * (`.ccweb/sessions/<cliTool>/`); other files under `.ccweb/` (project.json,
14
+ * shortcuts.json, other tools' stale subdirs) are never enumerated.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.backupProjectSessions = backupProjectSessions;
51
+ exports.getBackupStatus = getBackupStatus;
52
+ exports.backupAllProjects = backupAllProjects;
53
+ exports.startBackupScheduler = startBackupScheduler;
54
+ exports.stopBackupScheduler = stopBackupScheduler;
55
+ const fs = __importStar(require("fs"));
56
+ const path = __importStar(require("path"));
57
+ const crypto = __importStar(require("crypto"));
58
+ const adapters_1 = require("./adapters");
59
+ const config_1 = require("./config");
60
+ const logger_1 = require("./logger");
61
+ const log = (0, logger_1.modLogger)('backup');
62
+ const BACKUP_SUBDIR = 'sessions';
63
+ const META_FILE = 'backup-meta.json';
64
+ const POLL_INTERVAL_MS = 5 * 60000;
65
+ function backupRoot(project) {
66
+ return path.join((0, config_1.ccwebDir)(project.folderPath), BACKUP_SUBDIR);
67
+ }
68
+ function perToolDir(project) {
69
+ return path.join(backupRoot(project), project.cliTool);
70
+ }
71
+ function metaPath(project) {
72
+ return path.join(backupRoot(project), META_FILE);
73
+ }
74
+ function readMeta(project) {
75
+ try {
76
+ const raw = fs.readFileSync(metaPath(project), 'utf-8');
77
+ const parsed = JSON.parse(raw);
78
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
79
+ return null;
80
+ const m = parsed;
81
+ if (typeof m.lastBackupAt !== 'number')
82
+ return null;
83
+ return m;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ function writeMeta(project, meta) {
90
+ try {
91
+ fs.mkdirSync(backupRoot(project), { recursive: true });
92
+ const tmp = `${metaPath(project)}.tmp-${process.pid}-${crypto.randomBytes(3).toString('hex')}`;
93
+ fs.writeFileSync(tmp, JSON.stringify(meta, null, 2));
94
+ fs.renameSync(tmp, metaPath(project));
95
+ }
96
+ catch (err) {
97
+ log.warn({ err, project: project.id }, 'failed to write backup meta');
98
+ }
99
+ }
100
+ function atomicCopy(src, dst) {
101
+ const dstDir = path.dirname(dst);
102
+ fs.mkdirSync(dstDir, { recursive: true });
103
+ const tmp = `${dst}.tmp-${process.pid}-${crypto.randomBytes(3).toString('hex')}`;
104
+ fs.copyFileSync(src, tmp);
105
+ fs.renameSync(tmp, dst);
106
+ // Preserve source mtime so future mtime comparisons work correctly.
107
+ try {
108
+ const st = fs.statSync(src);
109
+ fs.utimesSync(dst, st.atime, st.mtime);
110
+ }
111
+ catch {
112
+ // non-fatal
113
+ }
114
+ }
115
+ function listSourceFiles(project) {
116
+ const adapter = (0, adapters_1.getAdapter)(project.cliTool);
117
+ if (typeof adapter.getSessionFilesForProject === 'function') {
118
+ try {
119
+ return adapter.getSessionFilesForProject(project.folderPath);
120
+ }
121
+ catch (err) {
122
+ log.warn({ err, project: project.id }, 'adapter.getSessionFilesForProject threw');
123
+ return [];
124
+ }
125
+ }
126
+ const dir = adapter.getSessionDir(project.folderPath);
127
+ if (!dir || !fs.existsSync(dir))
128
+ return [];
129
+ const ext = typeof adapter.getSessionFileExtension === 'function'
130
+ ? adapter.getSessionFileExtension()
131
+ : '.jsonl';
132
+ try {
133
+ return fs.readdirSync(dir)
134
+ .filter((f) => f.endsWith(ext))
135
+ .map((f) => path.join(dir, f));
136
+ }
137
+ catch (err) {
138
+ log.warn({ err, dir, project: project.id }, 'readdir session dir failed');
139
+ return [];
140
+ }
141
+ }
142
+ /**
143
+ * Mirror the CLI-native session files of `project` into
144
+ * `<folder>/.ccweb/sessions/<cliTool>/`. Idempotent: files whose size+mtime
145
+ * match the source are skipped; downstream files with no matching source are
146
+ * deleted.
147
+ */
148
+ function backupProjectSessions(project) {
149
+ const result = { projectId: project.id, copied: 0, deleted: 0, skipped: 0, errors: [] };
150
+ // `terminal` cliTool has no chat; nothing to back up.
151
+ if (project.cliTool === 'terminal')
152
+ return result;
153
+ const sources = listSourceFiles(project);
154
+ const dstDir = perToolDir(project);
155
+ // Ensure parent exists even if we'll have zero files (so UI shows empty state
156
+ // consistently and future writes don't race mkdir).
157
+ try {
158
+ fs.mkdirSync(dstDir, { recursive: true });
159
+ }
160
+ catch (err) {
161
+ result.errors.push(`mkdir ${dstDir}: ${err.message}`);
162
+ log.warn({ err, project: project.id, dstDir }, 'mkdir backup dir failed — skipping');
163
+ return result;
164
+ }
165
+ // Build src basename → {srcPath, mtime, size} map. Basename collisions across
166
+ // source dirs are theoretical; Claude/Codex/Gemini all use UUID-ish names.
167
+ const srcMap = new Map();
168
+ for (const src of sources) {
169
+ try {
170
+ const st = fs.statSync(src);
171
+ if (!st.isFile())
172
+ continue;
173
+ srcMap.set(path.basename(src), { srcPath: src, mtime: st.mtimeMs, size: st.size });
174
+ }
175
+ catch {
176
+ // source vanished mid-scan
177
+ }
178
+ }
179
+ // Enumerate downstream
180
+ let dstEntries = [];
181
+ try {
182
+ dstEntries = fs.readdirSync(dstDir);
183
+ }
184
+ catch {
185
+ dstEntries = [];
186
+ }
187
+ // Delete stale downstream files (in source → not in source = delete).
188
+ for (const entry of dstEntries) {
189
+ // Ignore subdirs (shouldn't exist but paranoid) and our own tmp/meta artifacts.
190
+ if (entry.includes('.tmp-'))
191
+ continue;
192
+ const dstPath = path.join(dstDir, entry);
193
+ try {
194
+ const st = fs.statSync(dstPath);
195
+ if (!st.isFile())
196
+ continue;
197
+ if (!srcMap.has(entry)) {
198
+ fs.unlinkSync(dstPath);
199
+ result.deleted++;
200
+ }
201
+ }
202
+ catch (err) {
203
+ result.errors.push(`stat/unlink ${entry}: ${err.message}`);
204
+ }
205
+ }
206
+ // Copy new / changed files.
207
+ for (const [name, info] of srcMap) {
208
+ const dst = path.join(dstDir, name);
209
+ try {
210
+ const dstSt = fs.existsSync(dst) ? fs.statSync(dst) : null;
211
+ if (dstSt && dstSt.size === info.size && Math.abs(dstSt.mtimeMs - info.mtime) < 1) {
212
+ result.skipped++;
213
+ continue;
214
+ }
215
+ atomicCopy(info.srcPath, dst);
216
+ result.copied++;
217
+ }
218
+ catch (err) {
219
+ result.errors.push(`copy ${name}: ${err.message}`);
220
+ log.warn({ err, name, project: project.id }, 'copy failed');
221
+ }
222
+ }
223
+ writeMeta(project, {
224
+ lastBackupAt: Date.now(),
225
+ ...(result.errors.length > 0 ? { lastError: result.errors[0] } : {}),
226
+ });
227
+ if (result.copied > 0 || result.deleted > 0) {
228
+ log.info({ project: project.id, cliTool: project.cliTool, ...result }, 'backed up project chat sessions');
229
+ }
230
+ return result;
231
+ }
232
+ /**
233
+ * Status for the UI panel: list the files currently present in the backup
234
+ * dir + last backup timestamp. Zero I/O into source dir.
235
+ */
236
+ function getBackupStatus(project) {
237
+ const supported = project.cliTool !== 'terminal';
238
+ const backupDir = perToolDir(project);
239
+ const meta = readMeta(project);
240
+ const files = [];
241
+ if (supported) {
242
+ try {
243
+ const entries = fs.readdirSync(backupDir);
244
+ for (const e of entries) {
245
+ if (e.includes('.tmp-'))
246
+ continue;
247
+ try {
248
+ const st = fs.statSync(path.join(backupDir, e));
249
+ if (!st.isFile())
250
+ continue;
251
+ files.push({ name: e, mtime: st.mtimeMs, bytes: st.size });
252
+ }
253
+ catch {
254
+ // skip
255
+ }
256
+ }
257
+ files.sort((a, b) => b.mtime - a.mtime);
258
+ }
259
+ catch {
260
+ // backup dir doesn't exist yet — empty list is correct
261
+ }
262
+ }
263
+ return { supported, cliTool: project.cliTool, backupDir, files, meta };
264
+ }
265
+ /** Run backup across every project. Non-throwing: per-project failures are logged. */
266
+ function backupAllProjects() {
267
+ const results = [];
268
+ const projects = (0, config_1.getProjects)();
269
+ for (const p of projects) {
270
+ try {
271
+ results.push(backupProjectSessions(p));
272
+ }
273
+ catch (err) {
274
+ log.error({ err, project: p.id }, 'backupProjectSessions threw unexpectedly');
275
+ }
276
+ }
277
+ return results;
278
+ }
279
+ let schedulerInterval = null;
280
+ function startBackupScheduler() {
281
+ if (schedulerInterval)
282
+ return;
283
+ log.info({ intervalMs: POLL_INTERVAL_MS }, 'starting chat-backup scheduler');
284
+ // Prime once immediately so new projects get baseline coverage without
285
+ // waiting the full interval.
286
+ setImmediate(() => {
287
+ try {
288
+ backupAllProjects();
289
+ }
290
+ catch (err) {
291
+ log.error({ err }, 'initial backupAllProjects threw');
292
+ }
293
+ });
294
+ schedulerInterval = setInterval(() => {
295
+ try {
296
+ backupAllProjects();
297
+ }
298
+ catch (err) {
299
+ log.error({ err }, 'scheduled backupAllProjects threw');
300
+ }
301
+ }, POLL_INTERVAL_MS);
302
+ }
303
+ function stopBackupScheduler() {
304
+ if (!schedulerInterval)
305
+ return;
306
+ clearInterval(schedulerInterval);
307
+ schedulerInterval = null;
308
+ log.info('stopped chat-backup scheduler');
309
+ }
310
+ //# sourceMappingURL=chat-backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-backup.js","sourceRoot":"","sources":["../src/chat-backup.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8EA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAmqBtB,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+EA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAoqBtB,eAAe,GAAG,CAAC"}
@@ -75,6 +75,7 @@ const plugins_1 = __importDefault(require("./routes/plugins"));
75
75
  const plugin_bridge_1 = __importDefault(require("./routes/plugin-bridge"));
76
76
  const sync_1 = __importDefault(require("./routes/sync"));
77
77
  const sync_scheduler_1 = require("./sync-scheduler");
78
+ const chat_backup_1 = require("./chat-backup");
78
79
  const sync_service_1 = require("./sync-service");
79
80
  const os = __importStar(require("os"));
80
81
  // Port file path: always ~/.ccweb/port (fixed path for hook shell commands)
@@ -726,6 +727,7 @@ function tryListen(port, maxAttempts = 20) {
726
727
  }
727
728
  hooksManager.install();
728
729
  (0, sync_scheduler_1.startSyncScheduler)();
730
+ (0, chat_backup_1.startBackupScheduler)();
729
731
  terminal_manager_1.terminalManager.resumeAll();
730
732
  // Wire up context broadcast to project WS clients
731
733
  (0, hooks_1.setBroadcastContextUpdate)((projectId, data) => {