@jamesaphoenix/tx-core 0.4.1

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 (289) hide show
  1. package/dist/db.d.ts +42 -0
  2. package/dist/db.d.ts.map +1 -0
  3. package/dist/db.js +46 -0
  4. package/dist/db.js.map +1 -0
  5. package/dist/errors.d.ts +231 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +139 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/id.d.ts +6 -0
  10. package/dist/id.d.ts.map +1 -0
  11. package/dist/id.js +21 -0
  12. package/dist/id.js.map +1 -0
  13. package/dist/index.d.ts +25 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +56 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/layer.d.ts +50 -0
  18. package/dist/layer.d.ts.map +1 -0
  19. package/dist/layer.js +155 -0
  20. package/dist/layer.js.map +1 -0
  21. package/dist/mappers/anchor.d.ts +14 -0
  22. package/dist/mappers/anchor.d.ts.map +1 -0
  23. package/dist/mappers/anchor.js +38 -0
  24. package/dist/mappers/anchor.js.map +1 -0
  25. package/dist/mappers/attempt.d.ts +15 -0
  26. package/dist/mappers/attempt.d.ts.map +1 -0
  27. package/dist/mappers/attempt.js +23 -0
  28. package/dist/mappers/attempt.js.map +1 -0
  29. package/dist/mappers/candidate.d.ts +23 -0
  30. package/dist/mappers/candidate.d.ts.map +1 -0
  31. package/dist/mappers/candidate.js +53 -0
  32. package/dist/mappers/candidate.js.map +1 -0
  33. package/dist/mappers/claim.d.ts +30 -0
  34. package/dist/mappers/claim.d.ts.map +1 -0
  35. package/dist/mappers/claim.js +32 -0
  36. package/dist/mappers/claim.js.map +1 -0
  37. package/dist/mappers/deduplication.d.ts +39 -0
  38. package/dist/mappers/deduplication.d.ts.map +1 -0
  39. package/dist/mappers/deduplication.js +53 -0
  40. package/dist/mappers/deduplication.js.map +1 -0
  41. package/dist/mappers/edge.d.ts +10 -0
  42. package/dist/mappers/edge.d.ts.map +1 -0
  43. package/dist/mappers/edge.js +19 -0
  44. package/dist/mappers/edge.js.map +1 -0
  45. package/dist/mappers/file-learning.d.ts +14 -0
  46. package/dist/mappers/file-learning.d.ts.map +1 -0
  47. package/dist/mappers/file-learning.js +75 -0
  48. package/dist/mappers/file-learning.js.map +1 -0
  49. package/dist/mappers/index.d.ts +17 -0
  50. package/dist/mappers/index.d.ts.map +1 -0
  51. package/dist/mappers/index.js +30 -0
  52. package/dist/mappers/index.js.map +1 -0
  53. package/dist/mappers/learning.d.ts +19 -0
  54. package/dist/mappers/learning.d.ts.map +1 -0
  55. package/dist/mappers/learning.js +41 -0
  56. package/dist/mappers/learning.js.map +1 -0
  57. package/dist/mappers/orchestrator-state.d.ts +33 -0
  58. package/dist/mappers/orchestrator-state.d.ts.map +1 -0
  59. package/dist/mappers/orchestrator-state.js +34 -0
  60. package/dist/mappers/orchestrator-state.js.map +1 -0
  61. package/dist/mappers/run.d.ts +32 -0
  62. package/dist/mappers/run.d.ts.map +1 -0
  63. package/dist/mappers/run.js +64 -0
  64. package/dist/mappers/run.js.map +1 -0
  65. package/dist/mappers/task.d.ts +23 -0
  66. package/dist/mappers/task.d.ts.map +1 -0
  67. package/dist/mappers/task.js +54 -0
  68. package/dist/mappers/task.js.map +1 -0
  69. package/dist/mappers/tracked-project.d.ts +15 -0
  70. package/dist/mappers/tracked-project.d.ts.map +1 -0
  71. package/dist/mappers/tracked-project.js +23 -0
  72. package/dist/mappers/tracked-project.js.map +1 -0
  73. package/dist/mappers/worker.d.ts +33 -0
  74. package/dist/mappers/worker.d.ts.map +1 -0
  75. package/dist/mappers/worker.js +35 -0
  76. package/dist/mappers/worker.js.map +1 -0
  77. package/dist/repo/anchor-repo.d.ts +52 -0
  78. package/dist/repo/anchor-repo.d.ts.map +1 -0
  79. package/dist/repo/anchor-repo.js +204 -0
  80. package/dist/repo/anchor-repo.js.map +1 -0
  81. package/dist/repo/attempt-repo.d.ts +25 -0
  82. package/dist/repo/attempt-repo.d.ts.map +1 -0
  83. package/dist/repo/attempt-repo.js +78 -0
  84. package/dist/repo/attempt-repo.js.map +1 -0
  85. package/dist/repo/candidate-repo.d.ts +16 -0
  86. package/dist/repo/candidate-repo.d.ts.map +1 -0
  87. package/dist/repo/candidate-repo.js +143 -0
  88. package/dist/repo/candidate-repo.js.map +1 -0
  89. package/dist/repo/claim-repo.d.ts +17 -0
  90. package/dist/repo/claim-repo.d.ts.map +1 -0
  91. package/dist/repo/claim-repo.js +62 -0
  92. package/dist/repo/claim-repo.js.map +1 -0
  93. package/dist/repo/deduplication-repo.d.ts +37 -0
  94. package/dist/repo/deduplication-repo.d.ts.map +1 -0
  95. package/dist/repo/deduplication-repo.js +133 -0
  96. package/dist/repo/deduplication-repo.js.map +1 -0
  97. package/dist/repo/dep-repo.d.ts +19 -0
  98. package/dist/repo/dep-repo.d.ts.map +1 -0
  99. package/dist/repo/dep-repo.js +104 -0
  100. package/dist/repo/dep-repo.js.map +1 -0
  101. package/dist/repo/edge-repo.d.ts +26 -0
  102. package/dist/repo/edge-repo.d.ts.map +1 -0
  103. package/dist/repo/edge-repo.js +227 -0
  104. package/dist/repo/edge-repo.js.map +1 -0
  105. package/dist/repo/file-learning-repo.d.ts +17 -0
  106. package/dist/repo/file-learning-repo.d.ts.map +1 -0
  107. package/dist/repo/file-learning-repo.js +60 -0
  108. package/dist/repo/file-learning-repo.js.map +1 -0
  109. package/dist/repo/index.d.ts +18 -0
  110. package/dist/repo/index.d.ts.map +1 -0
  111. package/dist/repo/index.js +18 -0
  112. package/dist/repo/index.js.map +1 -0
  113. package/dist/repo/learning-repo.d.ts +31 -0
  114. package/dist/repo/learning-repo.d.ts.map +1 -0
  115. package/dist/repo/learning-repo.js +165 -0
  116. package/dist/repo/learning-repo.js.map +1 -0
  117. package/dist/repo/orchestrator-state-repo.d.ts +27 -0
  118. package/dist/repo/orchestrator-state-repo.d.ts.map +1 -0
  119. package/dist/repo/orchestrator-state-repo.js +96 -0
  120. package/dist/repo/orchestrator-state-repo.js.map +1 -0
  121. package/dist/repo/run-repo.d.ts +31 -0
  122. package/dist/repo/run-repo.d.ts.map +1 -0
  123. package/dist/repo/run-repo.js +132 -0
  124. package/dist/repo/run-repo.js.map +1 -0
  125. package/dist/repo/task-repo.d.ts +21 -0
  126. package/dist/repo/task-repo.d.ts.map +1 -0
  127. package/dist/repo/task-repo.js +169 -0
  128. package/dist/repo/task-repo.js.map +1 -0
  129. package/dist/repo/tracked-project-repo.d.ts +16 -0
  130. package/dist/repo/tracked-project-repo.d.ts.map +1 -0
  131. package/dist/repo/tracked-project-repo.js +54 -0
  132. package/dist/repo/tracked-project-repo.js.map +1 -0
  133. package/dist/repo/worker-repo.d.ts +19 -0
  134. package/dist/repo/worker-repo.d.ts.map +1 -0
  135. package/dist/repo/worker-repo.js +72 -0
  136. package/dist/repo/worker-repo.js.map +1 -0
  137. package/dist/schemas/index.d.ts +8 -0
  138. package/dist/schemas/index.d.ts.map +1 -0
  139. package/dist/schemas/index.js +7 -0
  140. package/dist/schemas/index.js.map +1 -0
  141. package/dist/schemas/sync.d.ts +296 -0
  142. package/dist/schemas/sync.d.ts.map +1 -0
  143. package/dist/schemas/sync.js +146 -0
  144. package/dist/schemas/sync.js.map +1 -0
  145. package/dist/schemas/worker.d.ts +77 -0
  146. package/dist/schemas/worker.d.ts.map +1 -0
  147. package/dist/schemas/worker.js +80 -0
  148. package/dist/schemas/worker.js.map +1 -0
  149. package/dist/services/anchor-service.d.ts +147 -0
  150. package/dist/services/anchor-service.d.ts.map +1 -0
  151. package/dist/services/anchor-service.js +540 -0
  152. package/dist/services/anchor-service.js.map +1 -0
  153. package/dist/services/anchor-verification.d.ts +94 -0
  154. package/dist/services/anchor-verification.d.ts.map +1 -0
  155. package/dist/services/anchor-verification.js +617 -0
  156. package/dist/services/anchor-verification.js.map +1 -0
  157. package/dist/services/ast-grep-service.d.ts +58 -0
  158. package/dist/services/ast-grep-service.d.ts.map +1 -0
  159. package/dist/services/ast-grep-service.js +356 -0
  160. package/dist/services/ast-grep-service.js.map +1 -0
  161. package/dist/services/attempt-service.d.ts +24 -0
  162. package/dist/services/attempt-service.d.ts.map +1 -0
  163. package/dist/services/attempt-service.js +55 -0
  164. package/dist/services/attempt-service.js.map +1 -0
  165. package/dist/services/auto-sync-service.d.ts +56 -0
  166. package/dist/services/auto-sync-service.d.ts.map +1 -0
  167. package/dist/services/auto-sync-service.js +66 -0
  168. package/dist/services/auto-sync-service.js.map +1 -0
  169. package/dist/services/candidate-extractor-service.d.ts +56 -0
  170. package/dist/services/candidate-extractor-service.d.ts.map +1 -0
  171. package/dist/services/candidate-extractor-service.js +365 -0
  172. package/dist/services/candidate-extractor-service.js.map +1 -0
  173. package/dist/services/claim-service.d.ts +52 -0
  174. package/dist/services/claim-service.d.ts.map +1 -0
  175. package/dist/services/claim-service.js +134 -0
  176. package/dist/services/claim-service.js.map +1 -0
  177. package/dist/services/daemon-service.d.ts +214 -0
  178. package/dist/services/daemon-service.d.ts.map +1 -0
  179. package/dist/services/daemon-service.js +522 -0
  180. package/dist/services/daemon-service.js.map +1 -0
  181. package/dist/services/deduplication-service.d.ts +67 -0
  182. package/dist/services/deduplication-service.d.ts.map +1 -0
  183. package/dist/services/deduplication-service.js +145 -0
  184. package/dist/services/deduplication-service.js.map +1 -0
  185. package/dist/services/dep-service.d.ts +14 -0
  186. package/dist/services/dep-service.d.ts.map +1 -0
  187. package/dist/services/dep-service.js +34 -0
  188. package/dist/services/dep-service.js.map +1 -0
  189. package/dist/services/diversifier-service.d.ts +46 -0
  190. package/dist/services/diversifier-service.d.ts.map +1 -0
  191. package/dist/services/diversifier-service.js +197 -0
  192. package/dist/services/diversifier-service.js.map +1 -0
  193. package/dist/services/edge-service.d.ts +78 -0
  194. package/dist/services/edge-service.d.ts.map +1 -0
  195. package/dist/services/edge-service.js +158 -0
  196. package/dist/services/edge-service.js.map +1 -0
  197. package/dist/services/embedding-service.d.ts +138 -0
  198. package/dist/services/embedding-service.d.ts.map +1 -0
  199. package/dist/services/embedding-service.js +318 -0
  200. package/dist/services/embedding-service.js.map +1 -0
  201. package/dist/services/feedback-tracker.d.ts +64 -0
  202. package/dist/services/feedback-tracker.d.ts.map +1 -0
  203. package/dist/services/feedback-tracker.js +110 -0
  204. package/dist/services/feedback-tracker.js.map +1 -0
  205. package/dist/services/file-learning-service.d.ts +17 -0
  206. package/dist/services/file-learning-service.d.ts.map +1 -0
  207. package/dist/services/file-learning-service.js +41 -0
  208. package/dist/services/file-learning-service.js.map +1 -0
  209. package/dist/services/file-watcher-service.d.ts +141 -0
  210. package/dist/services/file-watcher-service.d.ts.map +1 -0
  211. package/dist/services/file-watcher-service.js +278 -0
  212. package/dist/services/file-watcher-service.js.map +1 -0
  213. package/dist/services/graph-expansion.d.ts +155 -0
  214. package/dist/services/graph-expansion.d.ts.map +1 -0
  215. package/dist/services/graph-expansion.js +466 -0
  216. package/dist/services/graph-expansion.js.map +1 -0
  217. package/dist/services/hierarchy-service.d.ts +16 -0
  218. package/dist/services/hierarchy-service.d.ts.map +1 -0
  219. package/dist/services/hierarchy-service.js +66 -0
  220. package/dist/services/hierarchy-service.js.map +1 -0
  221. package/dist/services/index.d.ts +36 -0
  222. package/dist/services/index.d.ts.map +1 -0
  223. package/dist/services/index.js +36 -0
  224. package/dist/services/index.js.map +1 -0
  225. package/dist/services/learning-service.d.ts +39 -0
  226. package/dist/services/learning-service.d.ts.map +1 -0
  227. package/dist/services/learning-service.js +151 -0
  228. package/dist/services/learning-service.js.map +1 -0
  229. package/dist/services/migration-service.d.ts +67 -0
  230. package/dist/services/migration-service.d.ts.map +1 -0
  231. package/dist/services/migration-service.js +144 -0
  232. package/dist/services/migration-service.js.map +1 -0
  233. package/dist/services/orchestrator-service.d.ts +52 -0
  234. package/dist/services/orchestrator-service.d.ts.map +1 -0
  235. package/dist/services/orchestrator-service.js +203 -0
  236. package/dist/services/orchestrator-service.js.map +1 -0
  237. package/dist/services/promotion-service.d.ts +67 -0
  238. package/dist/services/promotion-service.d.ts.map +1 -0
  239. package/dist/services/promotion-service.js +151 -0
  240. package/dist/services/promotion-service.js.map +1 -0
  241. package/dist/services/query-expansion-service.d.ts +55 -0
  242. package/dist/services/query-expansion-service.d.ts.map +1 -0
  243. package/dist/services/query-expansion-service.js +174 -0
  244. package/dist/services/query-expansion-service.js.map +1 -0
  245. package/dist/services/ready-service.d.ts +16 -0
  246. package/dist/services/ready-service.d.ts.map +1 -0
  247. package/dist/services/ready-service.js +70 -0
  248. package/dist/services/ready-service.js.map +1 -0
  249. package/dist/services/reranker-service.d.ts +51 -0
  250. package/dist/services/reranker-service.d.ts.map +1 -0
  251. package/dist/services/reranker-service.js +128 -0
  252. package/dist/services/reranker-service.js.map +1 -0
  253. package/dist/services/retriever-service.d.ts +49 -0
  254. package/dist/services/retriever-service.d.ts.map +1 -0
  255. package/dist/services/retriever-service.js +419 -0
  256. package/dist/services/retriever-service.js.map +1 -0
  257. package/dist/services/score-service.d.ts +43 -0
  258. package/dist/services/score-service.d.ts.map +1 -0
  259. package/dist/services/score-service.js +82 -0
  260. package/dist/services/score-service.js.map +1 -0
  261. package/dist/services/swarm-verification.d.ts +104 -0
  262. package/dist/services/swarm-verification.d.ts.map +1 -0
  263. package/dist/services/swarm-verification.js +400 -0
  264. package/dist/services/swarm-verification.js.map +1 -0
  265. package/dist/services/sync-service.d.ts +115 -0
  266. package/dist/services/sync-service.d.ts.map +1 -0
  267. package/dist/services/sync-service.js +350 -0
  268. package/dist/services/sync-service.js.map +1 -0
  269. package/dist/services/task-service.d.ts +22 -0
  270. package/dist/services/task-service.d.ts.map +1 -0
  271. package/dist/services/task-service.js +221 -0
  272. package/dist/services/task-service.js.map +1 -0
  273. package/dist/services/worker-process.d.ts +41 -0
  274. package/dist/services/worker-process.d.ts.map +1 -0
  275. package/dist/services/worker-process.js +280 -0
  276. package/dist/services/worker-process.js.map +1 -0
  277. package/dist/services/worker-service.d.ts +74 -0
  278. package/dist/services/worker-service.d.ts.map +1 -0
  279. package/dist/services/worker-service.js +148 -0
  280. package/dist/services/worker-service.js.map +1 -0
  281. package/dist/utils/glob.d.ts +15 -0
  282. package/dist/utils/glob.d.ts.map +1 -0
  283. package/dist/utils/glob.js +27 -0
  284. package/dist/utils/glob.js.map +1 -0
  285. package/dist/utils/math.d.ts +6 -0
  286. package/dist/utils/math.d.ts.map +1 -0
  287. package/dist/utils/math.js +21 -0
  288. package/dist/utils/math.js.map +1 -0
  289. package/package.json +72 -0
