browser-debugger-cli 0.6.6 → 0.6.7

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 (213) hide show
  1. package/README.md +48 -269
  2. package/dist/commands/__tests__/helpJson.contract.test.d.ts +12 -0
  3. package/dist/commands/__tests__/helpJson.contract.test.d.ts.map +1 -0
  4. package/dist/commands/__tests__/helpJson.contract.test.js +121 -0
  5. package/dist/commands/__tests__/helpJson.contract.test.js.map +1 -0
  6. package/dist/commands/__tests__/machineReadableHelp.contract.test.js +26 -7
  7. package/dist/commands/__tests__/machineReadableHelp.contract.test.js.map +1 -1
  8. package/dist/commands/cdp.d.ts.map +1 -1
  9. package/dist/commands/cdp.js.map +1 -1
  10. package/dist/commands/cleanup.d.ts.map +1 -1
  11. package/dist/commands/cleanup.js +17 -91
  12. package/dist/commands/cleanup.js.map +1 -1
  13. package/dist/commands/console.d.ts.map +1 -1
  14. package/dist/commands/console.js.map +1 -1
  15. package/dist/commands/details.d.ts.map +1 -1
  16. package/dist/commands/details.js +3 -1
  17. package/dist/commands/details.js.map +1 -1
  18. package/dist/commands/dom/DomElementResolver.d.ts +138 -0
  19. package/dist/commands/dom/DomElementResolver.d.ts.map +1 -0
  20. package/dist/commands/dom/DomElementResolver.js +173 -0
  21. package/dist/commands/dom/DomElementResolver.js.map +1 -0
  22. package/dist/commands/dom/a11y.d.ts.map +1 -1
  23. package/dist/commands/dom/a11y.js +4 -28
  24. package/dist/commands/dom/a11y.js.map +1 -1
  25. package/dist/commands/dom/evalHelpers.d.ts.map +1 -1
  26. package/dist/commands/dom/evalHelpers.js +1 -1
  27. package/dist/commands/dom/evalHelpers.js.map +1 -1
  28. package/dist/commands/dom/formFillHelpers.d.ts.map +1 -1
  29. package/dist/commands/dom/formFillHelpers.js +11 -11
  30. package/dist/commands/dom/formFillHelpers.js.map +1 -1
  31. package/dist/commands/dom/formInteraction.d.ts.map +1 -1
  32. package/dist/commands/dom/formInteraction.js +5 -5
  33. package/dist/commands/dom/formInteraction.js.map +1 -1
  34. package/dist/commands/dom/helpers.d.ts +0 -50
  35. package/dist/commands/dom/helpers.d.ts.map +1 -1
  36. package/dist/commands/dom/helpers.js +2 -56
  37. package/dist/commands/dom/helpers.js.map +1 -1
  38. package/dist/commands/dom/index.d.ts.map +1 -1
  39. package/dist/commands/dom/index.js +50 -44
  40. package/dist/commands/dom/index.js.map +1 -1
  41. package/dist/commands/dom/keyMapping.d.ts +0 -6
  42. package/dist/commands/dom/keyMapping.d.ts.map +1 -1
  43. package/dist/commands/dom/keyMapping.js +0 -8
  44. package/dist/commands/dom/keyMapping.js.map +1 -1
  45. package/dist/commands/dom/reactEventHelpers.d.ts +12 -18
  46. package/dist/commands/dom/reactEventHelpers.d.ts.map +1 -1
  47. package/dist/commands/dom/reactEventHelpers.js +21 -70
  48. package/dist/commands/dom/reactEventHelpers.js.map +1 -1
  49. package/dist/commands/helpJson.d.ts +165 -0
  50. package/dist/commands/helpJson.d.ts.map +1 -0
  51. package/dist/commands/helpJson.js +252 -0
  52. package/dist/commands/helpJson.js.map +1 -0
  53. package/dist/commands/machineReadableHelp.d.ts +21 -2
  54. package/dist/commands/machineReadableHelp.d.ts.map +1 -1
  55. package/dist/commands/machineReadableHelp.js +95 -60
  56. package/dist/commands/machineReadableHelp.js.map +1 -1
  57. package/dist/commands/network.d.ts.map +1 -1
  58. package/dist/commands/network.js.map +1 -1
  59. package/dist/commands/peek.d.ts.map +1 -1
  60. package/dist/commands/peek.js +5 -7
  61. package/dist/commands/peek.js.map +1 -1
  62. package/dist/commands/shared/CommandRunner.d.ts +4 -10
  63. package/dist/commands/shared/CommandRunner.d.ts.map +1 -1
  64. package/dist/commands/shared/CommandRunner.js +2 -2
  65. package/dist/commands/shared/CommandRunner.js.map +1 -1
  66. package/dist/commands/shared/commonOptions.d.ts +0 -15
  67. package/dist/commands/shared/commonOptions.d.ts.map +1 -1
  68. package/dist/commands/shared/commonOptions.js +0 -32
  69. package/dist/commands/shared/commonOptions.js.map +1 -1
  70. package/dist/commands/shared/optionTypes.d.ts +237 -0
  71. package/dist/commands/shared/optionTypes.d.ts.map +1 -0
  72. package/dist/commands/shared/optionTypes.js +20 -0
  73. package/dist/commands/shared/optionTypes.js.map +1 -0
  74. package/dist/commands/shared/startHelpers.d.ts.map +1 -1
  75. package/dist/commands/shared/startHelpers.js +3 -3
  76. package/dist/commands/shared/startHelpers.js.map +1 -1
  77. package/dist/commands/start.d.ts.map +1 -1
  78. package/dist/commands/start.js +8 -6
  79. package/dist/commands/start.js.map +1 -1
  80. package/dist/commands/status.d.ts.map +1 -1
  81. package/dist/commands/status.js.map +1 -1
  82. package/dist/commands/stop.d.ts.map +1 -1
  83. package/dist/commands/stop.js +17 -43
  84. package/dist/commands/stop.js.map +1 -1
  85. package/dist/commands/tail.d.ts.map +1 -1
  86. package/dist/commands/tail.js +49 -59
  87. package/dist/commands/tail.js.map +1 -1
  88. package/dist/connection/types.d.ts +2 -1
  89. package/dist/connection/types.d.ts.map +1 -1
  90. package/dist/constants.d.ts +14 -0
  91. package/dist/constants.d.ts.map +1 -1
  92. package/dist/constants.js +39 -0
  93. package/dist/constants.js.map +1 -1
  94. package/dist/daemon/handlers/responseHandler.d.ts.map +1 -1
  95. package/dist/daemon/handlers/responseHandler.js +1 -0
  96. package/dist/daemon/handlers/responseHandler.js.map +1 -1
  97. package/dist/daemon/ipcServer.js +8 -8
  98. package/dist/daemon/ipcServer.js.map +1 -1
  99. package/dist/daemon/lifecycle/signalHandlers.d.ts.map +1 -1
  100. package/dist/daemon/lifecycle/signalHandlers.js +19 -3
  101. package/dist/daemon/lifecycle/signalHandlers.js.map +1 -1
  102. package/dist/daemon/lifecycle/workerCleanup.d.ts.map +1 -1
  103. package/dist/daemon/lifecycle/workerCleanup.js +5 -2
  104. package/dist/daemon/lifecycle/workerCleanup.js.map +1 -1
  105. package/dist/daemon/patternDefinitions.d.ts +0 -13
  106. package/dist/daemon/patternDefinitions.d.ts.map +1 -1
  107. package/dist/daemon/patternDefinitions.js +0 -17
  108. package/dist/daemon/patternDefinitions.js.map +1 -1
  109. package/dist/daemon/worker/commandRegistry.d.ts.map +1 -1
  110. package/dist/daemon/worker/commandRegistry.js +1 -0
  111. package/dist/daemon/worker/commandRegistry.js.map +1 -1
  112. package/dist/index.js +34 -2
  113. package/dist/index.js.map +1 -1
  114. package/dist/ipc/protocol/commands.d.ts +2 -0
  115. package/dist/ipc/protocol/commands.d.ts.map +1 -1
  116. package/dist/ipc/protocol/commands.js.map +1 -1
  117. package/dist/ipc/session/queries.d.ts +2 -0
  118. package/dist/ipc/session/queries.d.ts.map +1 -1
  119. package/dist/ipc/utils/responseValidator.d.ts +7 -1
  120. package/dist/ipc/utils/responseValidator.d.ts.map +1 -1
  121. package/dist/ipc/utils/responseValidator.js +6 -1
  122. package/dist/ipc/utils/responseValidator.js.map +1 -1
  123. package/dist/session/QueryCacheManager.d.ts +140 -0
  124. package/dist/session/QueryCacheManager.d.ts.map +1 -0
  125. package/dist/session/QueryCacheManager.js +233 -0
  126. package/dist/session/QueryCacheManager.js.map +1 -0
  127. package/dist/session/cleanup.d.ts +63 -1
  128. package/dist/session/cleanup.d.ts.map +1 -1
  129. package/dist/session/cleanup.js +191 -65
  130. package/dist/session/cleanup.js.map +1 -1
  131. package/dist/session/queryCache.d.ts +25 -14
  132. package/dist/session/queryCache.d.ts.map +1 -1
  133. package/dist/session/queryCache.js +31 -60
  134. package/dist/session/queryCache.js.map +1 -1
  135. package/dist/telemetry/__tests__/roleInference.unit.test.d.ts +9 -0
  136. package/dist/telemetry/__tests__/roleInference.unit.test.d.ts.map +1 -0
  137. package/dist/telemetry/__tests__/roleInference.unit.test.js +138 -0
  138. package/dist/telemetry/__tests__/roleInference.unit.test.js.map +1 -0
  139. package/dist/telemetry/dom.d.ts +1 -0
  140. package/dist/telemetry/dom.d.ts.map +1 -1
  141. package/dist/telemetry/dom.js +9 -4
  142. package/dist/telemetry/dom.js.map +1 -1
  143. package/dist/telemetry/filters.d.ts +3 -0
  144. package/dist/telemetry/filters.d.ts.map +1 -1
  145. package/dist/telemetry/filters.js +25 -3
  146. package/dist/telemetry/filters.js.map +1 -1
  147. package/dist/telemetry/network.d.ts.map +1 -1
  148. package/dist/telemetry/network.js +35 -6
  149. package/dist/telemetry/network.js.map +1 -1
  150. package/dist/telemetry/roleInference.d.ts +50 -0
  151. package/dist/telemetry/roleInference.d.ts.map +1 -0
  152. package/dist/telemetry/roleInference.js +163 -0
  153. package/dist/telemetry/roleInference.js.map +1 -0
  154. package/dist/telemetry/utils.d.ts +0 -57
  155. package/dist/telemetry/utils.d.ts.map +1 -1
  156. package/dist/telemetry/utils.js +0 -71
  157. package/dist/telemetry/utils.js.map +1 -1
  158. package/dist/types/dom.d.ts +2 -0
  159. package/dist/types/dom.d.ts.map +1 -1
  160. package/dist/types.d.ts +2 -0
  161. package/dist/types.d.ts.map +1 -1
  162. package/dist/ui/formatters/a11y.d.ts +0 -9
  163. package/dist/ui/formatters/a11y.d.ts.map +1 -1
  164. package/dist/ui/formatters/a11y.js +0 -45
  165. package/dist/ui/formatters/a11y.js.map +1 -1
  166. package/dist/ui/formatters/preview.d.ts.map +1 -1
  167. package/dist/ui/formatters/preview.js +2 -39
  168. package/dist/ui/formatters/preview.js.map +1 -1
  169. package/dist/ui/logging/index.d.ts +1 -1
  170. package/dist/ui/logging/index.d.ts.map +1 -1
  171. package/dist/ui/logging/index.js +1 -1
  172. package/dist/ui/logging/index.js.map +1 -1
  173. package/dist/ui/logging/logger.d.ts +21 -0
  174. package/dist/ui/logging/logger.d.ts.map +1 -1
  175. package/dist/ui/logging/logger.js +24 -6
  176. package/dist/ui/logging/logger.js.map +1 -1
  177. package/dist/ui/messages/commands.d.ts +0 -14
  178. package/dist/ui/messages/commands.d.ts.map +1 -1
  179. package/dist/ui/messages/commands.js +0 -18
  180. package/dist/ui/messages/commands.js.map +1 -1
  181. package/dist/ui/messages/errors.d.ts +0 -16
  182. package/dist/ui/messages/errors.d.ts.map +1 -1
  183. package/dist/ui/messages/errors.js +0 -18
  184. package/dist/ui/messages/errors.js.map +1 -1
  185. package/dist/ui/messages/hints.d.ts +0 -10
  186. package/dist/ui/messages/hints.d.ts.map +1 -1
  187. package/dist/ui/messages/hints.js +0 -12
  188. package/dist/ui/messages/hints.js.map +1 -1
  189. package/dist/utils/decisionTrees.d.ts +0 -17
  190. package/dist/utils/decisionTrees.d.ts.map +1 -1
  191. package/dist/utils/decisionTrees.js +0 -21
  192. package/dist/utils/decisionTrees.js.map +1 -1
  193. package/dist/utils/errorMapping.d.ts +60 -0
  194. package/dist/utils/errorMapping.d.ts.map +1 -0
  195. package/dist/utils/errorMapping.js +97 -0
  196. package/dist/utils/errorMapping.js.map +1 -0
  197. package/dist/utils/file.d.ts +27 -0
  198. package/dist/utils/file.d.ts.map +1 -0
  199. package/dist/utils/file.js +37 -0
  200. package/dist/utils/file.js.map +1 -0
  201. package/dist/utils/levenshtein.d.ts +0 -17
  202. package/dist/utils/levenshtein.d.ts.map +1 -1
  203. package/dist/utils/levenshtein.js +0 -31
  204. package/dist/utils/levenshtein.js.map +1 -1
  205. package/dist/utils/taskMappings.d.ts +0 -17
  206. package/dist/utils/taskMappings.d.ts.map +1 -1
  207. package/dist/utils/taskMappings.js +0 -21
  208. package/dist/utils/taskMappings.js.map +1 -1
  209. package/dist/utils/typeGuards.d.ts +62 -0
  210. package/dist/utils/typeGuards.d.ts.map +1 -0
  211. package/dist/utils/typeGuards.js +94 -0
  212. package/dist/utils/typeGuards.js.map +1 -0
  213. package/package.json +1 -1
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Query cache manager for DOM element index-based access.
3
+ *
4
+ * Provides centralized management of DOM query result caching with:
5
+ * - Singleton pattern for consistent cache access
6
+ * - Navigation-aware staleness detection
7
+ * - TTL-based navigation ID caching to reduce IPC calls
8
+ *
9
+ * The cache enables index-based element access patterns like
10
+ * "bdg dom get 0" after running "bdg dom query .selector".
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const manager = QueryCacheManager.getInstance();
15
+ *
16
+ * // Store query results
17
+ * await manager.set(queryResult);
18
+ *
19
+ * // Validate and retrieve (throws if stale)
20
+ * const validation = await manager.validate();
21
+ * if (validation.valid) {
22
+ * const cache = validation.cache;
23
+ * }
24
+ * ```
25
+ */
26
+ import { existsSync } from 'fs';
27
+ import { readFile, rm, writeFile } from 'fs/promises';
28
+ import { join } from 'path';
29
+ import { getErrorMessage } from '../connection/errors.js';
30
+ import { getSessionDir } from '../session/paths.js';
31
+ import { createLogger } from '../ui/logging/index.js';
32
+ const log = createLogger('session');
33
+ /** TTL for cached navigation ID (500ms). */
34
+ const NAVIGATION_ID_CACHE_TTL_MS = 500;
35
+ /**
36
+ * Singleton manager for DOM query result caching.
37
+ *
38
+ * Centralizes all cache operations with navigation-aware staleness detection.
39
+ * Uses file-based persistence for cross-process access.
40
+ */
41
+ export class QueryCacheManager {
42
+ static instance = null;
43
+ /** Cached navigation ID with timestamp for TTL-based invalidation. */
44
+ cachedNavigationId = null;
45
+ /**
46
+ * Get the singleton instance.
47
+ *
48
+ * @returns QueryCacheManager instance
49
+ */
50
+ static getInstance() {
51
+ QueryCacheManager.instance ??= new QueryCacheManager();
52
+ return QueryCacheManager.instance;
53
+ }
54
+ /**
55
+ * Reset the singleton instance (for testing).
56
+ */
57
+ static resetInstance() {
58
+ QueryCacheManager.instance = null;
59
+ }
60
+ /**
61
+ * Get path to query cache file.
62
+ *
63
+ * @returns Absolute path to query-cache.json
64
+ */
65
+ getCachePath() {
66
+ return join(getSessionDir(), 'query-cache.json');
67
+ }
68
+ /**
69
+ * Store query results for index-based access.
70
+ *
71
+ * Writes results to ~/.bdg/query-cache.json for cross-process access.
72
+ *
73
+ * @param result - DOM query result to cache
74
+ */
75
+ async set(result) {
76
+ try {
77
+ const cachePath = this.getCachePath();
78
+ await writeFile(cachePath, JSON.stringify(result), 'utf-8');
79
+ log.debug(`Cached ${result.nodes.length} query results to ${cachePath}`);
80
+ }
81
+ catch (error) {
82
+ log.debug(`Failed to write query cache: ${getErrorMessage(error)}`);
83
+ }
84
+ }
85
+ /**
86
+ * Get validated cache results.
87
+ *
88
+ * Returns null if cache is stale or doesn't exist.
89
+ * Use getRaw() for unchecked access.
90
+ *
91
+ * @returns Cached query result or null if invalid/missing
92
+ */
93
+ async get() {
94
+ const validation = await this.validate();
95
+ return validation.valid ? validation.cache : null;
96
+ }
97
+ /**
98
+ * Get raw cache without validation.
99
+ *
100
+ * Reads from ~/.bdg/query-cache.json if it exists.
101
+ * Does not check navigation staleness.
102
+ *
103
+ * @returns Cached query result or null if no cache exists
104
+ */
105
+ async getRaw() {
106
+ try {
107
+ const cachePath = this.getCachePath();
108
+ if (!existsSync(cachePath)) {
109
+ return null;
110
+ }
111
+ const content = await readFile(cachePath, 'utf-8');
112
+ const result = JSON.parse(content);
113
+ log.debug(`Retrieved ${result.nodes.length} cached query results`);
114
+ return result;
115
+ }
116
+ catch (error) {
117
+ log.debug(`Failed to read query cache: ${getErrorMessage(error)}`);
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Validate cache against current navigation state.
123
+ *
124
+ * Checks if the cached query results are still valid by comparing
125
+ * the stored navigationId with the current one from the daemon.
126
+ *
127
+ * @returns Validation result with cache and error info
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const validation = await manager.validate();
132
+ * if (!validation.valid) {
133
+ * throw new CommandError(validation.error, { suggestion: validation.suggestion });
134
+ * }
135
+ * const cache = validation.cache;
136
+ * ```
137
+ */
138
+ async validate() {
139
+ const cache = await this.getRaw();
140
+ if (!cache) {
141
+ return {
142
+ valid: false,
143
+ cache: null,
144
+ error: 'No cached query results found',
145
+ suggestion: 'Run "bdg dom query <selector>" first to generate indexed results',
146
+ };
147
+ }
148
+ if (cache.navigationId === undefined) {
149
+ log.debug('Query cache missing navigationId (legacy format), allowing access');
150
+ return { valid: true, cache };
151
+ }
152
+ const currentNavId = await this.getCurrentNavigationId();
153
+ if (currentNavId === null) {
154
+ log.debug('Could not get current navigationId, allowing cache access');
155
+ return { valid: true, cache };
156
+ }
157
+ if (cache.navigationId !== currentNavId) {
158
+ return {
159
+ valid: false,
160
+ cache,
161
+ error: `Query cache is stale (page has navigated since query was run)`,
162
+ suggestion: `Re-run "bdg dom query ${cache.selector}" to refresh cached results`,
163
+ };
164
+ }
165
+ return { valid: true, cache };
166
+ }
167
+ /**
168
+ * Clear the query cache.
169
+ *
170
+ * Removes ~/.bdg/query-cache.json.
171
+ * Called when starting a new query or when the session ends.
172
+ */
173
+ async clear() {
174
+ try {
175
+ const cachePath = this.getCachePath();
176
+ if (existsSync(cachePath)) {
177
+ await rm(cachePath, { force: true });
178
+ log.debug('Cleared query cache');
179
+ }
180
+ }
181
+ catch (error) {
182
+ log.debug(`Failed to clear query cache: ${getErrorMessage(error)}`);
183
+ }
184
+ }
185
+ /**
186
+ * Check if cache file exists.
187
+ *
188
+ * @returns True if cache file exists
189
+ */
190
+ exists() {
191
+ return existsSync(this.getCachePath());
192
+ }
193
+ /**
194
+ * Get current navigation ID from the daemon.
195
+ *
196
+ * Caches the result for 500ms to avoid redundant IPC calls within a single
197
+ * command execution while ensuring freshness for subsequent commands.
198
+ *
199
+ * @returns Current navigation ID or null if unavailable
200
+ */
201
+ async getCurrentNavigationId() {
202
+ if (this.cachedNavigationId &&
203
+ Date.now() - this.cachedNavigationId.timestamp < NAVIGATION_ID_CACHE_TTL_MS) {
204
+ return this.cachedNavigationId.value;
205
+ }
206
+ try {
207
+ const { getStatus } = await import('../ipc/client.js');
208
+ const response = await getStatus();
209
+ if (response.status === 'ok' && response.data?.navigationId !== undefined) {
210
+ this.cachedNavigationId = {
211
+ value: response.data.navigationId,
212
+ timestamp: Date.now(),
213
+ };
214
+ return response.data.navigationId;
215
+ }
216
+ return null;
217
+ }
218
+ catch (error) {
219
+ log.debug(`Failed to get current navigation ID: ${getErrorMessage(error)}`);
220
+ return null;
221
+ }
222
+ }
223
+ /**
224
+ * Invalidate cached navigation ID.
225
+ *
226
+ * Forces fresh fetch on next getCurrentNavigationId() call.
227
+ * Useful after navigation events.
228
+ */
229
+ invalidateNavigationCache() {
230
+ this.cachedNavigationId = null;
231
+ }
232
+ }
233
+ //# sourceMappingURL=QueryCacheManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryCacheManager.js","sourceRoot":"","sources":["../../src/session/QueryCacheManager.ts"],"names":[],"mappings":"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"}
@@ -75,5 +75,67 @@ export declare function cleanupStaleDaemonPid(): boolean;
75
75
  * }
