@costrict/csc 4.1.10 → 4.1.11

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.
@@ -3,21 +3,57 @@ var __require = typeof import.meta.require === "function" ? import.meta.require
3
3
 
4
4
  // src/services/rawDump/state.ts
5
5
  import { promises as fs, readFileSync, writeFileSync } from "fs";
6
+ import os2 from "os";
7
+ import path2 from "path";
8
+
9
+ // src/services/rawDump/logger.ts
10
+ import { appendFileSync } from "fs";
6
11
  import os from "os";
7
12
  import path from "path";
8
- var STATE_DIR = path.join(os.homedir(), ".claude", "raw-dump");
9
- var CONVERSATION_FILE = path.join(STATE_DIR, "csc-conversation.json");
10
- var SUMMARY_FILE = path.join(STATE_DIR, "csc-summary.json");
11
- var COMMITS_FILE = path.join(STATE_DIR, "csc-commits.json");
12
- var STATISTICS_FILE = path.join(STATE_DIR, "csc-statistics.json");
13
- var TASKS_FILE = path.join(STATE_DIR, "csc-tasks.json");
14
- var DEAD_LETTER_FILE = path.join(STATE_DIR, "csc-dead-letter.jsonl");
15
- var STATE_LOCK_FILE = path.join(STATE_DIR, "csc-state.lock");
16
- var state_conv = { conversation: {}, total: 0, incomplete: 0 };
17
- var state_summary = { summary: {}, total: 0, incomplete: 0 };
18
- var state_commit = { commits: {}, total: 0, incomplete: 0 };
19
- var state_statistics = { statistics: {}, total: 0, incomplete: 0 };
20
- var state_task = { tasks: {}, total: 0, incomplete: 0 };
13
+ var LOG_FILE = path.join(os.homedir(), ".claude", "raw-dump", "csc-raw-dump.log");
14
+ function isDebugEnabled() {
15
+ const v = process.env.CSC_RAW_DUMP_DEBUG;
16
+ return v === "1" || v === "true";
17
+ }
18
+ function createLogger(prefix) {
19
+ const enabled = isDebugEnabled();
20
+ function write(level, msg, meta) {
21
+ const alwaysWrite = level === "error" || level === "warn";
22
+ if (!alwaysWrite && !enabled)
23
+ return;
24
+ const timestamp = new Date().toISOString();
25
+ const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
26
+ const line = `[${timestamp}] [${prefix}:${level}] ${msg}${metaStr}
27
+ `;
28
+ console.error(line.trimEnd());
29
+ try {
30
+ appendFileSync(LOG_FILE, line);
31
+ } catch {}
32
+ }
33
+ const log = Object.assign((level, msg, meta) => write(level, msg, meta), {
34
+ debug: (msg, meta) => write("debug", msg, meta),
35
+ info: (msg, meta) => write("info", msg, meta),
36
+ warn: (msg, meta) => write("warn", msg, meta),
37
+ error: (msg, meta) => write("error", msg, meta)
38
+ });
39
+ return log;
40
+ }
41
+
42
+ // src/services/rawDump/state.ts
43
+ var log = createLogger("state");
44
+ var STATE_DIR = path2.join(os2.homedir(), ".claude", "raw-dump");
45
+ var CONVERSATION_FILE = path2.join(STATE_DIR, "csc-conversation.json");
46
+ var SUMMARY_FILE = path2.join(STATE_DIR, "csc-summary.json");
47
+ var COMMITS_FILE = path2.join(STATE_DIR, "csc-commits.json");
48
+ var STATISTICS_FILE = path2.join(STATE_DIR, "csc-statistics.json");
49
+ var TASKS_FILE = path2.join(STATE_DIR, "csc-tasks.json");
50
+ var DEAD_LETTER_FILE = path2.join(STATE_DIR, "csc-dead-letter.jsonl");
51
+ var STATE_LOCK_FILE = path2.join(STATE_DIR, "csc-state.lock");
52
+ var state_conv = { conversation: {}, clean_time: "", total: 0, incomplete: 0 };
53
+ var state_summary = { summary: {}, clean_time: "", total: 0, incomplete: 0 };
54
+ var state_commit = { commits: {}, clean_time: "", total: 0, incomplete: 0 };
55
+ var state_statistics = { statistics: {}, clean_time: "", total: 0, incomplete: 0 };
56
+ var state_task = { tasks: {}, history_cursor: -1, total: 0, incomplete: 0 };
21
57
  function readLockInfo(lockPath) {
22
58
  try {
23
59
  const content = readFileSync(lockPath, "utf-8").trim();
@@ -91,64 +127,112 @@ async function withStateLock(fn) {
91
127
  releaseStateLock();
92
128
  }
93
129
  }
94
- function cleanupOldRecords() {
95
- const yesterday = new Date;
96
- yesterday.setDate(yesterday.getDate() - 1);
97
- yesterday.setHours(0, 0, 0, 0);
98
- const yesterdayMs = yesterday.getTime();
130
+ function cleanupOldConvs(clean_ts) {
99
131
  const cleanedConv = {};
100
132
  let incompleteConv = 0;
133
+ let cleanCount = 0;
101
134
  for (const [k, ts] of Object.entries(state_conv.conversation)) {
102
135
  if (!ts) {
103
136
  incompleteConv++;
104
137
  cleanedConv[k] = ts;
105
- } else if (new Date(ts).getTime() >= yesterdayMs) {
138
+ } else if (new Date(ts).getTime() >= clean_ts) {
106
139
  cleanedConv[k] = ts;
140
+ } else {
141
+ cleanCount++;
107
142
  }
108
143
  }
144
+ if (cleanCount > 0) {
145
+ state_conv.clean_time = new Date(clean_ts).toISOString();
146
+ log.info("cleanup conversation", { clean_count: cleanCount, clean_time: state_conv.clean_time });
147
+ }
109
148
  state_conv.conversation = cleanedConv;
110
149
  state_conv.incomplete = incompleteConv;
111
150
  state_conv.total = Object.keys(cleanedConv).length;
151
+ }
152
+ function cleanupOldSummarys(clean_ts) {
112
153
  const cleanedSum = {};
113
154
  let incompleteSum = 0;
155
+ let cleanCount = 0;
114
156
  for (const [k, ts] of Object.entries(state_summary.summary)) {
115
157
  if (!ts) {
116
158
  incompleteSum++;
117
159
  cleanedSum[k] = ts;
118
- } else if (new Date(ts).getTime() >= yesterdayMs) {
160
+ } else if (new Date(ts).getTime() >= clean_ts) {
119
161
  cleanedSum[k] = ts;
162
+ } else {
163
+ cleanCount++;
120
164
  }
121
165
  }
166
+ if (cleanCount > 0) {
167
+ state_summary.clean_time = new Date(clean_ts).toISOString();
168
+ log.info("cleanup summary", { clean_count: cleanCount, clean_time: state_summary.clean_time });
169
+ }
122
170
  state_summary.summary = cleanedSum;
123
171
  state_summary.incomplete = incompleteSum;
124
172
  state_summary.total = Object.keys(cleanedSum).length;
173
+ }
174
+ function cleanupOldTasks(clean_ts) {
125
175
  const cleanedTasks = {};
126
176
  let incompleteTasks = 0;
177
+ let history_cursor = -1;
178
+ let cleanCount = 0;
127
179
  for (const [k, r] of Object.entries(state_task.tasks)) {
128
180
  if (!r.uploadedAt) {
129
181
  incompleteTasks++;
130
182
  cleanedTasks[k] = r;
131
- } else if (new Date(r.uploadedAt).getTime() >= yesterdayMs) {
183
+ } else if (new Date(r.uploadedAt).getTime() >= clean_ts) {
132
184
  cleanedTasks[k] = r;
185
+ } else {
186
+ cleanCount++;
187
+ if (r.historyNo ?? -1 > history_cursor) {
188
+ history_cursor = r.historyNo;
189
+ }
133
190
  }
134
191
  }
192
+ if (history_cursor >= 0) {
193
+ state_task.history_cursor = history_cursor;
194
+ log.info("cleanup task", {
195
+ clean_count: cleanCount,
196
+ history_cursor,
197
+ clean_time: new Date(clean_ts).toISOString()
198
+ });
199
+ }
135
200
  state_task.tasks = cleanedTasks;
136
201
  state_task.incomplete = incompleteTasks;
137
202
  state_task.total = Object.keys(cleanedTasks).length;
203
+ }
204
+ function cleanupOldStats(clean_ts) {
138
205
  const cleanedStats = {};
139
206
  let incompleteStat = 0;
207
+ let cleanCount = 0;
140
208
  for (const [k, ts] of Object.entries(state_statistics.statistics)) {
141
209
  if (!ts.currentUploadAt) {
142
210
  cleanedStats[k] = ts;
143
211
  incompleteStat++;
144
- } else if (new Date(k).getTime() >= yesterdayMs) {
212
+ } else if (new Date(k).getTime() >= clean_ts) {
145
213
  cleanedStats[k] = ts;
214
+ } else {
215
+ cleanCount++;
146
216
  }
147
217
  }
218
+ if (cleanCount > 0) {
219
+ state_statistics.clean_time = new Date(clean_ts).toISOString();
220
+ log.info("cleanup statistics", { clean_count: cleanCount, clean_time: state_statistics.clean_time });
221
+ }
148
222
  state_statistics.statistics = cleanedStats;
149
223
  state_statistics.total = Object.keys(cleanedStats).length;
150
224
  state_statistics.incomplete = incompleteStat;
151
225
  }
226
+ function cleanupOldRecords() {
227
+ const yesterday = new Date;
228
+ yesterday.setDate(yesterday.getDate() - 1);
229
+ yesterday.setHours(0, 0, 0, 0);
230
+ const yesterdayMs = yesterday.getTime();
231
+ cleanupOldConvs(yesterdayMs);
232
+ cleanupOldSummarys(yesterdayMs);
233
+ cleanupOldTasks(yesterdayMs);
234
+ cleanupOldStats(yesterdayMs);
235
+ }
152
236
  async function loadAllState() {
153
237
  await Promise.all([
154
238
  loadConversation(),
@@ -166,11 +250,12 @@ async function loadConversation() {
166
250
  const parsed = JSON.parse(text);
167
251
  state_conv = {
168
252
  conversation: parsed.conversation ?? {},
253
+ clean_time: parsed.clean_time ?? "",
169
254
  total: Object.keys(parsed.conversation ?? {}).length,
170
255
  incomplete: Object.values(parsed.conversation ?? {}).filter((ts) => !ts).length
171
256
  };
172
257
  } catch {
173
- state_conv = { conversation: {}, total: 0, incomplete: 0 };
258
+ state_conv = { conversation: {}, clean_time: "", total: 0, incomplete: 0 };
174
259
  }
175
260
  });
176
261
  }
@@ -181,11 +266,12 @@ async function loadSummary() {
181
266
  const parsed = JSON.parse(text);
182
267
  state_summary = {
183
268
  summary: parsed.summary ?? {},
269
+ clean_time: parsed.clean_time ?? "",
184
270
  total: Object.keys(parsed.summary ?? {}).length,
185
271
  incomplete: Object.values(parsed.summary ?? {}).filter((ts) => !ts).length
186
272
  };
187
273
  } catch {
188
- state_summary = { summary: {}, total: 0, incomplete: 0 };
274
+ state_summary = { summary: {}, clean_time: "", total: 0, incomplete: 0 };
189
275
  }
190
276
  });
