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.
- package/README.md +48 -269
- package/dist/commands/__tests__/helpJson.contract.test.d.ts +12 -0
- package/dist/commands/__tests__/helpJson.contract.test.d.ts.map +1 -0
- package/dist/commands/__tests__/helpJson.contract.test.js +121 -0
- package/dist/commands/__tests__/helpJson.contract.test.js.map +1 -0
- package/dist/commands/__tests__/machineReadableHelp.contract.test.js +26 -7
- package/dist/commands/__tests__/machineReadableHelp.contract.test.js.map +1 -1
- package/dist/commands/cdp.d.ts.map +1 -1
- package/dist/commands/cdp.js.map +1 -1
- package/dist/commands/cleanup.d.ts.map +1 -1
- package/dist/commands/cleanup.js +17 -91
- package/dist/commands/cleanup.js.map +1 -1
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/details.d.ts.map +1 -1
- package/dist/commands/details.js +3 -1
- package/dist/commands/details.js.map +1 -1
- package/dist/commands/dom/DomElementResolver.d.ts +138 -0
- package/dist/commands/dom/DomElementResolver.d.ts.map +1 -0
- package/dist/commands/dom/DomElementResolver.js +173 -0
- package/dist/commands/dom/DomElementResolver.js.map +1 -0
- package/dist/commands/dom/a11y.d.ts.map +1 -1
- package/dist/commands/dom/a11y.js +4 -28
- package/dist/commands/dom/a11y.js.map +1 -1
- package/dist/commands/dom/evalHelpers.d.ts.map +1 -1
- package/dist/commands/dom/evalHelpers.js +1 -1
- package/dist/commands/dom/evalHelpers.js.map +1 -1
- package/dist/commands/dom/formFillHelpers.d.ts.map +1 -1
- package/dist/commands/dom/formFillHelpers.js +11 -11
- package/dist/commands/dom/formFillHelpers.js.map +1 -1
- package/dist/commands/dom/formInteraction.d.ts.map +1 -1
- package/dist/commands/dom/formInteraction.js +5 -5
- package/dist/commands/dom/formInteraction.js.map +1 -1
- package/dist/commands/dom/helpers.d.ts +0 -50
- package/dist/commands/dom/helpers.d.ts.map +1 -1
- package/dist/commands/dom/helpers.js +2 -56
- package/dist/commands/dom/helpers.js.map +1 -1
- package/dist/commands/dom/index.d.ts.map +1 -1
- package/dist/commands/dom/index.js +50 -44
- package/dist/commands/dom/index.js.map +1 -1
- package/dist/commands/dom/keyMapping.d.ts +0 -6
- package/dist/commands/dom/keyMapping.d.ts.map +1 -1
- package/dist/commands/dom/keyMapping.js +0 -8
- package/dist/commands/dom/keyMapping.js.map +1 -1
- package/dist/commands/dom/reactEventHelpers.d.ts +12 -18
- package/dist/commands/dom/reactEventHelpers.d.ts.map +1 -1
- package/dist/commands/dom/reactEventHelpers.js +21 -70
- package/dist/commands/dom/reactEventHelpers.js.map +1 -1
- package/dist/commands/helpJson.d.ts +165 -0
- package/dist/commands/helpJson.d.ts.map +1 -0
- package/dist/commands/helpJson.js +252 -0
- package/dist/commands/helpJson.js.map +1 -0
- package/dist/commands/machineReadableHelp.d.ts +21 -2
- package/dist/commands/machineReadableHelp.d.ts.map +1 -1
- package/dist/commands/machineReadableHelp.js +95 -60
- package/dist/commands/machineReadableHelp.js.map +1 -1
- package/dist/commands/network.d.ts.map +1 -1
- package/dist/commands/network.js.map +1 -1
- package/dist/commands/peek.d.ts.map +1 -1
- package/dist/commands/peek.js +5 -7
- package/dist/commands/peek.js.map +1 -1
- package/dist/commands/shared/CommandRunner.d.ts +4 -10
- package/dist/commands/shared/CommandRunner.d.ts.map +1 -1
- package/dist/commands/shared/CommandRunner.js +2 -2
- package/dist/commands/shared/CommandRunner.js.map +1 -1
- package/dist/commands/shared/commonOptions.d.ts +0 -15
- package/dist/commands/shared/commonOptions.d.ts.map +1 -1
- package/dist/commands/shared/commonOptions.js +0 -32
- package/dist/commands/shared/commonOptions.js.map +1 -1
- package/dist/commands/shared/optionTypes.d.ts +237 -0
- package/dist/commands/shared/optionTypes.d.ts.map +1 -0
- package/dist/commands/shared/optionTypes.js +20 -0
- package/dist/commands/shared/optionTypes.js.map +1 -0
- package/dist/commands/shared/startHelpers.d.ts.map +1 -1
- package/dist/commands/shared/startHelpers.js +3 -3
- package/dist/commands/shared/startHelpers.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +8 -6
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.d.ts.map +1 -1
- package/dist/commands/stop.js +17 -43
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +49 -59
- package/dist/commands/tail.js.map +1 -1
- package/dist/connection/types.d.ts +2 -1
- package/dist/connection/types.d.ts.map +1 -1
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +39 -0
- package/dist/constants.js.map +1 -1
- package/dist/daemon/handlers/responseHandler.d.ts.map +1 -1
- package/dist/daemon/handlers/responseHandler.js +1 -0
- package/dist/daemon/handlers/responseHandler.js.map +1 -1
- package/dist/daemon/ipcServer.js +8 -8
- package/dist/daemon/ipcServer.js.map +1 -1
- package/dist/daemon/lifecycle/signalHandlers.d.ts.map +1 -1
- package/dist/daemon/lifecycle/signalHandlers.js +19 -3
- package/dist/daemon/lifecycle/signalHandlers.js.map +1 -1
- package/dist/daemon/lifecycle/workerCleanup.d.ts.map +1 -1
- package/dist/daemon/lifecycle/workerCleanup.js +5 -2
- package/dist/daemon/lifecycle/workerCleanup.js.map +1 -1
- package/dist/daemon/patternDefinitions.d.ts +0 -13
- package/dist/daemon/patternDefinitions.d.ts.map +1 -1
- package/dist/daemon/patternDefinitions.js +0 -17
- package/dist/daemon/patternDefinitions.js.map +1 -1
- package/dist/daemon/worker/commandRegistry.d.ts.map +1 -1
- package/dist/daemon/worker/commandRegistry.js +1 -0
- package/dist/daemon/worker/commandRegistry.js.map +1 -1
- package/dist/index.js +34 -2
- package/dist/index.js.map +1 -1
- package/dist/ipc/protocol/commands.d.ts +2 -0
- package/dist/ipc/protocol/commands.d.ts.map +1 -1
- package/dist/ipc/protocol/commands.js.map +1 -1
- package/dist/ipc/session/queries.d.ts +2 -0
- package/dist/ipc/session/queries.d.ts.map +1 -1
- package/dist/ipc/utils/responseValidator.d.ts +7 -1
- package/dist/ipc/utils/responseValidator.d.ts.map +1 -1
- package/dist/ipc/utils/responseValidator.js +6 -1
- package/dist/ipc/utils/responseValidator.js.map +1 -1
- package/dist/session/QueryCacheManager.d.ts +140 -0
- package/dist/session/QueryCacheManager.d.ts.map +1 -0
- package/dist/session/QueryCacheManager.js +233 -0
- package/dist/session/QueryCacheManager.js.map +1 -0
- package/dist/session/cleanup.d.ts +63 -1
- package/dist/session/cleanup.d.ts.map +1 -1
- package/dist/session/cleanup.js +191 -65
- package/dist/session/cleanup.js.map +1 -1
- package/dist/session/queryCache.d.ts +25 -14
- package/dist/session/queryCache.d.ts.map +1 -1
- package/dist/session/queryCache.js +31 -60
- package/dist/session/queryCache.js.map +1 -1
- package/dist/telemetry/__tests__/roleInference.unit.test.d.ts +9 -0
- package/dist/telemetry/__tests__/roleInference.unit.test.d.ts.map +1 -0
- package/dist/telemetry/__tests__/roleInference.unit.test.js +138 -0
- package/dist/telemetry/__tests__/roleInference.unit.test.js.map +1 -0
- package/dist/telemetry/dom.d.ts +1 -0
- package/dist/telemetry/dom.d.ts.map +1 -1
- package/dist/telemetry/dom.js +9 -4
- package/dist/telemetry/dom.js.map +1 -1
- package/dist/telemetry/filters.d.ts +3 -0
- package/dist/telemetry/filters.d.ts.map +1 -1
- package/dist/telemetry/filters.js +25 -3
- package/dist/telemetry/filters.js.map +1 -1
- package/dist/telemetry/network.d.ts.map +1 -1
- package/dist/telemetry/network.js +35 -6
- package/dist/telemetry/network.js.map +1 -1
- package/dist/telemetry/roleInference.d.ts +50 -0
- package/dist/telemetry/roleInference.d.ts.map +1 -0
- package/dist/telemetry/roleInference.js +163 -0
- package/dist/telemetry/roleInference.js.map +1 -0
- package/dist/telemetry/utils.d.ts +0 -57
- package/dist/telemetry/utils.d.ts.map +1 -1
- package/dist/telemetry/utils.js +0 -71
- package/dist/telemetry/utils.js.map +1 -1
- package/dist/types/dom.d.ts +2 -0
- package/dist/types/dom.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/formatters/a11y.d.ts +0 -9
- package/dist/ui/formatters/a11y.d.ts.map +1 -1
- package/dist/ui/formatters/a11y.js +0 -45
- package/dist/ui/formatters/a11y.js.map +1 -1
- package/dist/ui/formatters/preview.d.ts.map +1 -1
- package/dist/ui/formatters/preview.js +2 -39
- package/dist/ui/formatters/preview.js.map +1 -1
- package/dist/ui/logging/index.d.ts +1 -1
- package/dist/ui/logging/index.d.ts.map +1 -1
- package/dist/ui/logging/index.js +1 -1
- package/dist/ui/logging/index.js.map +1 -1
- package/dist/ui/logging/logger.d.ts +21 -0
- package/dist/ui/logging/logger.d.ts.map +1 -1
- package/dist/ui/logging/logger.js +24 -6
- package/dist/ui/logging/logger.js.map +1 -1
- package/dist/ui/messages/commands.d.ts +0 -14
- package/dist/ui/messages/commands.d.ts.map +1 -1
- package/dist/ui/messages/commands.js +0 -18
- package/dist/ui/messages/commands.js.map +1 -1
- package/dist/ui/messages/errors.d.ts +0 -16
- package/dist/ui/messages/errors.d.ts.map +1 -1
- package/dist/ui/messages/errors.js +0 -18
- package/dist/ui/messages/errors.js.map +1 -1
- package/dist/ui/messages/hints.d.ts +0 -10
- package/dist/ui/messages/hints.d.ts.map +1 -1
- package/dist/ui/messages/hints.js +0 -12
- package/dist/ui/messages/hints.js.map +1 -1
- package/dist/utils/decisionTrees.d.ts +0 -17
- package/dist/utils/decisionTrees.d.ts.map +1 -1
- package/dist/utils/decisionTrees.js +0 -21
- package/dist/utils/decisionTrees.js.map +1 -1
- package/dist/utils/errorMapping.d.ts +60 -0
- package/dist/utils/errorMapping.d.ts.map +1 -0
- package/dist/utils/errorMapping.js +97 -0
- package/dist/utils/errorMapping.js.map +1 -0
- package/dist/utils/file.d.ts +27 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +37 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/levenshtein.d.ts +0 -17
- package/dist/utils/levenshtein.d.ts.map +1 -1
- package/dist/utils/levenshtein.js +0 -31
- package/dist/utils/levenshtein.js.map +1 -1
- package/dist/utils/taskMappings.d.ts +0 -17
- package/dist/utils/taskMappings.d.ts.map +1 -1
- package/dist/utils/taskMappings.js +0 -21
- package/dist/utils/taskMappings.js.map +1 -1
- package/dist/utils/typeGuards.d.ts +62 -0
- package/dist/utils/typeGuards.d.ts.map +1 -0
- package/dist/utils/typeGuards.js +94 -0
- package/dist/utils/typeGuards.js.map +1 -0
- 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;
|
|
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"}
|
package/dist/session/cleanup.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
|
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
|