76
76
  * ```
77
77
  */
78
- export declare function cleanupOrphanedDaemons(): number;
78
+ export declare function cleanupOrphanedDaemons(): Promise<number>;
79
+ /**
80
+ * Options for unified session cleanup.
81
+ */
82
+ export interface SessionCleanupOptions {
83
+ /** Kill the associated Chrome process. */
84
+ killChrome?: boolean | undefined;
85
+ /** Force cleanup even if session appears active. */
86
+ force?: boolean | undefined;
87
+ /** Aggressively kill all orphaned processes. */
88
+ aggressive?: boolean | undefined;
89
+ /** Also remove the session.json output file. */
90
+ removeOutput?: boolean | undefined;
91
+ /** Chrome PID from daemon response (for stop command). */
92
+ chromePid?: number | undefined;
93
+ }
94
+ /**
95
+ * Result of session cleanup operation.
96
+ */
97
+ export interface SessionCleanupResult {
98
+ /** What was cleaned up. */
99
+ cleaned: {
100
+ /** Session files (PID, metadata, socket, lock). */
101
+ session: boolean;
102
+ /** Chrome browser process. */
103
+ chrome: boolean;
104
+ /** Orphaned daemon processes. */
105
+ daemons: boolean;
106
+ /** Session output file (session.json). */
107
+ output: boolean;
108
+ };
109
+ /** Number of orphaned daemons killed. */
110
+ orphanedDaemonsCount: number;
111
+ /** Warnings encountered during cleanup. */
112
+ warnings: string[];
113
+ }
114
+ /**
115
+ * Unified session cleanup for stop and cleanup commands.
116
+ *
117
+ * Consolidates cleanup logic from stop.ts and cleanup.ts into a single
118
+ * reusable function. Handles Chrome processes, daemon PIDs, session files,
119
+ * and orphaned processes.
120
+ *
121
+ * @param options - Cleanup options
122
+ * @returns Cleanup result with what was cleaned and any warnings
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // From stop command
127
+ * const result = await performSessionCleanup({
128
+ * killChrome: opts.killChrome,
129
+ * chromePid: response.chromePid,
130
+ * });
131
+ *
132
+ * // From cleanup command
133
+ * const result = await performSessionCleanup({
134
+ * force: true,
135
+ * aggressive: true,
136
+ * removeOutput: true,
137
+ * });
138
+ * ```
139
+ */
140
+ export declare function performSessionCleanup(options: SessionCleanupOptions): Promise<SessionCleanupResult>;
79
141
  //# sourceMappingURL=cleanup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/session/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2GH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CA0F7C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CA6BrC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAoD/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAuB/C"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/session/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgHH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAmE7C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAcrC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAoD/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAuB9D;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,gDAAgD;IAChD,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2BAA2B;IAC3B,OAAO,EAAE;QACP,mDAAmD;QACnD,OAAO,EAAE,OAAO,CAAC;QACjB,8BAA8B;QAC9B,MAAM,EAAE,OAAO,CAAC;QAChB,iCAAiC;QACjC,OAAO,EAAE,OAAO,CAAC;QACjB,0CAA0C;QAC1C,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,yCAAyC;IACzC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAyHD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAkD/B"}