191
277
  }
@@ -196,11 +282,12 @@ async function loadCommits() {
196
282
  const parsed = JSON.parse(text);
197
283
  state_commit = {
198
284
  commits: parsed.commits ?? {},
285
+ clean_time: parsed.clean_time ?? "",
199
286
  total: Object.keys(parsed.commits ?? {}).length,
200
287
  incomplete: 0
201
288
  };
202
289
  } catch {
203
- state_commit = { commits: {}, total: 0, incomplete: 0 };
290
+ state_commit = { commits: {}, clean_time: "", total: 0, incomplete: 0 };
204
291
  }
205
292
  });
206
293
  }
@@ -211,11 +298,12 @@ async function loadStatistics() {
211
298
  const parsed = JSON.parse(text);
212
299
  state_statistics = {
213
300
  statistics: parsed.statistics ?? {},
301
+ clean_time: parsed.clean_time ?? "",
214
302
  total: Object.keys(parsed.statistics ?? {}).length,
215
303
  incomplete: Object.values(parsed.statistics ?? {}).filter((ts) => !ts.currentUploadAt).length
216
304
  };
217
305
  } catch {
218
- state_statistics = { statistics: {}, total: 0, incomplete: 0 };
306
+ state_statistics = { statistics: {}, clean_time: "", total: 0, incomplete: 0 };
219
307
  }
220
308
  });
221
309
  }
@@ -227,11 +315,12 @@ async function loadTasks() {
227
315
  const tasks = parsed.tasks ?? {};
228
316
  state_task = {
229
317
  tasks,
318
+ history_cursor: parsed.history_cursor ?? -1,
230
319
  total: Object.keys(tasks).length,
231
320
  incomplete: Object.values(tasks).filter((r) => !r.uploadedAt).length
232
321
  };
233
322
  } catch {
234
- state_task = { tasks: {}, total: 0, incomplete: 0 };
323
+ state_task = { tasks: {}, history_cursor: -1, total: 0, incomplete: 0 };
235
324
  }
236
325
  });
237
326
  }
@@ -261,7 +350,7 @@ async function saveCommits() {
261
350
  async function saveStatistics() {
262
351
  return withStateLock(async () => {
263
352
  await fs.mkdir(STATE_DIR, { recursive: true });
264
- state_statistics.incomplete = Object.values(state_statistics.statistics).filter((ts) => !ts).length;
353
+ state_statistics.incomplete = Object.values(state_statistics.statistics).filter((r) => !r.currentUploadAt).length;
265
354
  state_statistics.total = Object.keys(state_statistics.statistics).length;
266
355
  await fs.writeFile(STATISTICS_FILE, JSON.stringify(state_statistics, null, 2), "utf-8");
267
356
  });
@@ -297,9 +386,9 @@ function addQueueTasks(tasks) {
297
386
  }
298
387
  }
299
388
  }