@@ -0,0 +1,522 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ import { DaemonError } from "../errors.js";
7
+ /**
8
+ * Default path for the daemon PID file.
9
+ */
10
+ export const PID_FILE_PATH = ".tx/daemon.pid";
11
+ /**
12
+ * Default install path for the launchd plist file.
13
+ * Located in the user's LaunchAgents directory for per-user daemons.
14
+ */
15
+ export const LAUNCHD_PLIST_PATH = "~/Library/LaunchAgents/com.tx.daemon.plist";
16
+ /**
17
+ * Default install path for the systemd service file.
18
+ * Located in the user's systemd user directory for per-user services.
19
+ */
20
+ export const SYSTEMD_SERVICE_PATH = "~/.config/systemd/user/tx-daemon.service";
21
+ /**
22
+ * Generate a macOS launchd plist file content.
23
+ * Creates a valid XML plist that can be used with launchctl to run the daemon.
24
+ *
25
+ * The generated plist configures the daemon to:
26
+ * - Run at load (start when user logs in)
27
+ * - Keep alive (restart if it crashes)
28
+ * - Log stdout and stderr to the specified log path
29
+ *
30
+ * @param options - Configuration options for the plist
31
+ * @returns The XML content for the launchd plist file
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const plist = generateLaunchdPlist({
36
+ * label: "com.tx.daemon",
37
+ * executablePath: "/usr/local/bin/tx",
38
+ * logPath: "~/Library/Logs/tx-daemon.log"
39
+ * })
40
+ * ```
41
+ */
42
+ export const generateLaunchdPlist = (options) => {
43
+ const { label, executablePath, logPath } = options;
44
+ // Expand ~ to home directory for the log path
45
+ const resolvedLogPath = logPath
46
+ ? logPath.replace(/^~/, homedir())
47
+ : join(homedir(), "Library", "Logs", "tx-daemon.log");
48
+ // Generate valid XML plist
49
+ return `<?xml version="1.0" encoding="UTF-8"?>
50
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
51
+ <plist version="1.0">
52
+ <dict>
53
+ <key>Label</key>
54
+ <string>${escapeXml(label)}</string>
55
+ <key>ProgramArguments</key>
56
+ <array>
57
+ <string>${escapeXml(executablePath)}</string>
58
+ <string>daemon</string>
59
+ <string>run</string>
60
+ </array>
61
+ <key>RunAtLoad</key>
62
+ <true/>
63
+ <key>KeepAlive</key>
64
+ <true/>
65
+ <key>StandardOutPath</key>
66
+ <string>${escapeXml(resolvedLogPath)}</string>
67
+ <key>StandardErrorPath</key>
68
+ <string>${escapeXml(resolvedLogPath)}</string>
69
+ </dict>
70
+ </plist>
71
+ `;
72
+ };
73
+ /**
74
+ * Escape special characters for XML content.
75
+ */
76
+ const escapeXml = (str) => str
77
+ .replace(/&/g, "&amp;")
78
+ .replace(/</g, "&lt;")
79
+ .replace(/>/g, "&gt;")
80
+ .replace(/"/g, "&quot;")
81
+ .replace(/'/g, "&apos;");
82
+ /**
83
+ * Generate a Linux systemd service file content.
84
+ * Creates a valid systemd unit file for a user service.
85
+ *
86
+ * The generated service file configures the daemon to:
87
+ * - Start after the network is available
88
+ * - Run as Type=simple (foreground process)
89
+ * - Restart always on failure with 5 second delay
90
+ * - Be enabled for multi-user target
91
+ *
92
+ * @param options - Configuration options for the service file
93
+ * @returns The content for the systemd service file
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const service = generateSystemdService({
98
+ * executablePath: "/usr/local/bin/tx",
99
+ * user: "myuser"
100
+ * })
101
+ * ```
102
+ */
103
+ export const generateSystemdService = (options) => {
104
+ const { executablePath, user } = options;
105
+ // Build the [Service] section lines
106
+ const serviceLines = [
107
+ "Type=simple",
108
+ `ExecStart=${executablePath} daemon run`,
109
+ "Restart=always",
110
+ "RestartSec=5"
111
+ ];
112
+ // Add User= directive only if user is provided
113
+ if (user) {
114
+ serviceLines.push(`User=${user}`);
115
+ }
116
+ return `[Unit]
117
+ Description=tx Daemon - Task and memory management for AI agents
118
+ After=network.target
119
+
120
+ [Service]
121
+ ${serviceLines.join("\n")}
122
+
123
+ [Install]
124
+ WantedBy=default.target
125
+ `;
126
+ };
127
+ /**
128
+ * DaemonService manages the lifecycle of the tx daemon process.
129
+ * The daemon runs in the background and provides:
130
+ * - File watching for auto-sync
131
+ * - JSONL promotion on git operations
132
+ * - Background task processing
133
+ *
134
+ * See DD-015 and PRD-015 for full specification.
135
+ */
136
+ export class DaemonService extends Context.Tag("DaemonService")() {
137
+ }
138
+ /**
139
+ * Check if a process with the given PID is currently running.
140
+ * Uses kill signal 0 which doesn't actually send a signal but checks if the process exists.
141
+ *
142
+ * @param pid - The process ID to check
143
+ * @returns Effect that resolves to true if the process is running, false otherwise
144
+ */
145
+ export const isProcessRunning = (pid) => Effect.try({
146
+ try: () => {
147
+ // Signal 0 doesn't send anything but checks if process exists
148
+ process.kill(pid, 0);
149
+ return true;
150
+ },
151
+ catch: (error) => error
152
+ }).pipe(Effect.catchAll((error) => {
153
+ // ESRCH means no such process - return false (not running) as success value
154
+ if (error instanceof Error && "code" in error && error.code === "ESRCH") {
155
+ return Effect.succeed(false);
156
+ }
157
+ // EPERM means process exists but we don't have permission to signal it
158
+ // The process is still running, just owned by another user - return true as success value
159
+ if (error instanceof Error && "code" in error && error.code === "EPERM") {
160
+ return Effect.succeed(true);
161
+ }
162
+ // Any other error is unexpected - convert to DaemonError
163
+ return Effect.fail(new DaemonError({
164
+ code: "PROCESS_CHECK_FAILED",
165
+ message: `Failed to check if process ${pid} is running: ${error}`,
166
+ pid
167
+ }));
168
+ }));
169
+ /**
170
+ * Write a PID to the daemon PID file.
171
+ * Creates the .tx directory if it doesn't exist.
172
+ *
173
+ * @param pid - The process ID to write
174
+ * @returns Effect that resolves to void on success
175
+ */
176
+ export const writePid = (pid) => Effect.try({
177
+ try: () => {
178
+ const dir = dirname(PID_FILE_PATH);
179
+ if (!existsSync(dir)) {
180
+ mkdirSync(dir, { recursive: true });
181
+ }
182
+ writeFileSync(PID_FILE_PATH, String(pid), "utf-8");
183
+ },
184
+ catch: (error) => new DaemonError({
185
+ code: "PID_WRITE_FAILED",
186
+ message: `Failed to write PID file: ${error}`,
187
+ pid
188
+ })
189
+ });
190
+ /**
191
+ * Read the PID from the daemon PID file.
192
+ * Returns null if the file doesn't exist.
193
+ * Handles stale PID cleanup: if the PID file exists but the process is not running,
194
+ * the stale PID file is automatically removed and null is returned.
195
+ *
196
+ * @returns Effect that resolves to the PID number, or null if no valid PID file exists
197
+ */
198
+ export const readPid = () => Effect.gen(function* () {
199
+ // Check if file exists
200
+ if (!existsSync(PID_FILE_PATH)) {
201
+ return null;
202
+ }
203
+ // Read the PID file
204
+ const content = yield* Effect.try({
205
+ try: () => readFileSync(PID_FILE_PATH, "utf-8").trim(),
206
+ catch: (error) => new DaemonError({
207
+ code: "PID_READ_FAILED",
208
+ message: `Failed to read PID file: ${error}`,
209
+ pid: null
210
+ })
211
+ });
212
+ // Parse the PID
213
+ const pid = parseInt(content, 10);
214
+ if (isNaN(pid) || pid <= 0) {
215
+ // Invalid PID content, remove the stale file
216
+ yield* removePid();
217
+ return null;
218
+ }
219
+ // Check if the process is still running
220
+ const running = yield* isProcessRunning(pid);
221
+ if (!running) {
222
+ // Process is not running, remove the stale PID file
223
+ yield* removePid();
224
+ return null;
225
+ }
226
+ return pid;
227
+ });
228
+ /**
229
+ * Remove the daemon PID file.
230
+ * Does nothing if the file doesn't exist.
231
+ *
232
+ * @returns Effect that resolves to void on success
233
+ */
234
+ export const removePid = () => Effect.try({
235
+ try: () => {
236
+ if (existsSync(PID_FILE_PATH)) {
237
+ unlinkSync(PID_FILE_PATH);
238
+ }
239
+ },
240
+ catch: (error) => new DaemonError({
241
+ code: "PID_REMOVE_FAILED",
242
+ message: `Failed to remove PID file: ${error}`,
243
+ pid: null
244
+ })
245
+ });
246
+ /**
247
+ * Path to store the daemon start timestamp for uptime calculation.
248
+ */
249
+ const STARTED_AT_PATH = ".tx/daemon.started";
250
+ /**
251
+ * Write the daemon start timestamp to a file.
252
+ */
253
+ const writeStartedAt = (date) => Effect.try({
254
+ try: () => {
255
+ const dir = dirname(STARTED_AT_PATH);
256
+ if (!existsSync(dir)) {
257
+ mkdirSync(dir, { recursive: true });
258
+ }
259
+ writeFileSync(STARTED_AT_PATH, date.toISOString(), "utf-8");
260
+ },
261
+ catch: (error) => new DaemonError({
262
+ code: "STARTED_AT_WRITE_FAILED",
263
+ message: `Failed to write started_at file: ${error}`,
264
+ pid: null
265
+ })
266
+ });
267
+ /**
268
+ * Read the daemon start timestamp from file.
269
+ */
270
+ const readStartedAt = () => Effect.try({
271
+ try: () => {
272
+ if (!existsSync(STARTED_AT_PATH)) {
273
+ return null;
274
+ }
275
+ const content = readFileSync(STARTED_AT_PATH, "utf-8").trim();
276
+ const date = new Date(content);
277
+ return isNaN(date.getTime()) ? null : date;
278
+ },
279
+ catch: (error) => new DaemonError({
280
+ code: "STARTED_AT_READ_FAILED",
281
+ message: `Failed to read started_at file: ${error}`,
282
+ pid: null
283
+ })
284
+ });
285
+ /**
286
+ * Remove the daemon start timestamp file.
287
+ */
288
+ const removeStartedAt = () => Effect.try({
289
+ try: () => {
290
+ if (existsSync(STARTED_AT_PATH)) {
291
+ unlinkSync(STARTED_AT_PATH);
292
+ }
293
+ },
294
+ catch: (error) => new DaemonError({
295
+ code: "STARTED_AT_REMOVE_FAILED",
296
+ message: `Failed to remove started_at file: ${error}`,
297
+ pid: null
298
+ })
299
+ });
300
+ /**
301
+ * Send a signal to a process by PID.
302
+ * Returns true if the signal was sent successfully, false if the process doesn't exist.
303
+ */
304
+ const sendSignal = (pid, signal) => Effect.try({
305
+ try: () => {
306
+ process.kill(pid, signal);
307
+ return true;
308
+ },
309
+ catch: (error) => error
310
+ }).pipe(Effect.catchAll((error) => {
311
+ // ESRCH means no such process - return false (not running) as success value
312
+ if (error instanceof Error && "code" in error && error.code === "ESRCH") {
313
+ return Effect.succeed(false);
314
+ }
315
+ // Any other error is unexpected - convert to DaemonError
316
+ return Effect.fail(new DaemonError({
317
+ code: "SIGNAL_FAILED",
318
+ message: `Failed to send signal ${signal} to process ${pid}: ${error}`,
319
+ pid
320
+ }));
321
+ }));
322
+ /**
323
+ * Wait for a process to exit with a timeout.
324
+ * Returns true if the process exited, false if timeout was reached.
325
+ */
326
+ const waitForExit = (pid, timeoutMs) => Effect.gen(function* () {
327
+ const startTime = Date.now();
328
+ const pollIntervalMs = 100;
329
+ while (Date.now() - startTime < timeoutMs) {
330
+ const running = yield* isProcessRunning(pid);
331
+ if (!running) {
332
+ return true;
333
+ }
334
+ yield* Effect.sleep(`${pollIntervalMs} millis`);
335
+ }
336
+ return false;
337
+ });
338
+ /**
339
+ * Default daemon configuration.
340
+ */
341
+ export const defaultDaemonConfig = {
342
+ command: "tx",
343
+ args: ["daemon", "run"]
344
+ };
345
+ /**
346
+ * Spawn a daemon process and wait for it to either start successfully or fail.
347
+ * This properly handles the async ENOENT error that spawn emits.
348
+ */
349
+ const spawnDaemonProcess = (config) => new Promise((resolve, reject) => {
350
+ const spawnedProcess = spawn(config.command, [...config.args], {
351
+ detached: true,
352
+ stdio: "ignore",
353
+ cwd: config.cwd ?? process.cwd(),
354
+ env: config.env ?? process.env
355
+ });
356
+ // Handle spawn errors (like ENOENT when command doesn't exist)
357
+ spawnedProcess.on("error", (error) => {
358
+ if (error.code === "ENOENT") {
359
+ reject(new DaemonError({
360
+ code: "COMMAND_NOT_FOUND",
361
+ message: `Command '${config.command}' not found. Make sure it is installed and in PATH.`,
362
+ pid: null
363
+ }));
364
+ }
365
+ else {
366
+ reject(new DaemonError({
367
+ code: "SPAWN_FAILED",
368
+ message: `Failed to spawn daemon process: ${error.message}`,
369
+ pid: null
370
+ }));
371
+ }
372
+ });
373
+ // Check if we got a PID (spawn succeeded synchronously)
374
+ if (spawnedProcess.pid !== undefined) {
375
+ // Unref to allow parent to exit independently
376
+ spawnedProcess.unref();
377
+ resolve({ child: spawnedProcess, pid: spawnedProcess.pid });
378
+ }
379
+ else {
380
+ // Wait a tick for the error event to fire
381
+ setImmediate(() => {
382
+ if (spawnedProcess.pid !== undefined) {
383
+ spawnedProcess.unref();
384
+ resolve({ child: spawnedProcess, pid: spawnedProcess.pid });
385
+ }
386
+ // If still no PID, the error handler will reject
387
+ });
388
+ }
389
+ });
390
+ /**
391
+ * Internal start implementation for the daemon.
392
+ * Extracted to allow reuse in restart without circular dependency.
393
+ */
394
+ const startDaemonImpl = (config) => Effect.gen(function* () {
395
+ // Check if daemon is already running
396
+ const existingPid = yield* readPid();
397
+ if (existingPid !== null) {
398
+ return yield* Effect.fail(new DaemonError({
399
+ code: "ALREADY_RUNNING",
400
+ message: `Daemon is already running with PID ${existingPid}`,
401
+ pid: existingPid
402
+ }));
403
+ }
404
+ // Spawn the daemon process with proper error handling
405
+ const { pid } = yield* Effect.tryPromise({
406
+ try: () => spawnDaemonProcess(config),
407
+ catch: (error) => {
408
+ if (error instanceof DaemonError) {
409
+ return error;
410
+ }
411
+ return new DaemonError({
412
+ code: "SPAWN_FAILED",
413
+ message: `Failed to spawn daemon process: ${error}`,
414
+ pid: null
415
+ });
416
+ }
417
+ });
418
+ // Write PID file
419
+ yield* writePid(pid);
420
+ // Write started timestamp
421
+ yield* writeStartedAt(new Date());
422
+ // Wait a short time and verify process is still running
423
+ yield* Effect.sleep("100 millis");
424
+ const running = yield* isProcessRunning(pid);
425
+ if (!running) {
426
+ // Process died immediately, clean up
427
+ yield* removePid();
428
+ yield* removeStartedAt();
429
+ return yield* Effect.fail(new DaemonError({
430
+ code: "PROCESS_DIED",
431
+ message: "Daemon process exited immediately after starting",
432
+ pid
433
+ }));
434
+ }
435
+ });
436
+ /**
437
+ * Internal stop implementation for the daemon.
438
+ * Extracted to allow reuse in restart without circular dependency.
439
+ */
440
+ const stopDaemonImpl = () => Effect.gen(function* () {
441
+ // Read PID from file
442
+ const pid = yield* readPid();
443
+ if (pid === null) {
444
+ // No daemon running (readPid handles stale cleanup)
445
+ return;
446
+ }
447
+ // Send SIGTERM
448
+ const termSent = yield* sendSignal(pid, "SIGTERM");
449
+ if (!termSent) {
450
+ // Process already gone, clean up files
451
+ yield* removePid();
452
+ yield* removeStartedAt();
453
+ return;
454
+ }
455
+ // Wait for process to exit (timeout 10s)
456
+ const exited = yield* waitForExit(pid, 10000);
457
+ if (!exited) {
458
+ // Process didn't exit gracefully, send SIGKILL
459
+ yield* sendSignal(pid, "SIGKILL");
460
+ // Wait a bit more for SIGKILL to take effect
461
+ yield* waitForExit(pid, 1000);
462
+ }
463
+ // Clean up files
464
+ yield* removePid();
465
+ yield* removeStartedAt();
466
+ });
467
+ /**
468
+ * Internal status implementation for the daemon.
469
+ */
470
+ const statusDaemonImpl = () => Effect.gen(function* () {
471
+ const pid = yield* readPid();
472
+ if (pid === null) {
473
+ return {
474
+ running: false,
475
+ pid: null,
476
+ uptime: null,
477
+ startedAt: null
478
+ };
479
+ }
480
+ // Process is running (readPid already verified this)
481
+ const startedAt = yield* readStartedAt();
482
+ const uptime = startedAt ? Date.now() - startedAt.getTime() : null;
483
+ return {
484
+ running: true,
485
+ pid,
486
+ uptime,
487
+ startedAt
488
+ };
489
+ });
490
+ /**
491
+ * DaemonServiceLive implementation that manages a real daemon process.
492
+ * Uses child_process.spawn with detached:true to fork the daemon.
493
+ */
494
+ export const DaemonServiceLive = Layer.succeed(DaemonService, {
495
+ start: () => startDaemonImpl(defaultDaemonConfig),
496
+ stop: () => stopDaemonImpl(),
497
+ status: () => statusDaemonImpl(),
498
+ restart: () => Effect.gen(function* () {
499
+ // Stop if running
500
+ yield* stopDaemonImpl();
501
+ // Small delay before starting again
502
+ yield* Effect.sleep("200 millis");
503
+ // Start fresh
504
+ yield* startDaemonImpl(defaultDaemonConfig);
505
+ })
506
+ });
507
+ /**
508
+ * DaemonServiceNoop - no-op implementation for testing.
509
+ * All operations succeed but don't actually manage a process.
510
+ */
511
+ export const DaemonServiceNoop = Layer.succeed(DaemonService, {
512
+ start: () => Effect.void,
513
+ stop: () => Effect.void,
514
+ status: () => Effect.succeed({
515
+ running: false,
516
+ pid: null,
517
+ uptime: null,
518
+ startedAt: null
519
+ }),
520
+ restart: () => Effect.void
521
+ });
522
+ //# sourceMappingURL=daemon-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-service.js","sourceRoot":"","sources":["../../src/services/daemon-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACxF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,4CAA4C,CAAA;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,0CAA0C,CAAA;AAsB9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAA4B,EAAU,EAAE;IAC3E,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAElD,8CAA8C;IAC9C,MAAM,eAAe,GAAG,OAAO;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAClC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,CAAA;IAEvD,2BAA2B;IAC3B,OAAO;;;;;cAKK,SAAS,CAAC,KAAK,CAAC;;;kBAGZ,SAAS,CAAC,cAAc,CAAC;;;;;;;;;cAS7B,SAAS,CAAC,eAAe,CAAC;;cAE1B,SAAS,CAAC,eAAe,CAAC;;;CAGvC,CAAA;AACD,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE,CACxC,GAAG;KACA,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;KACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;KACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAiB5B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAA8B,EAAU,EAAE;IAC/E,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,OAAO,CAAA;IAExC,oCAAoC;IACpC,MAAM,YAAY,GAAG;QACnB,aAAa;QACb,aAAa,cAAc,aAAa;QACxC,gBAAgB;QAChB,cAAc;KACf,CAAA;IAED,+CAA+C;IAC/C,IAAI,IAAI,EAAE,CAAC;QACT,YAAY,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;IACnC,CAAC;IAED,OAAO;;;;;EAKP,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;;;;CAIxB,CAAA;AACD,CAAC,CAAA;AAYD;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAyB5D;CAAG;AAEN;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAuC,EAAE,CACnF,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,8DAA8D;QAC9D,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACpB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAA8B;CACjD,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;IACxB,4EAA4E;IAC5E,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACxE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IACD,uEAAuE;IACvE,0FAA0F;IAC1F,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACxE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IACD,yDAAyD;IACzD,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,8BAA8B,GAAG,gBAAgB,KAAK,EAAE;QACjE,GAAG;KACJ,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CACH,CAAA;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAoC,EAAE,CACxE,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,MAAM,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;QAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACrC,CAAC;QACD,aAAa,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,6BAA6B,KAAK,EAAE;QAC7C,GAAG;KACJ,CAAC;CACL,CAAC,CAAA;AAEJ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAA8C,EAAE,CACrE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,uBAAuB;IACvB,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,oBAAoB;IACpB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAChC,GAAG,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE;QACtD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;YACd,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,4BAA4B,KAAK,EAAE;YAC5C,GAAG,EAAE,IAAI;SACV,CAAC;KACL,CAAC,CAAA;IAEF,gBAAgB;IAChB,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IACjC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QAC3B,6CAA6C;QAC7C,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;QAClB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,wCAAwC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,oDAAoD;QACpD,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;QAClB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC,CAAC,CAAA;AAEJ;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAqC,EAAE,CAC9D,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9B,UAAU,CAAC,aAAa,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,8BAA8B,KAAK,EAAE;QAC9C,GAAG,EAAE,IAAI;KACV,CAAC;CACL,CAAC,CAAA;AAEJ;;GAEG;AACH,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAE5C;;GAEG;AACH,MAAM,cAAc,GAAG,CAAC,IAAU,EAAoC,EAAE,CACtE,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,MAAM,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;QACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACrC,CAAC;QACD,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAA;IAC7D,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,oCAAoC,KAAK,EAAE;QACpD,GAAG,EAAE,IAAI;KACV,CAAC;CACL,CAAC,CAAA;AAEJ;;GAEG;AACH,MAAM,aAAa,GAAG,GAA4C,EAAE,CAClE,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAA;QACb,CAAC;QACD,MAAM,OAAO,GAAG,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;QAC7D,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5C,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,mCAAmC,KAAK,EAAE;QACnD,GAAG,EAAE,IAAI;KACV,CAAC;CACL,CAAC,CAAA;AAEJ;;GAEG;AACH,MAAM,eAAe,GAAG,GAAqC,EAAE,CAC7D,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YAChC,UAAU,CAAC,eAAe,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,qCAAqC,KAAK,EAAE;QACrD,GAAG,EAAE,IAAI;KACV,CAAC;CACL,CAAC,CAAA;AAEJ;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,MAAsB,EAAuC,EAAE,CAC9F,MAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAA8B;CACjD,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;IACxB,4EAA4E;IAC5E,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACxE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IACD,yDAAyD;IACzD,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;QACd,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,yBAAyB,MAAM,eAAe,GAAG,KAAK,KAAK,EAAE;QACtE,GAAG;KACJ,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CACH,CAAA;AAEH;;;GAGG;AACH,MAAM,WAAW,GAAG,CAAC,GAAW,EAAE,SAAiB,EAAuC,EAAE,CAC1F,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC5B,MAAM,cAAc,GAAG,GAAG,CAAA;IAE1B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAA;QACb,CAAC;QACD,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,SAAS,CAAC,CAAA;IACjD,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAC,CAAA;AAoCJ;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,OAAO,EAAE,IAAI;IACb,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;CACxB,CAAA;AAED;;;GAGG;AACH,MAAM,kBAAkB,GAAG,CAAC,MAAoB,EAAiD,EAAE,CACjG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE;QAC7D,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;QACf,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QAChC,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;KAC/B,CAAC,CAAA;IAEF,+DAA+D;IAC/D,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;QAC1D,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,WAAW,CAAC;gBACrB,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,YAAY,MAAM,CAAC,OAAO,qDAAqD;gBACxF,GAAG,EAAE,IAAI;aACV,CAAC,CAAC,CAAA;QACL,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,WAAW,CAAC;gBACrB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,mCAAmC,KAAK,CAAC,OAAO,EAAE;gBAC3D,GAAG,EAAE,IAAI;aACV,CAAC,CAAC,CAAA;QACL,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,wDAAwD;IACxD,IAAI,cAAc,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QACrC,8CAA8C;QAC9C,cAAc,CAAC,KAAK,EAAE,CAAA;QACtB,OAAO,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,CAAC,GAAG,EAAE,CAAC,CAAA;IAC7D,CAAC;SAAM,CAAC;QACN,0CAA0C;QAC1C,YAAY,CAAC,GAAG,EAAE;YAChB,IAAI,cAAc,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBACrC,cAAc,CAAC,KAAK,EAAE,CAAA;gBACtB,OAAO,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,cAAc,CAAC,GAAG,EAAE,CAAC,CAAA;YAC7D,CAAC;YACD,iDAAiD;QACnD,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ;;;GAGG;AACH,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAoC,EAAE,CACjF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,qCAAqC;IACrC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;IACpC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,WAAW,CAAC;YACd,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,sCAAsC,WAAW,EAAE;YAC5D,GAAG,EAAE,WAAW;SACjB,CAAC,CACH,CAAA;IACH,CAAC;IAED,sDAAsD;IACtD,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACvC,GAAG,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC;QACrC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;YACf,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;gBACjC,OAAO,KAAK,CAAA;YACd,CAAC;YACD,OAAO,IAAI,WAAW,CAAC;gBACrB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,mCAAmC,KAAK,EAAE;gBACnD,GAAG,EAAE,IAAI;aACV,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;IAEF,iBAAiB;IACjB,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IAEpB,0BAA0B;IAC1B,KAAK,CAAC,CAAC,cAAc,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IAEjC,wDAAwD;IACxD,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAE5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,qCAAqC;QACrC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;QAClB,KAAK,CAAC,CAAC,eAAe,EAAE,CAAA;QACxB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,WAAW,CAAC;YACd,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,kDAAkD;YAC3D,GAAG;SACJ,CAAC,CACH,CAAA;IACH,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ;;;GAGG;AACH,MAAM,cAAc,GAAG,GAAqC,EAAE,CAC5D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,qBAAqB;IACrB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;IAE5B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,oDAAoD;QACpD,OAAM;IACR,CAAC;IAED,eAAe;IACf,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;IAElD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,uCAAuC;QACvC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;QAClB,KAAK,CAAC,CAAC,eAAe,EAAE,CAAA;QACxB,OAAM;IACR,CAAC;IAED,yCAAyC;IACzC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAE7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,+CAA+C;QAC/C,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;QAEjC,6CAA6C;QAC7C,KAAK,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED,iBAAiB;IACjB,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;IAClB,KAAK,CAAC,CAAC,eAAe,EAAE,CAAA;AAC1B,CAAC,CAAC,CAAA;AAEJ;;GAEG;AACH,MAAM,gBAAgB,GAAG,GAA6C,EAAE,CACtE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;IAE5B,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI;SACO,CAAA;IAC1B,CAAC;IAED,qDAAqD;IACrD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,aAAa,EAAE,CAAA;IACxC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAElE,OAAO;QACL,OAAO,EAAE,IAAI;QACb,GAAG;QACH,MAAM;QACN,SAAS;KACa,CAAA;AAC1B,CAAC,CAAC,CAAA;AAEJ;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE;IAC5D,KAAK,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,mBAAmB,CAAC;IACjD,IAAI,EAAE,GAAG,EAAE,CAAC,cAAc,EAAE;IAC5B,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE;IAChC,OAAO,EAAE,GAAG,EAAE,CACZ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,kBAAkB;QAClB,KAAK,CAAC,CAAC,cAAc,EAAE,CAAA;QAEvB,oCAAoC;QACpC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAEjC,cAAc;QACd,KAAK,CAAC,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAA;IAC7C,CAAC,CAAC;CACL,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE;IAC5D,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;IACxB,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;IACvB,MAAM,EAAE,GAAG,EAAE,CACX,MAAM,CAAC,OAAO,CAAC;QACb,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,IAAI;QACT,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI;KACO,CAAC;IAC3B,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;CAC3B,CAAC,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { DeduplicationRepository } from "../repo/deduplication-repo.js";
3
+ import { DatabaseError } from "../errors.js";
4
+ import type { FileProgress, LineProcessResult, FileProcessResult, DeduplicationOptions } from "@jamesaphoenix/tx-types";
5
+ declare const DeduplicationService_base: Context.TagClass<DeduplicationService, "DeduplicationService", {
6
+ /**
7
+ * Process a single JSONL line and return deduplication result.
8
+ * Returns isNew=true if this is a newly seen line (hash recorded).
9
+ * Returns isNew=false if we've already processed this content.
10
+ */
11
+ readonly processLine: (content: string, filePath: string, lineNumber: number) => Effect.Effect<LineProcessResult, DatabaseError>;
12
+ /**
13
+ * Process multiple JSONL lines with batch hash checking.
14
+ * More efficient than calling processLine repeatedly.
15
+ */
16
+ readonly processLines: (lines: readonly {
17
+ content: string;
18
+ lineNumber: number;
19
+ }[], filePath: string, options?: DeduplicationOptions) => Effect.Effect<FileProcessResult, DatabaseError>;
20
+ /**
21
+ * Check if a content hash has already been processed.
22
+ */
23
+ readonly isProcessed: (content: string) => Effect.Effect<boolean, DatabaseError>;
24
+ /**
25
+ * Check multiple content strings at once.
26
+ * Returns the set of content strings that have been processed.
27
+ */
28
+ readonly filterProcessed: (contents: readonly string[]) => Effect.Effect<Set<string>, DatabaseError>;
29
+ /**
30
+ * Get processing progress for a file.
31
+ * Returns null if file has never been processed.
32
+ */
33
+ readonly getProgress: (filePath: string) => Effect.Effect<FileProgress | null, DatabaseError>;
34
+ /**
35
+ * Update processing progress for a file.
36
+ */
37
+ readonly updateProgress: (filePath: string, lastLineProcessed: number, lastByteOffset: number, fileSize?: number, fileChecksum?: string) => Effect.Effect<FileProgress, DatabaseError>;
38
+ /**
39
+ * Reset progress for a file (for reprocessing).
40
+ * Also clears all recorded hashes from this file.
41
+ */
42
+ readonly resetFile: (filePath: string) => Effect.Effect<{
43
+ hashesDeleted: number;
44
+ }, DatabaseError>;
45
+ /**
46
+ * Get statistics about processed content.
47
+ */
48
+ readonly getStats: () => Effect.Effect<{
49
+ totalHashes: number;
50
+ trackedFiles: number;
51
+ }, DatabaseError>;
52
+ /**
53
+ * Compute SHA256 hash of content.
54
+ * Exposed for use by callers who need to pre-compute hashes.
55
+ */
56
+ readonly computeHash: (content: string) => string;
57
+ }>;
58
+ /**
59
+ * DeduplicationService handles JSONL line deduplication via SHA256 hashing.
60
+ * Tracks processed content hashes to skip already-seen lines.
61
+ * Supports incremental processing by tracking file progress.
62
+ */
63
+ export declare class DeduplicationService extends DeduplicationService_base {
64
+ }
65
+ export declare const DeduplicationServiceLive: Layer.Layer<DeduplicationService, never, DeduplicationRepository>;
66
+ export {};
67
+ //# sourceMappingURL=deduplication-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deduplication-service.d.ts","sourceRoot":"","sources":["../../src/services/deduplication-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAA;AAEvE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,yBAAyB,CAAA;;IAgB5B;;;;OAIG;0BACmB,CACpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;IAEpD;;;OAGG;2BACoB,CACrB,KAAK,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,EACzD,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;IAEpD;;OAEG;0BACmB,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;IAEhF;;;OAGG;8BACuB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAEpG;;;OAGG;0BACmB,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,EAAE,aAAa,CAAC;IAE7F;;OAEG;6BACsB,CACvB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EACzB,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,KAClB,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC;IAE/C;;;OAGG;wBACiB,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,aAAa,CAAC;IAEjG;;OAEG;uBACgB,MAAM,MAAM,CAAC,MAAM,CAAC;QACrC,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;KACrB,EAAE,aAAa,CAAC;IAEjB;;;OAGG;0BACmB,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM;;AA3ErD;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,yBAwEvC;CAAG;AAEN,eAAO,MAAM,wBAAwB,mEAgKpC,CAAA"}