@@ -4,16 +4,20 @@
4
4
  * Handles cleanup of session files (PID, metadata, lock, socket).
5
5
  * WHY: Centralized cleanup logic ensures consistent cleanup across error paths and normal shutdown.
6
6
  */
7
- import { execSync } from 'child_process';
7
+ import { exec } from 'child_process';
8
8
  import * as fs from 'fs';
9
+ import { promisify } from 'util';
10
+ const execAsync = promisify(exec);
9
11
  import { getErrorMessage } from '../connection/errors.js';
10
- import { createLogger } from '../ui/logging/index.js';
12
+ import { createLogger, logDebugError } from '../ui/logging/index.js';
13
+ import { safeRemoveFile } from '../utils/file.js';
11
14
  import { isProcessAlive, killChromeProcess } from '../utils/process.js';
15
+ import { QueryCacheManager } from './QueryCacheManager.js';
12
16
  import { readChromePid, clearChromePid } from './chrome.js';
13
17
  import { acquireSessionLock, releaseSessionLock } from './lock.js';
14
18
  import { getSessionFilePath, ensureSessionDir } from './paths.js';
15
19
  import { readPid, cleanupPidFile, readPidFromFile } from './pid.js';
16
- import { clearSessionQueryCache } from './queryCache.js';
20
+ // Note: QueryCacheManager must be after chrome.js per alphabetical order
17
21
  const log = createLogger('cleanup');