300
- function addHistoryTasks(startNo, tasks) {
389
+ function addHistoryTasks(tasks) {
301
390
  const seen = new Map;
302
- for (let i = 0;i < tasks.length; i++) {
391
+ for (let i = state_task.history_cursor + 1;i < tasks.length; i++) {
303
392
  const item = tasks[i];
304
393
  const key = `${item.sessionId}`;
305
394
  const existing = seen.get(key);
@@ -308,7 +397,7 @@ function addHistoryTasks(startNo, tasks) {
308
397
  }
309
398
  }
310
399
  for (const { item, index } of seen.values()) {
311
- const historyNo = startNo + index;
400
+ const historyNo = index;
312
401
  const key = `${item.sessionId}:history-${historyNo}`;
313
402
  const existing = state_task.tasks[key];
314
403
  if (!existing) {
@@ -352,10 +441,10 @@ function getCoStrictCredentialsPath() {
352
441
  return join(COSTRICT_CONFIG_DIR, "auth.json");
353
442
  }
354
443
  function generateMachineId() {
355
- const os2 = __require("os");
356
- const platform = os2.platform();
357
- const hostname = os2.hostname();
358
- const username = os2.userInfo().username;
444
+ const os3 = __require("os");
445
+ const platform = os3.platform();
446
+ const hostname = os3.hostname();
447
+ const username = os3.userInfo().username;
359
448
  const machineInfo = `${platform}-${hostname}-${username}`;
360
449
  return createHash("sha256").update(machineInfo).digest("hex");
361
450
  }
@@ -388,8 +477,8 @@ async function saveCoStrictCredentials(credentials) {
388
477
  import { createRequire } from "module";
389
478
  function getVersion() {
390
479
  try {
391
- if (typeof MACRO !== "undefined" && "4.1.10")
392
- return "4.1.10";
480
+ if (typeof MACRO !== "undefined" && "4.1.11")
481
+ return "4.1.11";
393
482
  } catch {}
394
483
  try {
395
484
  const require2 = createRequire(import.meta.url);
@@ -586,39 +675,6 @@ function toCommitComment(subject) {
586
675
  return Array.from(subject).slice(0, 150).join("");
587
676
  }
588
677
 
589
- // src/services/rawDump/logger.ts
590
- import { appendFileSync } from "fs";
591
- import os2 from "os";
592
- import path2 from "path";
593
- var LOG_FILE = path2.join(os2.homedir(), ".claude", "raw-dump", "csc-raw-dump.log");
594
- function isDebugEnabled() {
595
- const v = process.env.CSC_RAW_DUMP_DEBUG;
596
- return v === "1" || v === "true";
597
- }
598
- function createLogger(prefix) {
599
- const enabled = isDebugEnabled();
600
- function write(level, msg, meta) {
601
- const alwaysWrite = level === "error" || level === "warn";
602
- if (!alwaysWrite && !enabled)
603
- return;
604
- const timestamp = new Date().toISOString();
605
- const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
606
- const line = `[${timestamp}] [${prefix}:${level}] ${msg}${metaStr}
607
- `;
608
- console.error(line.trimEnd());
609
- try {
610
- appendFileSync(LOG_FILE, line);
611
- } catch {}
612
- }
613
- const log = Object.assign((level, msg, meta) => write(level, msg, meta), {
614
- debug: (msg, meta) => write("debug", msg, meta),
615
- info: (msg, meta) => write("info", msg, meta),
616
- warn: (msg, meta) => write("warn", msg, meta),
617
- error: (msg, meta) => write("error", msg, meta)
618
- });
619
- return log;
620
- }
621
-
622
678
  // src/services/rawDump/localStorage.ts
623
679
  import { promises as fs3 } from "fs";
624
680
  import os3 from "os";
@@ -643,23 +699,67 @@ function getRawDumpMode() {
643
699
  return 3;
644
700
  return 1;
645
701
  }
702
+ function normalizeProjectPath(dir) {
703
+ return dir.replace(/:/g, "-").replace(/[/\\]/g, "-");
704
+ }
705
+ function getDateFromTimestamp(ts) {
706
+ const d = new Date(ts);
707
+ const year = d.getFullYear();
708
+ const month = String(d.getMonth() + 1).padStart(2, "0");
709
+ const day = String(d.getDate()).padStart(2, "0");
710
+ return `${year}/${month}/${day}`;
711
+ }
712
+ function getTimestampField(type, body) {
713
+ if (type === "commit")
714
+ return body.commit_time;
715
+ return body.start_time;
716
+ }
646
717
  async function writeLocalDump(type, body) {
647
718
  const dir = getLocalDumpDir();
648
- const taskId = body.task_id || body.commit_id || "unknown";
649
- const taskDir = path3.join(dir, type, taskId);
650
- await fs3.mkdir(taskDir, { recursive: true });
651
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
652
- const requestId = body.request_id || body.commit_id || body.task_id || "unknown";
653
- const filename = `${timestamp}-${requestId}.json`;
654
- const filePath = path3.join(taskDir, filename);
719
+ let subdir = "";
720
+ let ymd = "";
721
+ let fname = "";
722
+ let endpoint = "";
723
+ if (type == "summary") {
724
+ ymd = getDateFromTimestamp(getTimestampField("summary", body));
725
+ subdir = ymd;
726
+ fname = body.task_id;
727
+ endpoint = "/raw-store/task-summary";
728
+ } else if (type == "conversation") {
729
+ ymd = getDateFromTimestamp(getTimestampField("conversation", body));
730
+ subdir = path3.join(ymd, body.task_id);
731
+ fname = body.request_id;
732
+ endpoint = "/raw-store/task-conversation";
733
+ } else if (type == "commit") {
734
+ ymd = getDateFromTimestamp(getTimestampField("commit", body));
735
+ subdir = path3.join(normalizeProjectPath(body.repo_addr), normalizeProjectPath(body.repo_branch), ymd);
736
+ fname = body.commit_id;
737
+ endpoint = "/raw-store/commit";
738
+ } else if (type == "statistics") {
739
+ subdir = getDateFromTimestamp(getTimestampField("statistics", body));
740
+ const t = new Date(body.end_time);
741
+ const h = String(t.getHours()).padStart(2, "0");
742
+ const m = String(t.getMinutes()).padStart(2, "0");
743
+ const s = String(t.getSeconds()).padStart(2, "0");
744
+ fname = `${h}-${m}-${s}`;
745
+ endpoint = "/raw-store/statistics";
746
+ } else {
747
+ subdir = "unknown";
748
+ fname = "unknown";
749
+ endpoint = "unknown";
750
+ }
751
+ const dumpDir = path3.join(dir, type, subdir);
752
+ const filename = `${fname}.json`;
753
+ const filePath = path3.join(dumpDir, filename);
655
754
  const payload = {
656
755
  _dumpMeta: {
657
756
  type,
658
757
  dumpedAt: new Date().toISOString(),
659
- endpoint: type === "conversation" ? "/raw-store/task-conversation" : type === "summary" ? "/raw-store/task-summary" : "/raw-store/commit"
758
+ endpoint
660
759
  },
661
760
  ...body
662
761
  };
762
+ await fs3.mkdir(dumpDir, { recursive: true });
663
763
  await fs3.writeFile(filePath, JSON.stringify(payload, null, 2) + `
664
764
  `, "utf-8");
665
765
  }
@@ -698,11 +798,11 @@ function clearQueue() {
698
798
  function getQueue() {
699
799
  return [...queue];
700
800
  }
701
- async function acquireLock() {
801
+ async function acquireQueueLock() {
702
802
  try {
703
803
  try {
704
- const stat = await fs4.readFile(QUEUE_LOCK_FILE, "utf-8");
705
- const pid = parseInt(stat, 10);
804
+ const stat2 = await fs4.readFile(QUEUE_LOCK_FILE, "utf-8");
805
+ const pid = parseInt(stat2, 10);
706
806
  if (!isNaN(pid) && pid !== process.pid) {
707
807
  try {
708
808
  process.kill(pid, 0);
@@ -716,110 +816,84 @@ async function acquireLock() {
716
816
  return false;
717
817
  }
718
818
  }
719
- async function releaseLock() {
819
+ async function releaseQueueLock() {
720
820
  try {
721
821
  await fs4.writeFile(QUEUE_LOCK_FILE, "", "utf-8");
722
822
  } catch {}
723
823
  }
724
824
 
725
- // src/services/rawDump/statistics.ts
726
- function getTodayKey() {
727
- const now = new Date;
728
- const year = now.getFullYear();
729
- const month = String(now.getMonth() + 1).padStart(2, "0");
730
- const day = String(now.getDate()).padStart(2, "0");
731
- return `${year}-${month}-${day}`;
732
- }
733
- var globalStats = {
734
- dateKey: getTodayKey(),
735
- sessionCount: 0,
736
- conversationCount: 0,
737
- upstreamTokens: 0,
738
- downstreamTokens: 0,
739
- startTime: 0,
740
- endTime: 0
741
- };
742
- function resetGlobalStats() {
743
- globalStats = {
744
- dateKey: getTodayKey(),
745
- sessionCount: 0,
746
- conversationCount: 0,
747
- upstreamTokens: 0,
748
- downstreamTokens: 0,
749
- startTime: 0,
750
- endTime: 0
751
- };
752
- }
753
- function shallowEqual(obj1, obj2) {
754
- const keys1 = Object.keys(obj1);
755
- const keys2 = Object.keys(obj2);
756
- if (keys1.length !== keys2.length)
757
- return false;
758
- return keys1.every((key) => obj1[key] === obj2[key]);
759
- }
760
- function updateStatistics(dateKey) {
761
- const dateValue = state_statistics.statistics[dateKey];
762
- if (!dateValue) {
763
- state_statistics.statistics[dateKey] = {
764
- lastUploadAt: "",
765
- currentUploadAt: "",
766
- daily: Object.assign({}, globalStats)
767
- };
768
- } else {
769
- if (shallowEqual(globalStats, dateValue)) {
770
- return;
825
+ // src/services/rawDump/history.ts
826
+ import { promises as fs5 } from "fs";
827
+ import os5 from "os";
828
+ import path5 from "path";
829
+ var HISTORY_FILE = path5.join(os5.homedir(), ".claude", "history.jsonl");
830
+ var cache = null;
831
+ async function loadHistory() {
832
+ const items = [];
833
+ let fileStat = null;
834
+ try {
835
+ fileStat = await fs5.stat(HISTORY_FILE);
836
+ } catch {
837
+ cache = null;
838
+ return [];
839
+ }
840
+ try {
841
+ const content = await fs5.readFile(HISTORY_FILE, "utf-8");
842
+ const lines = content.split(`
843
+ `);
844
+ for (const line of lines) {
845
+ if (!line.trim())
846
+ continue;
847
+ try {
848
+ const item = JSON.parse(line);
849
+ items.push(item);
850
+ } catch {}
771
851
  }
772
- Object.assign(state_statistics.statistics[dateKey].daily, globalStats);
773
- state_statistics.statistics[dateKey].currentUploadAt = "";
852
+ } catch {
853
+ return cache?.items ?? [];
774
854
  }
855
+ cache = {
856
+ mtime: fileStat.mtimeMs,
857
+ size: fileStat.size,
858
+ items
859
+ };
860
+ return cache.items;
775
861
  }
776
- function checkAndResetForNewDay() {
777
- const todayKey = getTodayKey();
778
- if (globalStats.dateKey === todayKey) {
779
- return;
862
+ async function autoLoadHistory() {
863
+ let fileStat = null;
864
+ try {
865
+ fileStat = await fs5.stat(HISTORY_FILE);
866
+ } catch {
867
+ if (cache) {
868
+ return null;
869
+ }
870
+ return [];
780
871
  }
781
- updateStatistics(globalStats.dateKey);
782
- const saved = state_statistics.statistics[todayKey];
783
- if (!saved) {
784
- resetGlobalStats();
785
- } else {
786
- Object.assign(globalStats, saved.daily);
872
+ if (cache && cache.mtime === fileStat.mtimeMs && cache.size === fileStat.size) {
873
+ return cache.items;
787
874
  }
875
+ return await loadHistory();
788
876
  }
789
- function incrementSession(timestamp) {
790
- checkAndResetForNewDay();
791
- globalStats.sessionCount++;
792
- updateTimeRange(timestamp);
793
- }
794
- function incrementConversation(timestamp) {
795
- checkAndResetForNewDay();
796
- globalStats.conversationCount++;
797
- updateTimeRange(timestamp);
798
- }
799
- function addTokens(upstream, downstream, timestamp) {
800
- checkAndResetForNewDay();
801
- globalStats.upstreamTokens += upstream;
802
- globalStats.downstreamTokens += downstream;
803
- updateTimeRange(timestamp);
804
- }
805
- function updateTimeRange(timestamp) {
806
- if (globalStats.startTime === 0 || timestamp < globalStats.startTime) {
807
- globalStats.startTime = timestamp;
808
- }
809
- if (globalStats.endTime === 0 || timestamp > globalStats.endTime) {
810
- globalStats.endTime = timestamp;
877
+ async function getProjectDirs() {
878
+ if (cache) {
879
+ let fileStat = null;
880
+ try {
881
+ fileStat = await fs5.stat(HISTORY_FILE);
882
+ } catch {
883
+ return [...new Set(cache.items.map((item) => item.project))];
884
+ }
885
+ if (fileStat.mtimeMs === cache.mtime && fileStat.size === cache.size) {
886
+ return [...new Set(cache.items.map((item) => item.project))];
887
+ }
811
888
  }
812
- }
813
- function updateStatisticsForUpload() {
814
- checkAndResetForNewDay();
815
- const todayKey = getTodayKey();
816
- updateStatistics(todayKey);
889
+ const items = await loadHistory();
890
+ return [...new Set(items.map((item) => item.project))];
817
891
  }
818
892
 
819
893
  // src/services/rawDump/session.ts
820
- import { promises as fs5 } from "fs";
821
- import os5 from "os";
822
- import path5 from "path";
894
+ import { promises as fs6 } from "fs";
895
+ import os6 from "os";
896
+ import path6 from "path";
823
897
 
824
898
  // src/services/rawDump/parse.ts
825
899
  function buildFlows(events) {
@@ -1179,22 +1253,22 @@ function parseSession(cacheConversations, events) {
1179
1253
  }
1180
1254
 
1181
1255
  // src/services/rawDump/session.ts
1182
- var log = createLogger("raw-dump-session");
1256
+ var log2 = createLogger("session");
1183
1257
  function getClaudeConfigHomeDir() {
1184
- return process.env.CLAUDE_CONFIG_HOME || path5.join(os5.homedir(), ".claude");
1258
+ return process.env.CLAUDE_CONFIG_HOME || path6.join(os6.homedir(), ".claude");
1185
1259
  }
1186
- function normalizeProjectPath(dir) {
1260
+ function normalizeProjectPath2(dir) {
1187
1261
  return dir.replace(/:/g, "-").replace(/[/\\]/g, "-");
1188
1262
  }
1189
1263
  function getSessionDirectory(directory) {
1190
1264
  const claudeHome = getClaudeConfigHomeDir();
1191
- const projectPath = normalizeProjectPath(directory);
1265
+ const projectPath = normalizeProjectPath2(directory);
1192
1266
  const candidates = [
1193
- path5.join(claudeHome, "projects", projectPath),
1194
- path5.join(claudeHome, "transcripts"),
1195
- path5.join(claudeHome, "sessions"),
1196
- path5.join(directory, ".claude", "sessions"),
1197
- path5.join(directory, ".claude"),
1267
+ path6.join(claudeHome, "projects", projectPath),
1268
+ path6.join(claudeHome, "transcripts"),
1269
+ path6.join(claudeHome, "sessions"),
1270
+ path6.join(directory, ".claude", "sessions"),
1271
+ path6.join(directory, ".claude"),
1198
1272
  directory,
1199
1273
  process.env.CSC_SESSION_DIR || ""
1200
1274
  ];
@@ -1202,7 +1276,7 @@ function getSessionDirectory(directory) {
1202
1276
  }
1203
1277
  async function loadSessionMessages(sessionFile) {
1204
1278
  try {
1205
- const text = await fs5.readFile(sessionFile, "utf-8");
1279
+ const text = await fs6.readFile(sessionFile, "utf-8");
1206
1280
  const messages = text.split(`
1207
1281
  `).filter(Boolean).map((line) => {
1208
1282
  try {
@@ -1211,7 +1285,7 @@ async function loadSessionMessages(sessionFile) {
1211
1285
  return null;
1212
1286
  }
1213
1287
  }).filter((m) => m !== null);
1214
- log.debug("loaded messages from file", {
1288
+ log2.debug("loaded messages from file", {
1215
1289
  sessionFile,
1216
1290
  count: messages.length
1217
1291
  });
@@ -1234,10 +1308,10 @@ async function getParsedSession(taskDir, sessionId) {
1234
1308
  const sessionDir = getSessionDirectory(taskDir);
1235
1309
  const cacheKey = `${sessionDir}:${sessionId}`;
1236
1310
  const cached = sessionMessagesCache.get(cacheKey);
1237
- const sessionFile = path5.join(sessionDir, `${sessionId}.jsonl`);
1311
+ const sessionFile = path6.join(sessionDir, `${sessionId}.jsonl`);
1238
1312
  let stats;
1239
1313
  try {
1240
- stats = await fs5.stat(sessionFile);
1314
+ stats = await fs6.stat(sessionFile);
1241
1315
  } catch {
1242
1316
  return {
1243
1317
  sessionJsonlFileName: sessionFile,
@@ -1250,7 +1324,7 @@ async function getParsedSession(taskDir, sessionId) {
1250
1324
  }
1251
1325
  const needsReanalysis = !cached || cached.fileTimestamp !== stats.mtimeMs || cached.fileSize !== stats.size || !cached.parsedSession;
1252
1326
  if (!needsReanalysis && cached) {
1253
- log.debug("using cached parsed session", { sessionId });
1327
+ log2.debug("using cached parsed session", { sessionId });
1254
1328
  return cached;
1255
1329
  }
1256
1330
  cleanupOldSessions();
@@ -1258,9 +1332,9 @@ async function getParsedSession(taskDir, sessionId) {
1258
1332
  const messages = cached?.messages ?? await loadSessionMessages(sessionFile);
1259
1333
  const elapsed = Date.now() - start;
1260
1334
  if (elapsed > 100) {
1261
- log.info("loadSessionMessages slow", { sessionId, elapsedMs: elapsed });
1335
+ log2.info("loadSessionMessages slow", { sessionId, elapsedMs: elapsed });
1262
1336
  }
1263
- log.debug("reparsing session", { sessionId });
1337
+ log2.debug("reparsing session", { sessionId });
1264
1338
  const cachedConvs = cached?.parsedSession?.conversations ?? [];
1265
1339
  const parsedSession = parseSession(cachedConvs, messages);
1266
1340
  sessionMessagesCache.set(cacheKey, {
@@ -1275,76 +1349,132 @@ async function getParsedSession(taskDir, sessionId) {
1275
1349
  return cacheEntry;
1276
1350
  }
1277
1351
 
1278
- // src/services/rawDump/history.ts
1279
- import { promises as fs6 } from "fs";
1280
- import os6 from "os";
1281
- import path6 from "path";
1282
- var HISTORY_FILE = path6.join(os6.homedir(), ".claude", "history.jsonl");
1283
- var cache = null;
1284
- async function loadHistory() {
1285
- const items = [];
1286
- let fileStat = null;
1287
- try {
1288
- fileStat = await fs6.stat(HISTORY_FILE);
1289
- } catch {
1290
- cache = null;
1291
- return [];
1292
- }
1293
- try {
1294
- const content = await fs6.readFile(HISTORY_FILE, "utf-8");
1295
- const lines = content.split(`
1296
- `);
1297
- for (const line of lines) {
1298
- if (!line.trim())
1299
- continue;
1300
- try {
1301
- const item = JSON.parse(line);
1302
- items.push(item);
1303
- } catch {}
1352
+ // src/services/rawDump/statistics.ts
1353
+ var log3 = createLogger("worker");
1354
+ async function updateStatisticsForUpload() {
1355
+ const dailyStats = await statHistorySessions();
1356
+ for (const [_dateKey, stats] of dailyStats) {
1357
+ const existing = state_statistics.statistics[stats.dateKey];
1358
+ if (existing) {
1359
+ const isSame = existing.daily.sessionCount === stats.sessionCount && existing.daily.conversationCount === stats.conversationCount && existing.daily.upstreamTokens === stats.upstreamTokens && existing.daily.downstreamTokens === stats.downstreamTokens && existing.daily.startTime === stats.startTime && existing.daily.endTime === stats.endTime;
1360
+ if (!isSame) {
1361
+ Object.assign(state_statistics.statistics[stats.dateKey].daily, stats);
1362
+ state_statistics.statistics[stats.dateKey].currentUploadAt = "";
1363
+ }
1364
+ } else {
1365
+ const startTime = new Date(_dateKey);
1366
+ if (state_statistics.clean_time && startTime < new Date(state_statistics.clean_time)) {
1367
+ log3.info("statistics skipped: too old", {
1368
+ startTime: startTime.toISOString(),
1369
+ clean_time: state_statistics.clean_time
1370
+ });
1371
+ } else {
1372
+ state_statistics.statistics[stats.dateKey] = {
1373
+ lastUploadAt: "",
1374
+ currentUploadAt: "",
1375
+ daily: { ...stats }
1376
+ };
1377
+ }
1304
1378
  }
1305
- } catch {
1306
- return cache?.items ?? [];
1307
1379
  }
1308
- cache = {
1309
- mtime: fileStat.mtimeMs,
1310
- size: fileStat.size,
1311
- items,
1312
- lastLoadTime: Date.now(),
1313
- lastProcessedIndex: -1
1314
- };
1315
- return cache.items;
1380
+ await saveStatistics();
1316
1381
  }
1317
- async function autoLoadHistory() {
1318
- let fileStat = null;
1319
- try {
1320
- fileStat = await fs6.stat(HISTORY_FILE);
1321
- } catch {
1322
- if (cache) {
1323
- return null;
1382
+ async function statHistorySessions() {
1383
+ const items = await autoLoadHistory();
1384
+ const dailyStats = new Map;
1385
+ const seen = new Set;
1386
+ const uniqueItems = (items ?? []).filter((item) => {
1387
+ if (seen.has(item.sessionId))
1388
+ return false;
1389
+ seen.add(item.sessionId);
1390
+ return true;
1391
+ });
1392
+ const sessionDateMap = new Map;
1393
+ for (const item of uniqueItems) {
1394
+ const cacheEntry = await getParsedSession(item.project, item.sessionId);
1395
+ const messages = cacheEntry.messages;
1396
+ if (!messages || messages.length === 0)
1397
+ continue;
1398
+ let sessionTimestamp = 0;
1399
+ let sessionUpstreamTokens = 0;
1400
+ let sessionDownstreamTokens = 0;
1401
+ for (const msg of messages) {
1402
+ if (msg.timestamp) {
1403
+ const ts = new Date(msg.timestamp).getTime();
1404
+ if (sessionTimestamp === 0 || ts < sessionTimestamp) {
1405
+ sessionTimestamp = ts;
1406
+ }
1407
+ }
1408
+ const usage = msg.message?.usage;
1409
+ if (usage) {
1410
+ sessionUpstreamTokens += (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
1411
+ sessionDownstreamTokens += usage.output_tokens ?? 0;
1412
+ }
1413
+ }
1414
+ const sessionDateKey = sessionTimestamp ? new Date(sessionTimestamp).toISOString().slice(0, 10) : new Date().toISOString().slice(0, 10);
1415
+ sessionDateMap.set(item.sessionId, sessionDateKey);
1416
+ let conversationCount = 0;
1417
+ let upstreamTokens = 0;
1418
+ let downstreamTokens = 0;
1419
+ let startTime = 0;
1420
+ let endTime = 0;
1421
+ for (const msg of messages) {
1422
+ if (msg.type === "user") {
1423
+ conversationCount++;
1424
+ }
1425
+ const usage = msg.message?.usage;
1426
+ if (usage) {
1427
+ upstreamTokens += (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
1428
+ downstreamTokens += usage.output_tokens ?? 0;
1429
+ }
1430
+ if (msg.timestamp) {
1431
+ const ts = new Date(msg.timestamp).getTime();
1432
+ if (startTime === 0 || ts < startTime) {
1433
+ startTime = ts;
1434
+ }
1435
+ if (endTime === 0 || ts > endTime) {
1436
+ endTime = ts;
1437
+ }
1438
+ }
1439
+ }
1440
+ const dateKey = sessionDateKey;
1441
+ if (!dailyStats.has(dateKey)) {
1442
+ dailyStats.set(dateKey, {
1443
+ dateKey,
1444
+ sessionCount: 0,
1445
+ conversationCount: 0,
1446
+ upstreamTokens: 0,
1447
+ downstreamTokens: 0,
1448
+ startTime: 0,
1449
+ endTime: 0
1450
+ });
1451
+ }
1452
+ const stats = dailyStats.get(dateKey);
1453
+ stats.conversationCount += conversationCount;
1454
+ stats.upstreamTokens += upstreamTokens;
1455
+ stats.downstreamTokens += downstreamTokens;
1456
+ if (startTime > 0) {
1457
+ if (stats.startTime === 0 || startTime < stats.startTime) {
1458
+ stats.startTime = startTime;
1459
+ }
1460
+ }
1461
+ if (endTime > 0) {
1462
+ if (stats.endTime === 0 || endTime > stats.endTime) {
1463
+ stats.endTime = endTime;
1464
+ }
1324
1465
  }
1325
- return [];
1326
- }
1327
- if (cache && cache.mtime === fileStat.mtimeMs && cache.size === fileStat.size) {
1328
- return null;
1329
- }
1330
- return await loadHistory();
1331
- }
1332
- async function fetchIncompleteItems() {
1333
- await autoLoadHistory();
1334
- if (!cache || cache.items.length === 0) {
1335
- return { items: [], startNo: -1 };
1336
1466
  }
1337
- const startIndex = cache.lastProcessedIndex === -1 ? 0 : cache.lastProcessedIndex + 1;
1338
- const startNo = startIndex;
1339
- const incompleteItems = cache.items.slice(startIndex);
1340
- if (incompleteItems.length > 0) {
1341
- cache.lastProcessedIndex = cache.items.length - 1;
1467
+ for (const [_sessionId, dateKey] of sessionDateMap) {
1468
+ const stats = dailyStats.get(dateKey);
1469
+ if (stats) {
1470
+ stats.sessionCount++;
1471
+ }
1342
1472
  }
1343
- return { items: incompleteItems, startNo };
1473
+ return dailyStats;
1344
1474
  }
1345
1475
 
1346
1476
  // src/services/rawDump/worker.ts
1347
- var log2 = createLogger("raw-dump");
1477
+ var log4 = createLogger("worker");
1348
1478
  var REQUEST_TIMEOUT_MS = 30000;
1349
1479
  var repoInfoCache = new Map;
1350
1480
  var REPO_CACHE_TTL_MS = 60000;
@@ -1357,40 +1487,14 @@ async function getCachedRepoInfo(directory) {
1357
1487
  repoInfoCache.set(directory, { repoInfo, ts: Date.now() });
1358
1488
  return repoInfo;
1359
1489
  }
1360
- function statisticsMessages(messages) {
1361
- let upstreamTokens = 0;
1362
- let downstreamTokens = 0;
1363
- let startTime = 0;
1364
- let endTime = 0;
1365
- for (const msg of messages) {
1366
- const usage = msg.message?.usage;
1367
- if (usage) {
1368
- upstreamTokens += (usage.input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0);
1369
- downstreamTokens += usage.output_tokens ?? 0;
1370
- }
1371
- const ts = msg.timestamp;
1372
- if (ts) {
1373
- const numTs = typeof ts === "number" ? ts : new Date(ts).getTime();
1374
- if (startTime === 0 || numTs < startTime)
1375
- startTime = numTs;
1376
- if (endTime === 0 || numTs > endTime)
1377
- endTime = numTs;
1378
- }
1379
- }
1380
- const latestTs = endTime || Date.now();
1381
- incrementConversation(latestTs);
1382
- addTokens(upstreamTokens, downstreamTokens, latestTs);
1383
- incrementSession(startTime || Date.now());
1384
- }
1385
1490
  async function processTask(task) {
1386
- log2.info("processing task:", { task });
1491
+ log4.info("processing task:", { task });
1387
1492
  const authData = await authWithFallback();
1388
1493
  const repoInfo = await getCachedRepoInfo(task.directory);
1389
1494
  const cacheEntry = await getParsedSession(task.directory, task.sessionId);
1390
1495
  const parsedSession = cacheEntry.parsedSession;
1391
- statisticsMessages(cacheEntry.messages);
1392
1496
  if (parsedSession.conversations.length === 0) {
1393
- log2.warn("no conversations found", { task });
1497
+ log4.warn("no conversations found", { task });
1394
1498
  }
1395
1499
  await uploadSummary({ sessionId: task.sessionId, directory: task.directory, parsedSession }, authData);
1396
1500
  for (const conv of parsedSession.conversations) {
@@ -1400,7 +1504,7 @@ async function processTask(task) {
1400
1504
  conversation: conv
1401
1505
  }, authData, { repoInfo });
1402
1506
  }
1403
- log2.info("task completed", {
1507
+ log4.info("task completed", {
1404
1508
  sessionId: task.sessionId,
1405
1509
  conversationCount: parsedSession.conversations.length
1406
1510
  });
@@ -1437,21 +1541,26 @@ function getRawDumpUrl(baseUrl, endpoint, isAnonymous = false) {
1437
1541
  async function uploadReport(authData, endpoint, body) {
1438
1542
  const mode = getRawDumpMode();
1439
1543
  if (mode === RAW_DUMP_MODE.DISABLED) {
1440
- log2.debug(`dump disabled, skipping ${endpoint}`);
1544
+ log4.debug(`dump disabled, skipping ${endpoint}`);
1441
1545
  return;
1442
1546
  }
1443
1547
  const type = endpoint === "/raw-store/task-conversation" ? "conversation" : endpoint === "/raw-store/task-summary" ? "summary" : endpoint === "/raw-store/commit" ? "commit" : endpoint === "/raw-store/statistics" ? "statistics" : "unknown";
1444
1548
  if (type === "unknown") {
1445
- log2.warn("unknown endpoint, skipping local dump", { endpoint });
1549
+ log4.warn("unknown endpoint, skipping local dump", { endpoint });
1446
1550
  } else if (mode === RAW_DUMP_MODE.LOCAL || mode === RAW_DUMP_MODE.BOTH) {
1447
1551
  await writeLocalDump(type, body);
1448
1552
  }
1553
+ if (mode === RAW_DUMP_MODE.LOCAL)
1554
+ return;
1555
+ await writeRemoteDump(endpoint, authData, body);
1556
+ }
1557
+ async function writeRemoteDump(endpoint, authData, body) {
1449
1558
  const isAnonymous = authData.isAnonymous ?? false;
1450
1559
  const url = getRawDumpUrl(authData.baseUrl, endpoint, isAnonymous);
1451
- log2.debug(`POST ${endpoint}`, { url, authData, isAnonymous });
1560
+ log4.debug(`POST ${endpoint}`, { url, authData, isAnonymous });
1452
1561
  try {
1453
1562
  await postJson(url, body, authData.headers);
1454
- log2.debug(`POST ${endpoint} ok`);
1563
+ log4.debug(`POST ${endpoint} ok`);
1455
1564
  return;
1456
1565
  } catch (err) {
1457
1566
  throw err instanceof UploadError ? err : new Error(`${endpoint} failed after 3 attempts`);
@@ -1492,18 +1601,18 @@ async function postJson(url, body, headers, maxAttempts = 3) {
1492
1601
  const text = await res.text().catch(() => "");
1493
1602
  lastError = new Error(`${url} failed: ${res.status} ${text}`);
1494
1603
  if (res.status === 429 || res.status >= 500) {
1495
- log2.warn(`retryable error ${res.status}, will retry`, { url, attempt, status: res.status });
1604
+ log4.warn(`retryable error ${res.status}, will retry`, { url, attempt, status: res.status });
1496
1605
  continue;
1497
1606
  }
1498
1607
  if (res.status === 401 || res.status === 502 || res.status === 503) {
1499
- log2.warn(`retryable status ${res.status}, will retry`, { url, attempt });
1608
+ log4.warn(`retryable status ${res.status}, will retry`, { url, attempt });
1500
1609
  continue;
1501
1610
  }
1502
1611
  throw lastError;
1503
1612
  } catch (err) {
1504
1613
  lastError = err instanceof Error ? err : new Error(String(err));
1505
1614
  const isAbort = lastError.name === "AbortError";
1506
- log2.warn(`${isAbort ? "timeout" : "network error"}, will retry`, {
1615
+ log4.warn(`${isAbort ? "timeout" : "network error"}, will retry`, {
1507
1616
  url,
1508
1617
  attempt,
1509
1618
  timeoutMs: REQUEST_TIMEOUT_MS,
@@ -1513,7 +1622,7 @@ async function postJson(url, body, headers, maxAttempts = 3) {
1513
1622
  clearTimeout(timer);
1514
1623
  }
1515
1624
  }
1516
- log2.error("postJson failed", {
1625
+ log4.error("postJson failed", {
1517
1626
  url,
1518
1627
  headers: headersObj,
1519
1628
  error: lastError?.message
@@ -1541,16 +1650,16 @@ function detectOs() {
1541
1650
  return map[process.platform] ?? process.platform;
1542
1651
  }
1543
1652
  async function auth() {
1544
- log2.debug("auth start");
1653
+ log4.debug("auth start");
1545
1654
  let creds = await loadCoStrictCredentials();
1546
1655
  if (!creds?.access_token)
1547
1656
  throw new Error("Not authenticated");
1548
- log2.debug("credentials loaded", {
1657
+ log4.debug("credentials loaded", {
1549
1658
  hasRefreshToken: !!creds.refresh_token,
1550
1659
  baseUrl: creds.base_url
1551
1660
  });
1552
1661
  if (creds.refresh_token && !isCoStrictTokenValid(creds)) {
1553
- log2.debug("token expired, refreshing...");
1662
+ log4.debug("token expired, refreshing...");
1554
1663
  const next = await refreshCoStrictToken({
1555
1664
  baseUrl: creds.base_url || process.env.COSTRICT_BASE_URL || process.env.COSTRICT_RAW_DUMP_BASE_URL || "https://zgsm.sangfor.com",
1556
1665
  refreshToken: creds.refresh_token,
@@ -1569,7 +1678,7 @@ async function auth() {
1569
1678
  access_token: next.access_token,
1570
1679
  refresh_token: next.refresh_token
1571
1680
  };
1572
- log2.debug("token refreshed");
1681
+ log4.debug("token refreshed");
1573
1682
  }
1574
1683
  const headers = new Headers;
1575
1684
  headers.set("Authorization", `Bearer ${creds.access_token}`);
@@ -1598,7 +1707,7 @@ async function auth() {
1598
1707
  }
1599
1708
  const user = parseUser(accessPayload, refreshPayload);
1600
1709
  const baseUrl = resolveRawDumpBaseUrl(creds.base_url);
1601
- log2.debug("auth success", {
1710
+ log4.debug("auth success", {
1602
1711
  baseUrl,
1603
1712
  user_id: user.user_id,
1604
1713
  clientId,
@@ -1613,18 +1722,76 @@ async function auth() {
1613
1722
  isAnonymous: false
1614
1723
  };
1615
1724
  }
1725
+ async function authWithFallback() {
1726
+ try {
1727
+ return await auth();
1728
+ } catch (err) {
1729
+ log4.info("auth failed, falling back to anonymous interface", {
1730
+ error: err instanceof Error ? err.message : String(err)
1731
+ });
1732
+ let version = "unknown";
1733
+ try {
1734
+ const pkgPath = path7.resolve(fileURLToPath(import.meta.url), "../../package.json");
1735
+ log4.info("pkgPath:", { meta_url: import.meta.url, pkgPath });
1736
+ const pkg = JSON.parse(await fs7.readFile(pkgPath, "utf-8"));
1737
+ version = pkg.version ?? "unknown";
1738
+ } catch {}
1739
+ let deviceId = process.env.CSC_DEVICE_ID;
1740
+ if (!deviceId) {
1741
+ const deviceIdFile = path7.join(getLocalDumpDir(), "device-id");
1742
+ try {
1743
+ deviceId = (await fs7.readFile(deviceIdFile, "utf-8")).trim();
1744
+ } catch {}
1745
+ if (!deviceId) {
1746
+ deviceId = generateMachineId();
1747
+ try {
1748
+ await fs7.writeFile(deviceIdFile, deviceId, "utf-8");
1749
+ } catch {}
1750
+ }
1751
+ process.env.CSC_DEVICE_ID = deviceId;
1752
+ log4.debug("resolved CSC_DEVICE_ID", { deviceId });
1753
+ }
1754
+ const headers = new Headers;
1755
+ headers.set("Content-Type", "application/json");
1756
+ headers.set("Authorization", `${createHash2("md5").update(deviceId).digest("hex")}`);
1757
+ headers.set("zgsm-client-id", deviceId);
1758
+ headers.set("zgsm-client-ide", "cli");
1759
+ headers.set("X-Costrict-Version", `csc-${version}`);
1760
+ headers.set("User-Agent", `csc/${version}`);
1761
+ return {
1762
+ baseUrl: resolveRawDumpBaseUrl(),
1763
+ headers,
1764
+ user: {
1765
+ user_id: "anonymous",
1766
+ user_name: "anonymous"
1767
+ },
1768
+ clientId: deviceId,
1769
+ version,
1770
+ isAnonymous: true
1771
+ };
1772
+ }
1773
+ }
1616
1774
  async function uploadConversation(payload, authData, options) {
1617
1775
  const conv = payload.conversation;
1618
1776
  const requestID = conv.id;
1619
1777
  const key = `${payload.sessionId}:${requestID}`;
1620
1778
  if (state_conv.conversation[key]) {
1621
- log2.info("conversation skipped: already uploaded", {
1779
+ log4.info("conversation skipped: already uploaded", {
1622
1780
  task_id: payload.sessionId,
1623
1781
  request_id: requestID,
1624
1782
  last_reported: state_conv.conversation[key]
1625
1783
  });
1626
1784
  return false;
1627
1785
  }
1786
+ if (state_conv.clean_time && payload.conversation.startTime && payload.conversation.startTime.getTime() < new Date(state_conv.clean_time).getTime()) {
1787
+ log4.info("conversation skipped: too old", {
1788
+ task_id: payload.sessionId,
1789
+ request_id: requestID,
1790
+ clean_time: state_conv.clean_time,
1791
+ start_time: payload.conversation.startTime.toISOString()
1792
+ });
1793
+ return false;
1794
+ }
1628
1795
  const rawDiff = conv.diffs.map((d) => d.content).join(`
1629
1796
  `);
1630
1797
  const diffLines = rawDiff ? countDiffLines(rawDiff) : 0;
@@ -1656,7 +1823,7 @@ async function uploadConversation(payload, authData, options) {
1656
1823
  repo_branch: repoInfo.repo_branch,
1657
1824
  work_dir: payload.directory
1658
1825
  };
1659
- log2.debug("conversation uploading", {
1826
+ log4.debug("conversation uploading", {
1660
1827
  task_id: payload.sessionId,
1661
1828
  request_id: requestID
1662
1829
  });
@@ -1667,7 +1834,7 @@ async function uploadConversation(payload, authData, options) {
1667
1834
  if (wasIncomplete)
1668
1835
  state_conv.incomplete--;
1669
1836
  await saveConversation();
1670
- log2.info("conversation uploaded", {
1837
+ log4.info("conversation uploaded", {
1671
1838
  task_id: payload.sessionId,
1672
1839
  request_id: requestID,
1673
1840
  upstream_tokens: body.upstream_tokens,
@@ -1677,18 +1844,25 @@ async function uploadConversation(payload, authData, options) {
1677
1844
  }
1678
1845
  async function uploadSummary(payload, authData) {
1679
1846
  const parsed = payload.parsedSession;
1680
- log2.debug("uploadSummary start", {
1847
+ log4.debug("uploadSummary start", {
1681
1848
  sessionId: payload.sessionId,
1682
1849
  conversationCount: parsed.conversations.length
1683
1850
  });
1684
1851
  if (state_summary.summary[payload.sessionId]) {
1685
- log2.info("summary skipped: already uploaded", {
1852
+ log4.info("summary skipped: already uploaded", {
1686
1853
  task_id: payload.sessionId
1687
1854
  });
1688
1855
  return;
1689
1856
  }
1690
1857
  const firstConv = parsed.conversations[0];
1691
1858
  const startTime = firstConv?.userEvent.timestamp ? new Date(firstConv.userEvent.timestamp) : new Date(Date.now());
1859
+ if (state_summary.clean_time && startTime < new Date(state_summary.clean_time)) {
1860
+ log4.info("summary skipped: too old", {
1861
+ startTime: startTime.toISOString(),
1862
+ clean_time: state_summary.clean_time
1863
+ });
1864
+ return;
1865
+ }
1692
1866
  const body = {
1693
1867
  task_id: payload.sessionId,
1694
1868
  start_time: formatIso(startTime),
@@ -1707,31 +1881,30 @@ async function uploadSummary(payload, authData) {
1707
1881
  if (wasIncomplete)
1708
1882
  state_summary.incomplete--;
1709
1883
  await saveSummary();
1710
- log2.info("summary uploaded", { task_id: payload.sessionId });
1884
+ log4.info("summary uploaded", { task_id: payload.sessionId });
1711
1885
  }
1712
- async function uploadCommits(payload, authData, options) {
1713
- log2.debug("uploadCommits start", { directory: payload.directory });
1714
- const repoInfo = options?.repoInfo ?? await getRepoInfo(payload.directory);
1886
+ async function uploadCommits(directory, authData, options) {
1887
+ const repoInfo = options?.repoInfo ?? await getRepoInfo(directory);
1715
1888
  if (!repoInfo.repo_addr || !repoInfo.repo_branch) {
1716
- log2.info("commits skipped: missing repo info", {
1717
- work_dir: payload.directory,
1889
+ log4.info("commits skipped: missing repo info", {
1890
+ work_dir: directory,
1718
1891
  repo_addr: repoInfo.repo_addr,
1719
1892
  repo_branch: repoInfo.repo_branch
1720
1893
  });
1721
1894
  return 0;
1722
1895
  }
1723
- const stateKey = `${repoInfo.repo_addr}#${repoInfo.repo_branch}#${payload.directory}`;
1896
+ const stateKey = `${repoInfo.repo_addr}#${repoInfo.repo_branch}#${directory}`;
1724
1897
  const lastCommit = state_commit.commits[stateKey];
1725
- log2.debug("commits state", { stateKey, lastCommit: lastCommit || "" });
1726
- const logText = await getCommitLog(payload.directory, lastCommit);
1898
+ log4.debug("commits state", { stateKey, lastCommit: lastCommit || "" });
1899
+ const logText = await getCommitLog(directory, lastCommit);
1727
1900
  const allCommits = parseCommitLog(logText);
1728
1901
  const commits = allCommits.slice(0, 50);
1729
- log2.debug("parsed commits", {
1902
+ log4.debug("parsed commits", {
1730
1903
  total: allCommits.length,
1731
1904
  sending: commits.length
1732
1905
  });
1733
1906
  if (!commits.length) {
1734
- log2.info("commits skipped: no new commits", { work_dir: payload.directory });
1907
+ log4.info("commits skipped: no new commits", { work_dir: directory });
1735
1908
  return 0;
1736
1909
  }
1737
1910
  const originalLastCommit = state_commit.commits[stateKey] || "";
@@ -1742,7 +1915,7 @@ async function uploadCommits(payload, authData, options) {
1742
1915
  if (i > 0 && i % 10 === 0) {
1743
1916
  await new Promise((r) => setTimeout(r, 500));
1744
1917
  }
1745
- const diff = await getCommitDiff(payload.directory, commit.commit_id);
1918
+ const diff = await getCommitDiff(directory, commit.commit_id);
1746
1919
  const body = {
1747
1920
  commit_id: commit.commit_id,
1748
1921
  commit_time: commit.commit_time,
@@ -1754,7 +1927,7 @@ async function uploadCommits(payload, authData, options) {
1754
1927
  client_id: authData.clientId,
1755
1928
  client_version: authData.version,
1756
1929
  client_ide: "cli",
1757
- work_dir: payload.directory,
1930
+ work_dir: directory,
1758
1931
  diff_lines: countDiffLines(diff),
1759
1932
  diff,
1760
1933
  files: extractFilesFromDiff(diff),
@@ -1766,7 +1939,7 @@ async function uploadCommits(payload, authData, options) {
1766
1939
  uploadedCount++;
1767
1940
  state_commit.commits[stateKey] = commit.commit_id;
1768
1941
  await saveCommits();
1769
- log2.info("commit uploaded", {
1942
+ log4.info("commit uploaded", {
1770
1943
  commit_id: commit.commit_id,
1771
1944
  progress: `${i + 1}/${commits.length}`
1772
1945
  });
@@ -1774,8 +1947,8 @@ async function uploadCommits(payload, authData, options) {
1774
1947
  } catch (err) {
1775
1948
  state_commit.commits[stateKey] = originalLastCommit;
1776
1949
  await saveCommits();
1777
- log2.error("commit batch failed, rolled back", {
1778
- work_dir: payload.directory,
1950
+ log4.error("commit batch failed, rolled back", {
1951
+ work_dir: directory,
1779
1952
  uploadedCount,
1780
1953
  error: err instanceof Error ? err.message : String(err)
1781
1954
  });
@@ -1783,14 +1956,14 @@ async function uploadCommits(payload, authData, options) {
1783
1956
  }
1784
1957
  return commits.length;
1785
1958
  }
1786
- async function uploadStatistics(payload, authData) {
1959
+ async function uploadStatistics(authData) {
1787
1960
  updateStatisticsForUpload();
1788
1961
  for (const [k, r] of Object.entries(state_statistics.statistics)) {
1789
1962
  if (r.currentUploadAt)
1790
1963
  continue;
1791
1964
  const daily = r.daily;
1792
1965
  const body = {
1793
- task_id: payload.sessionId,
1966
+ date: k,
1794
1967
  start_time: formatIso(new Date(daily.startTime)),
1795
1968
  end_time: formatIso(new Date(daily.endTime)),
1796
1969
  ...authData.user,
@@ -1807,7 +1980,7 @@ async function uploadStatistics(payload, authData) {
1807
1980
  state_statistics.statistics[k].currentUploadAt = timestamp;
1808
1981
  state_statistics.incomplete--;
1809
1982
  await saveStatistics();
1810
- log2.info("statistics uploaded", {
1983
+ log4.info("statistics uploaded", {
1811
1984
  k,
1812
1985
  session_count: daily.sessionCount,
1813
1986
  conversation_count: daily.conversationCount,
@@ -1829,7 +2002,7 @@ async function processIncompleteTasks() {
1829
2002
  await saveTasks();
1830
2003
  } catch (err) {
1831
2004
  const errorMsg = err instanceof Error ? err.message : String(err);
1832
- log2.error("task failed", {
2005
+ log4.error("task failed", {
1833
2006
  error: errorMsg,
1834
2007
  task: record
1835
2008
  });
@@ -1850,7 +2023,7 @@ async function processIncompleteTasks() {
1850
2023
  state_task.tasks[key].uploadedAt = "DEAD_LETTER";
1851
2024
  state_task.incomplete--;
1852
2025
  await saveTasks();
1853
- log2.error("task moved to dead letter", {
2026
+ log4.error("task moved to dead letter", {
1854
2027
  key,
1855
2028
  attemptCount: state_task.tasks[key].attemptCount
1856
2029
  });
@@ -1858,269 +2031,194 @@ async function processIncompleteTasks() {
1858
2031
  }
1859
2032
  }
1860
2033
  }
1861
- async function runHistoryWorker() {
2034
+ async function runHistorySessionWorker() {
1862
2035
  try {
1863
- log2.info("history start");
1864
- const { items: newItems, startNo } = await fetchIncompleteItems();
1865
- log2.info("history loaded: ", { startNo, length: newItems.length });
1866
- addHistoryTasks(startNo, newItems);
2036
+ log4.info("history session start");
2037
+ const items = await autoLoadHistory();
2038
+ log4.info("history session loaded: ", { length: items ? items.length : 0 });
2039
+ addHistoryTasks(items ?? []);
1867
2040
  await saveTasks();
1868
2041
  } catch (err) {
1869
- log2.error("history failed", {
2042
+ log4.error("history failed", {
1870
2043
  error: err instanceof Error ? err.message : String(err)
1871
2044
  });
1872
2045
  }
1873
- log2.info(`tracking ${state_task.total} tasks total, ${state_task.incomplete} incomplete`);
2046
+ log4.info(`tracking ${state_task.total} tasks total, ${state_task.incomplete} incomplete`);
1874
2047
  try {
1875
2048
  await processIncompleteTasks();
1876
- log2.info("history completed");
2049
+ log4.info("history completed");
1877
2050
  } catch (err) {
1878
- log2.error("history process failed, queue retained", {
2051
+ log4.error("history process failed, queue retained", {
1879
2052
  error: err instanceof Error ? err.message : String(err)
1880
2053
  });
1881
2054
  throw err;
1882
2055
  }
1883
2056
  }
1884
2057
  async function runRawDumpWorker() {
2058
+ log4.info("WORKER start");
1885
2059
  try {
1886
- log2.info("WORKER start");
1887
2060
  await loadAllState();
2061
+ await runHistorySessionWorker();
2062
+ await runCommitTimer();
2063
+ await runStatisticsTimer();
1888
2064
  } catch (err) {
1889
- log2.error("WORKER failed", {
2065
+ log4.error("WORKER failed", {
1890
2066
  error: err instanceof Error ? err.message : String(err)
1891
2067
  });
1892
2068
  }
1893
- runHistoryWorker();
2069
+ log4.info("WORKER end");
1894
2070
  }
1895
- async function authWithFallback() {
1896
- try {
1897
- return await auth();
1898
- } catch (err) {
1899
- log2.info("auth failed, falling back to anonymous interface", {
1900
- error: err instanceof Error ? err.message : String(err)
1901
- });
1902
- let version = "unknown";
1903
- try {
1904
- const pkgPath = path7.resolve(fileURLToPath(import.meta.url), "../../package.json");
1905
- log2.info("pkgPath:", { meta_url: import.meta.url, pkgPath });
1906
- const pkg = JSON.parse(await fs7.readFile(pkgPath, "utf-8"));
1907
- version = pkg.version ?? "unknown";
1908
- } catch {}
1909
- let deviceId = process.env.CSC_DEVICE_ID;
1910
- if (!deviceId) {
1911
- const deviceIdFile = path7.join(getLocalDumpDir(), "device-id");
1912
- try {
1913
- deviceId = (await fs7.readFile(deviceIdFile, "utf-8")).trim();
1914
- } catch {}
1915
- if (!deviceId) {
1916
- deviceId = generateMachineId();
1917
- try {
1918
- await fs7.writeFile(deviceIdFile, deviceId, "utf-8");
1919
- } catch {}
1920
- }
1921
- process.env.CSC_DEVICE_ID = deviceId;
1922
- log2.debug("resolved CSC_DEVICE_ID", { deviceId });
1923
- }
1924
- const headers = new Headers;
1925
- headers.set("Content-Type", "application/json");
1926
- headers.set("Authorization", `${createHash2("md5").update(deviceId).digest("hex")}`);
1927
- headers.set("zgsm-client-id", deviceId);
1928
- headers.set("zgsm-client-ide", "cli");
1929
- headers.set("X-Costrict-Version", `csc-${version}`);
1930
- headers.set("User-Agent", `csc/${version}`);
1931
- return {
1932
- baseUrl: resolveRawDumpBaseUrl(),
1933
- headers,
1934
- user: {
1935
- user_id: "anonymous",
1936
- user_name: "anonymous"
1937
- },
1938
- clientId: deviceId,
1939
- version,
1940
- isAnonymous: true
1941
- };
1942
- }
1943
- }
1944
- var scriptPath = process.argv[1] || "";
1945
- if (scriptPath.endsWith("worker.ts") || scriptPath.endsWith("worker.js")) {
1946
- runRawDumpWorker();
1947
- }
1948
-
1949
- // src/services/rawDump/timerWorker.ts
1950
- import { promises as fs8 } from "fs";
1951
- import os8 from "os";
1952
- import path8 from "path";
1953
- var log3 = createLogger("raw-dump-timer");
1954
- var TIMER_LOCK_FILE = path8.join(os8.homedir(), ".claude", "raw-dump", "csc-timer.lock");
1955
- var HISTORY_FILE2 = path8.join(os8.homedir(), ".claude", "history.jsonl");
1956
2071
  var COMMIT_INTERVAL_MS = 5 * 60 * 1000;
1957
2072
  var STATS_INTERVAL_MS = 60 * 60 * 1000;
1958
2073
  var QUEUE_INTERVAL_MS = 120000;
1959
- var isRunning = false;
1960
- var lastCommitRun = 0;
1961
2074
  var lastStatsRun = 0;
1962
2075
  var lastQueueRun = 0;
1963
- var projectDirsCache = null;
1964
- async function acquireTimerLock() {
1965
- try {
1966
- const stat = await fs8.readFile(TIMER_LOCK_FILE, "utf-8").catch(() => "");
1967
- const pid = parseInt(stat.trim(), 10);
1968
- if (!isNaN(pid) && pid !== process.pid) {
1969
- try {
1970
- process.kill(pid, 0);
1971
- return false;
1972
- } catch {}
1973
- }
1974
- await fs8.writeFile(TIMER_LOCK_FILE, String(process.pid), "utf-8");
1975
- return true;
1976
- } catch {
1977
- return false;
1978
- }
1979
- }
1980
- async function releaseTimerLock() {
1981
- try {
1982
- await fs8.writeFile(TIMER_LOCK_FILE, "", "utf-8");
1983
- } catch {}
1984
- }
1985
- async function getProjectDirs() {
1986
- try {
1987
- const stat = await fs8.stat(HISTORY_FILE2);
1988
- if (projectDirsCache && projectDirsCache.mtime === stat.mtimeMs && projectDirsCache.size === stat.size) {
1989
- return projectDirsCache.dirs;
1990
- }
1991
- } catch {
1992
- if (projectDirsCache) {
1993
- return projectDirsCache.dirs;
1994
- }
1995
- return [];
1996
- }
1997
- const projects = new Set;
1998
- try {
1999
- const content = await fs8.readFile(HISTORY_FILE2, "utf-8");
2000
- const lines = content.split(`
2001
- `);
2002
- for (const line of lines) {
2003
- if (!line.trim())
2004
- continue;
2005
- try {
2006
- const record = JSON.parse(line);
2007
- if (record.project) {
2008
- projects.add(record.project);
2009
- }
2010
- } catch {}
2011
- }
2012
- } catch {
2013
- return projectDirsCache ? projectDirsCache.dirs : [];
2014
- }
2015
- const dirs = [...projects];
2016
- try {
2017
- const stat = await fs8.stat(HISTORY_FILE2);
2018
- projectDirsCache = { mtime: stat.mtimeMs, size: stat.size, dirs };
2019
- } catch {}
2020
- return dirs;
2076
+ var lastCommitRun = 0;
2077
+ function getTimerIntervals() {
2078
+ return {
2079
+ commitIntervalMs: COMMIT_INTERVAL_MS,
2080
+ statsIntervalMs: STATS_INTERVAL_MS,
2081
+ queueIntervalMs: QUEUE_INTERVAL_MS
2082
+ };
2021
2083
  }
2022
2084
  async function runCommitTimer() {
2023
2085
  const now = Date.now();
2024
2086
  if (now - lastCommitRun < COMMIT_INTERVAL_MS)
2025
2087
  return;
2026
- log3.debug("commit timer firing");
2027
2088
  lastCommitRun = now;
2089
+ log4.debug("commit timer firing");
2028
2090
  const dirs = await getProjectDirs();
2029
- log3.debug("scanned project dirs", { count: dirs.length });
2091
+ log4.debug("scanned project dirs", { count: dirs.length });
2030
2092
  const authData = await authWithFallback();
2031
2093
  for (const dir of dirs) {
2032
2094
  try {
2033
- const repoInfo = await getRepoInfo(dir);
2095
+ const repoInfo = await getCachedRepoInfo(dir);
2034
2096
  if (!repoInfo.repo_addr || !repoInfo.repo_branch) {
2035
- log3.debug("skip dir: missing repo info", { dir });
2097
+ log4.debug("skip dir: missing repo info", { dir });
2036
2098
  continue;
2037
2099
  }
2038
- await uploadCommits({ directory: dir }, authData, { repoInfo });
2100
+ await uploadCommits(dir, authData, { repoInfo });
2039
2101
  } catch (err) {
2040
- log3.error("commit timer failed for dir", {
2102
+ log4.error("commit timer failed for dir", {
2041
2103
  dir,
2042
2104
  error: err instanceof Error ? err.message : String(err)
2043
2105
  });
2044
2106
  }
2045
2107
  }
2046
- log3.debug("commit timer done");
2108
+ log4.debug("commit timer done");
2047
2109
  }
2048
2110
  async function runStatisticsTimer() {
2049
2111
  const now = Date.now();
2050
2112
  if (now - lastStatsRun < STATS_INTERVAL_MS)
2051
2113
  return;
2052
- log3.debug("statistics timer firing");
2053
2114
  lastStatsRun = now;
2115
+ log4.debug("statistics timer firing");
2054
2116
  try {
2055
2117
  const authData = await authWithFallback();
2056
- await uploadStatistics({ sessionId: "", directory: "" }, authData);
2118
+ await uploadStatistics(authData);
2057
2119
  } catch (err) {
2058
- log3.error("statistics timer failed", {
2120
+ log4.error("statistics timer failed", {
2059
2121
  error: err instanceof Error ? err.message : String(err)
2060
2122
  });
2061
2123
  }
2062
- log3.debug("statistics timer done");
2124
+ log4.debug("statistics timer done");
2063
2125
  }
2064
2126
  async function runQueueTimer() {
2065
2127
  const now = Date.now();
2066
2128
  if (now - lastQueueRun < QUEUE_INTERVAL_MS)
2067
2129
  return;
2068
2130
  lastQueueRun = now;
2069
- log3.debug("queue timer firing");
2131
+ log4.debug("queue timer firing");
2132
+ if (!await acquireQueueLock()) {
2133
+ log4.debug("another worker process holds the lock, skip");
2134
+ return;
2135
+ }
2070
2136
  try {
2071
- if (!await acquireLock()) {
2072
- log3.debug("another worker process holds the lock, skip");
2073
- return;
2074
- }
2075
2137
  const newTasks = getQueue();
2076
2138
  if (newTasks.length === 0) {
2077
2139
  if (state_task.incomplete === 0) {
2078
- log3.debug("queue empty, no incomplete tasks");
2140
+ log4.debug("queue empty, no incomplete tasks");
2079
2141
  return;
2080
2142
  }
2081
- log3.debug("queue empty, but has incomplete tasks in state");
2143
+ log4.debug("queue empty, but has incomplete tasks in state");
2082
2144
  }
2083
- log3.info(`processing ${newTasks.length} new tasks`);
2145
+ log4.info(`processing ${newTasks.length} new tasks`);
2084
2146
  addQueueTasks(newTasks);
2085
2147
  await saveTasks();
2086
2148
  clearQueue();
2087
2149
  } catch (err) {
2088
- log3.error("runBatch failed", {
2150
+ log4.error("runBatch failed", {
2089
2151
  error: err instanceof Error ? err.message : String(err)
2090
2152
  });
2091
2153
  } finally {
2092
- await releaseLock();
2154
+ await releaseQueueLock();
2093
2155
  }
2094
- log3.info(`tracking ${state_task.total} tasks total, ${state_task.incomplete} incomplete`);
2156
+ log4.info(`tracking ${state_task.total} tasks total, ${state_task.incomplete} incomplete`);
2095
2157
  try {
2096
2158
  await processIncompleteTasks();
2097
- log3.info("queue timer completed");
2159
+ log4.info("queue timer completed");
2098
2160
  } catch (err) {
2099
- log3.error("queue timer failed, queue retained", {
2161
+ log4.error("queue timer failed, queue retained", {
2100
2162
  error: err instanceof Error ? err.message : String(err)
2101
2163
  });
2102
2164
  throw err;
2103
2165
  }
2104
2166
  }
2167
+ var scriptPath = process.argv[1] || "";
2168
+ if (scriptPath.endsWith("worker.ts") || scriptPath.endsWith("worker.js")) {
2169
+ (async () => {
2170
+ await runRawDumpWorker();
2171
+ })();
2172
+ }
2173
+
2174
+ // src/services/rawDump/timerWorker.ts
2175
+ import { promises as fs8 } from "fs";
2176
+ import os8 from "os";
2177
+ import path8 from "path";
2178
+ var log5 = createLogger("timer");
2179
+ var TIMER_LOCK_FILE = path8.join(os8.homedir(), ".claude", "raw-dump", "csc-timer.lock");
2180
+ var isRunning = false;
2181
+ async function acquireTimerLock() {
2182
+ try {
2183
+ const stat2 = await fs8.readFile(TIMER_LOCK_FILE, "utf-8").catch(() => "");
2184
+ const pid = parseInt(stat2.trim(), 10);
2185
+ if (!isNaN(pid) && pid !== process.pid) {
2186
+ try {
2187
+ process.kill(pid, 0);
2188
+ return false;
2189
+ } catch {}
2190
+ }
2191
+ await fs8.writeFile(TIMER_LOCK_FILE, String(process.pid), "utf-8");
2192
+ return true;
2193
+ } catch {
2194
+ return false;
2195
+ }
2196
+ }
2197
+ async function releaseTimerLock() {
2198
+ try {
2199
+ await fs8.writeFile(TIMER_LOCK_FILE, "", "utf-8");
2200
+ } catch {}
2201
+ }
2105
2202
  async function runTimers() {
2106
2203
  if (isRunning)
2107
2204
  return;
2108
2205
  isRunning = true;
2206
+ if (!await acquireTimerLock()) {
2207
+ log5.debug("another timer worker holds the lock, skip");
2208
+ isRunning = false;
2209
+ return;
2210
+ }
2109
2211
  try {
2110
- if (!await acquireTimerLock()) {
2111
- log3.debug("another timer worker holds the lock, skip");
2112
- return;
2113
- }
2114
2212
  await runQueueTimer();
2115
2213
  await runCommitTimer();
2116
2214
  await runStatisticsTimer();
2117
- await releaseTimerLock();
2118
2215
  } catch (err) {
2119
- log3.error("timer worker error", {
2216
+ log5.error("timer worker error", {
2120
2217
  error: err instanceof Error ? err.message : String(err)
2121
2218
  });
2122
2219
  } finally {
2123
2220
  isRunning = false;
2221
+ await releaseTimerLock();
2124
2222
  }
2125
2223
  }
2126
2224
  function scheduleNext() {
@@ -2128,7 +2226,7 @@ function scheduleNext() {
2128
2226
  try {
2129
2227
  await runTimers();
2130
2228
  } catch (err) {
2131
- log3.error("runTimers threw", {
2229
+ log5.error("runTimers threw", {
2132
2230
  error: err instanceof Error ? err.message : String(err)
2133
2231
  });
2134
2232
  }
@@ -2136,16 +2234,12 @@ function scheduleNext() {
2136
2234
  }, 60000);
2137
2235
  }
2138
2236
  function startTimerWorker() {
2139
- log3.info("timer worker starting", {
2140
- commitIntervalMs: COMMIT_INTERVAL_MS,
2141
- statsIntervalMs: STATS_INTERVAL_MS,
2142
- queueIntervalMs: QUEUE_INTERVAL_MS
2143
- });
2237
+ log5.info("timer worker starting", getTimerIntervals());
2144
2238
  setImmediate(async () => {
2145
2239
  try {
2146
2240
  await runTimers();
2147
2241
  } catch (err) {
2148
- log3.error("initial timer run failed", {
2242
+ log5.error("initial timer run failed", {
2149
2243
  error: err instanceof Error ? err.message : String(err)
2150
2244
  });
2151
2245
  }
@@ -2158,24 +2252,32 @@ if (scriptPath2.endsWith("timerWorker.ts") || scriptPath2.endsWith("timerWorker.
2158
2252
  }
2159
2253
 
2160
2254
  // src/services/rawDump/batchWorker.ts
2161
- var log4 = createLogger("raw-dump-batch");
2255
+ var log6 = createLogger("batch");
2162
2256
  var PARENT_PID = process.ppid;
2163
2257
  var IS_WORKER_PROCESS = process.argv[1]?.includes("batchWorker") || false;
2164
2258
  function startBatchWorker() {
2165
- log4.info("batch worker started");
2259
+ log6.info("batch worker started");
2166
2260
  loadAllState().catch((err) => {
2167
- log4.error("failed to load state", {
2261
+ log6.error("failed to load state", {
2168
2262
  error: err instanceof Error ? err.message : String(err)
2169
2263
  });
2170
2264
  process.exit(1);
2171
2265
  });
2172
2266
  loadQueue().catch((err) => {
2173
- log4.error("failed to load queue", {
2267
+ log6.error("failed to load queue", {
2174
2268
  error: err instanceof Error ? err.message : String(err)
2175
2269
  });
2176
2270
  process.exit(1);
2177
2271
  });
2178
- runHistoryWorker();
2272
+ setImmediate(async () => {
2273
+ try {
2274
+ await runHistorySessionWorker();
2275
+ } catch (err) {
2276
+ log6.error("runHistorySessionWorker failed", {
2277
+ error: err instanceof Error ? err.message : String(err)
2278
+ });
2279
+ }
2280
+ });
2179
2281
  startTimerWorker();
2180
2282
  }
2181
2283
  var scriptPath3 = process.argv[1] || "";
@@ -2186,5 +2288,5 @@ export {
2186
2288
  startBatchWorker
2187
2289
  };
2188
2290
 
2189
- //# debugId=A6D84926CC73F00864756E2164756E21
2291
+ //# debugId=2644B5BA08355EE564756E2164756E21
2190
2292
  //# sourceMappingURL=batchWorker.js.map