18
22
  function killOrphanedWorker(pid) {
19
23
  try {
@@ -52,7 +56,7 @@ function killCachedChromeProcess(reason) {
52
56
  *
53
57
  * @returns Array of orphaned daemon PIDs
54
58
  */
55
- function findOrphanedDaemons() {
59
+ async function findOrphanedDaemons() {
56
60
  const orphanedPids = [];
57
61
  try {
58
62
  const daemonPidPath = getSessionFilePath('DAEMON_PID');
@@ -60,7 +64,7 @@ function findOrphanedDaemons() {
60
64
  const psCommand = process.platform === 'win32'
61
65
  ? 'wmic process where "commandline like \'%dist/daemon.js%\'" get processid'
62
66
  : 'ps aux | grep -E "node.*dist/daemon\\.js" | grep -v grep';
63
- const output = execSync(psCommand, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
67
+ const { stdout: output } = await execAsync(psCommand);
64
68
  const lines = output.trim().split('\n');
65
69
  for (const line of lines) {
66
70
  let pid;
@@ -90,7 +94,7 @@ function findOrphanedDaemons() {
90
94
  }
91
95
  }
92
96
  catch (error) {
93
- log.debug(`Failed to find orphaned daemons: ${getErrorMessage(error)}`);
97
+ logDebugError(log, 'find orphaned daemons', error);
94
98
  }
95
99
  return orphanedPids;
96
100
  }
@@ -127,14 +131,9 @@ export function cleanupStaleSession() {
127
131
  }
128
132
  }
129
133
  catch (error) {
130
- log.debug(`Failed to read stale session lock: ${getErrorMessage(error)}`);
131
- }
132
- try {
133
- fs.rmSync(lockPath, { force: true });
134
- }
135
- catch (error) {
136
- log.debug(`Failed to remove stale session lock: ${getErrorMessage(error)}`);
134
+ logDebugError(log, 'read stale session lock', error);
137
135
  }
136
+ safeRemoveFile(lockPath, 'stale session lock', log);
138
137
  if (!acquireSessionLock()) {
139
138
  return false;
140
139
  }
@@ -160,30 +159,10 @@ export function cleanupStaleSession() {
160
159
  log.info('Removing stale session files...');
161
160
  killCachedChromeProcess('stale session cleanup');
162
161
  cleanupPidFile();
163
- try {
164
- fs.rmSync(getSessionFilePath('METADATA'), { force: true });
165
- }
166
- catch (error) {
167
- log.debug(`Failed to remove metadata file: ${getErrorMessage(error)}`);
168
- }
169
- try {
170
- fs.rmSync(daemonPidPath, { force: true });
171
- }
172
- catch (error) {
173
- log.debug(`Failed to remove daemon PID file: ${getErrorMessage(error)}`);
174
- }
175
- try {
176
- fs.rmSync(getSessionFilePath('DAEMON_SOCKET'), { force: true });
177
- }
178
- catch (error) {
179
- log.debug(`Failed to remove daemon socket: ${getErrorMessage(error)}`);
180
- }
181
- try {
182
- fs.rmSync(getSessionFilePath('DAEMON_LOCK'), { force: true });
183
- }
184
- catch (error) {
185
- log.debug(`Failed to remove daemon lock: ${getErrorMessage(error)}`);
186
- }
162
+ safeRemoveFile(getSessionFilePath('METADATA'), 'metadata file', log);
163
+ safeRemoveFile(daemonPidPath, 'daemon PID file', log);
164
+ safeRemoveFile(getSessionFilePath('DAEMON_SOCKET'), 'daemon socket', log);
165
+ safeRemoveFile(getSessionFilePath('DAEMON_LOCK'), 'daemon lock', log);
187
166
  log.info('Stale session cleanup complete');
188
167
  return true;
189
168
  }
@@ -215,31 +194,15 @@ export function cleanupStaleSession() {
215
194
  export function cleanupSession() {
216
195
  cleanupPidFile();
217
196
  releaseSessionLock();
218
- try {
219
- fs.rmSync(getSessionFilePath('METADATA'), { force: true });
220
- }
221
- catch (error) {
222
- log.debug(`Failed to remove metadata file: ${getErrorMessage(error)}`);
223
- }
224
- try {
225
- fs.rmSync(getSessionFilePath('DAEMON_PID'), { force: true });
226
- }
227
- catch (error) {
228
- log.debug(`Failed to remove daemon PID file: ${getErrorMessage(error)}`);
229
- }
230
- try {
231
- fs.rmSync(getSessionFilePath('DAEMON_SOCKET'), { force: true });
232
- }
233
- catch (error) {
234
- log.debug(`Failed to remove daemon socket: ${getErrorMessage(error)}`);
235
- }
236
- try {
237
- fs.rmSync(getSessionFilePath('DAEMON_LOCK'), { force: true });
238
- }
239
- catch (error) {
240
- log.debug(`Failed to remove daemon lock: ${getErrorMessage(error)}`);
241
- }
242
- clearSessionQueryCache();
197
+ safeRemoveFile(getSessionFilePath('METADATA'), 'metadata file', log);
198
+ safeRemoveFile(getSessionFilePath('DAEMON_PID'), 'daemon PID file', log);
199
+ safeRemoveFile(getSessionFilePath('DAEMON_SOCKET'), 'daemon socket', log);
200
+ safeRemoveFile(getSessionFilePath('DAEMON_LOCK'), 'daemon lock', log);
201
+ void QueryCacheManager.getInstance()
202
+ .clear()
203
+ .catch((error) => {
204
+ logDebugError(log, 'clear query cache', error);
205
+ });
243
206
  }
244
207
  /**
245
208
  * Cleanup stale daemon PID file if daemon process is dead.
@@ -319,8 +282,8 @@ export function cleanupStaleDaemonPid() {
319
282
  * }
320
283
  * ```
321
284
  */
322
- export function cleanupOrphanedDaemons() {
323
- const orphanedPids = findOrphanedDaemons();
285
+ export async function cleanupOrphanedDaemons() {
286
+ const orphanedPids = await findOrphanedDaemons();
324
287
  if (orphanedPids.length === 0) {
325
288
  log.debug('No orphaned daemon processes found');
326
289
  return 0;
@@ -334,9 +297,172 @@ export function cleanupOrphanedDaemons() {
334
297
  killedCount++;
335
298
  }
336
299
  catch (error) {
337
- log.debug(`Failed to kill orphaned daemon ${pid}: ${getErrorMessage(error)}`);
300
+ logDebugError(log, `kill orphaned daemon ${pid}`, error);
338
301
  }
339
302
  }
340
303
  return killedCount;
341
304
  }
305
+ function createCleanupState() {
306
+ return {
307
+ warnings: [],
308
+ sessionCleaned: false,
309
+ chromeCleaned: false,
310
+ daemonsCleaned: false,
311
+ outputCleaned: false,
312
+ orphanedDaemonsCount: 0,
313
+ };
314
+ }
315
+ /**
316
+ * Kill a specific Chrome process by PID.
317
+ */
318
+ function cleanupChromeProcess(chromePid, clearChromePid, state) {
319
+ try {
320
+ killChromeProcess(chromePid, 'SIGTERM');
321
+ state.chromeCleaned = true;
322
+ clearChromePid();
323
+ }
324
+ catch (error) {
325
+ state.warnings.push(`Could not kill Chrome: ${getErrorMessage(error)}`);
326
+ }
327
+ }
328
+ /**
329
+ * Clean up stale daemon PID file and update state.
330
+ */
331
+ function cleanupDaemonPidIfStale(state) {
332
+ const daemonPidPath = getSessionFilePath('DAEMON_PID');
333
+ if (!fs.existsSync(daemonPidPath)) {
334
+ return;
335
+ }
336
+ try {
337
+ const daemonPidStr = fs.readFileSync(daemonPidPath, 'utf-8').trim();
338
+ const daemonPid = parseInt(daemonPidStr, 10);
339
+ if (Number.isNaN(daemonPid) || !isProcessAlive(daemonPid)) {
340
+ log.info(`Removing stale daemon PID file (PID ${daemonPid})`);
341
+ fs.unlinkSync(daemonPidPath);
342
+ state.sessionCleaned = true;
343
+ }
344
+ }
345
+ catch {
346
+ try {
347
+ fs.unlinkSync(daemonPidPath);
348
+ state.sessionCleaned = true;
349
+ }
350
+ catch (removeError) {
351
+ state.warnings.push(`Could not remove daemon.pid: ${getErrorMessage(removeError)}`);
352
+ }
353
+ }
354
+ }
355
+ /**
356
+ * Clean up active session, optionally forcing cleanup if still running.
357
+ */
358
+ async function cleanupActiveSession(force, cleanupStaleChrome, state) {
359
+ const sessionPid = readPid();
360
+ if (!sessionPid) {
361
+ return;
362
+ }
363
+ const isAlive = isProcessAlive(sessionPid);
364
+ if (isAlive && !force) {
365
+ return;
366
+ }
367
+ if (isAlive && force) {
368
+ state.warnings.push(`Process ${sessionPid} is still running but forcing cleanup anyway`);
369
+ log.info(`Force cleanup: killing Chrome for active session ${sessionPid}`);
370
+ try {
371
+ await cleanupStaleChrome();
372
+ state.chromeCleaned = true;
373
+ }
374
+ catch (error) {
375
+ state.warnings.push(`Could not kill Chrome processes: ${getErrorMessage(error)}`);
376
+ }
377
+ }
378
+ cleanupSession();
379
+ state.sessionCleaned = true;
380
+ }
381
+ /**
382
+ * Clean up session output file.
383
+ */
384
+ function cleanupOutputFile(state) {
385
+ const outputPath = getSessionFilePath('OUTPUT');
386
+ if (!fs.existsSync(outputPath)) {
387
+ return;
388
+ }
389
+ try {
390
+ fs.unlinkSync(outputPath);
391
+ state.outputCleaned = true;
392
+ }
393
+ catch (error) {
394
+ state.warnings.push(`Could not remove session.json: ${getErrorMessage(error)}`);
395
+ }
396
+ }
397
+ /**
398
+ * Unified session cleanup for stop and cleanup commands.
399
+ *
400
+ * Consolidates cleanup logic from stop.ts and cleanup.ts into a single
401
+ * reusable function. Handles Chrome processes, daemon PIDs, session files,
402
+ * and orphaned processes.
403
+ *
404
+ * @param options - Cleanup options
405
+ * @returns Cleanup result with what was cleaned and any warnings
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * // From stop command
410
+ * const result = await performSessionCleanup({
411
+ * killChrome: opts.killChrome,
412
+ * chromePid: response.chromePid,
413
+ * });
414
+ *
415
+ * // From cleanup command
416
+ * const result = await performSessionCleanup({
417
+ * force: true,
418
+ * aggressive: true,
419
+ * removeOutput: true,
420
+ * });
421
+ * ```
422
+ */
423
+ export async function performSessionCleanup(options) {
424
+ const { cleanupStaleChrome, clearChromePid } = await import('./chrome.js');
425
+ const state = createCleanupState();
426
+ if (options.aggressive) {
427
+ const daemonsKilled = await cleanupOrphanedDaemons();
428
+ if (daemonsKilled > 0) {
429
+ state.daemonsCleaned = true;
430
+ state.orphanedDaemonsCount = daemonsKilled;
431
+ console.error(`✓ Killed ${daemonsKilled} orphaned daemon process(es)`);
432
+ }
433
+ const errorCount = await cleanupStaleChrome();
434
+ state.chromeCleaned = true;
435
+ if (errorCount > 0) {
436
+ state.warnings.push('Some Chrome processes could not be killed');
437
+ }
438
+ }
439
+ if (options.killChrome && options.chromePid) {
440
+ cleanupChromeProcess(options.chromePid, clearChromePid, state);
441
+ }
442
+ else if (options.killChrome && !options.chromePid) {
443
+ state.warnings.push('Chrome PID not found (Chrome was not launched by bdg)');
444
+ }
445
+ cleanupDaemonPidIfStale(state);
446
+ await cleanupActiveSession(options.force ?? false, cleanupStaleChrome, state);
447
+ if (!options.aggressive) {
448
+ const daemonsKilled = await cleanupOrphanedDaemons();
449
+ if (daemonsKilled > 0) {
450
+ state.daemonsCleaned = true;
451
+ state.orphanedDaemonsCount = daemonsKilled;
452
+ }
453
+ }
454
+ if (options.removeOutput) {
455
+ cleanupOutputFile(state);
456
+ }
457
+ return {
458
+ cleaned: {
459
+ session: state.sessionCleaned,
460
+ chrome: state.chromeCleaned,
461
+ daemons: state.daemonsCleaned,
462
+ output: state.outputCleaned,
463
+ },
464
+ orphanedDaemonsCount: state.orphanedDaemonsCount,
465
+ warnings: state.warnings,
466
+ };
467
+ }
342
468
  //# sourceMappingURL=cleanup.js.map