session-steward 0.8.0 → 0.10.0
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/CHANGELOG.md +15 -0
- package/README.md +73 -4
- package/bin/session-steward-mcp.mjs +65 -0
- package/bin/session-steward-scheduler.mjs +61 -0
- package/lib/cleanup-scheduler-service.mjs +205 -0
- package/lib/cleanup-schedules.mjs +439 -0
- package/lib/cli.mjs +145 -117
- package/lib/installed-products.mjs +44 -0
- package/lib/mcp.mjs +617 -0
- package/lib/providers/claude-code/events.mjs +2 -0
- package/lib/providers/claude-code/store.mjs +16 -3
- package/lib/providers/codex/database-families.mjs +1 -1
- package/lib/providers/codex/store.mjs +18 -4
- package/lib/server.mjs +49 -115
- package/lib/session-cleanup.mjs +540 -0
- package/lib/settings.mjs +1 -0
- package/package.json +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.0] - 2026-08-30
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added full MCP support for finding, inspecting, cleaning, restoring, and automatically cleaning Codex and Claude Code sessions.
|
|
8
|
+
|
|
9
|
+
## [0.9.0] - 2026-08-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Add MCP server for Codex, Claude Code, and other local clients. It can inspect sessions, storage, timelines, and token usage; cleanup remains in the browser and terminal with the existing review and backup safeguards.
|
|
14
|
+
- MCP session searches understand inactivity, workspace, and minimum transcript size filters.
|
|
15
|
+
|
|
3
16
|
## [0.8.0] - 2026-08-23
|
|
4
17
|
|
|
5
18
|
### Added
|
|
@@ -130,6 +143,8 @@
|
|
|
130
143
|
- Support for custom Codex home folders and a saved folder preference.
|
|
131
144
|
- Streaming and bounded-memory discovery for large session collections and transcripts.
|
|
132
145
|
|
|
146
|
+
[0.10.0]: https://github.com/mallikcheripally/session-steward/compare/v0.9.0...v0.10.0
|
|
147
|
+
[0.9.0]: https://github.com/mallikcheripally/session-steward/compare/v0.8.0...v0.9.0
|
|
133
148
|
[0.8.0]: https://github.com/mallikcheripally/session-steward/compare/v0.7.0...v0.8.0
|
|
134
149
|
[0.7.0]: https://github.com/mallikcheripally/session-steward/compare/v0.6.0...v0.7.0
|
|
135
150
|
[0.6.0]: https://github.com/mallikcheripally/session-steward/compare/v0.5.2...v0.6.0
|
package/README.md
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
[](https://github.com/mallikcheripally/session-steward/actions/workflows/validate.yml)
|
|
5
5
|
[](https://github.com/mallikcheripally/session-steward/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
A local Codex and Claude Code session manager for safely reviewing, backing up, and deleting old sessions from a browser UI or terminal CLI.
|
|
7
|
+
A local Codex and Claude Code session manager for safely reviewing, backing up, and deleting old sessions from a browser UI or terminal CLI, or through MCP with ChatGPT or Claude.
|
|
8
8
|
|
|
9
9
|
AI coding tools can accumulate hundreds or thousands of local sessions. A session may leave behind transcripts, history, logs, checkpoints, and linked artifacts, so manual cleanup can easily miss related data.
|
|
10
10
|
|
|
11
|
-
Session Steward makes session cleanup safer by finding those records, showing what cleanup will affect, creating a local backup, removing supported data, and verifying the result afterward.
|
|
11
|
+
Session Steward makes session cleanup safer by finding those records, showing what cleanup will affect, creating a local backup, removing supported data, and verifying the result afterward. Session Steward runs locally.
|
|
12
12
|
|
|
13
13
|

|
|
14
14
|
|
|
15
15
|
## Manage local Codex and Claude Code sessions
|
|
16
16
|
|
|
17
|
-
- Review sessions
|
|
17
|
+
- Review and clean up sessions from the browser, terminal, ChatGPT, or Claude through MCP.
|
|
18
18
|
- Switch between Codex and Claude Code without installing another package.
|
|
19
19
|
- See session counts and the storage used by recognized session files.
|
|
20
20
|
- Find sessions inactive for 30, 60, or 90 days.
|
|
@@ -33,7 +33,7 @@ Session Steward makes session cleanup safer by finding those records, showing wh
|
|
|
33
33
|
- Cleanup is verified before the backup is removed.
|
|
34
34
|
- Unrecognized storage is reported and left untouched.
|
|
35
35
|
- Thorough cleanup is unavailable when the detected storage format is not supported.
|
|
36
|
-
-
|
|
36
|
+
- Cleanup stays local and does not upload session contents.
|
|
37
37
|
|
|
38
38
|
At startup, Session Steward may contact the public npm registry to check for a newer version.
|
|
39
39
|
|
|
@@ -206,6 +206,75 @@ Run `inactive`, `archive`, or `workspace` without a value to clear that filter.
|
|
|
206
206
|
|
|
207
207
|
Use `session-steward-cli --help` to see all available options.
|
|
208
208
|
|
|
209
|
+
## Clean up sessions with ChatGPT or Claude
|
|
210
|
+
|
|
211
|
+
Connect Session Steward once, then ask ChatGPT or Claude to find old or large
|
|
212
|
+
sessions, delete them safely, restore a backup, or clean sessions automatically
|
|
213
|
+
on a schedule.
|
|
214
|
+
|
|
215
|
+
Connect it to ChatGPT and Codex:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
codex mcp add session-steward -- session-steward-mcp
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Connect it to Claude Code:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
claude mcp add --scope user session-steward -- session-steward-mcp
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
You can then ask things like:
|
|
228
|
+
|
|
229
|
+
- “Find sessions I have not used in 60 days.”
|
|
230
|
+
- “Show sessions from this workspace, largest first.”
|
|
231
|
+
- “Delete those sessions.”
|
|
232
|
+
- “Every 12 days, delete sessions I have not used in 45 days.”
|
|
233
|
+
- “Restore my latest backup.”
|
|
234
|
+
|
|
235
|
+
Cleanup uses the same local backup and verification checks as the browser and
|
|
236
|
+
terminal. Codex or Claude Code asks for approval before cleanup, restore, or
|
|
237
|
+
schedule changes.
|
|
238
|
+
|
|
239
|
+
Scheduled cleanup continues in the background after you close Codex or Claude
|
|
240
|
+
Code. You can ask to pause, resume, run, change, or remove a schedule. Before
|
|
241
|
+
uninstalling Session Steward, stop scheduled cleanup:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
session-steward-scheduler --stop
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Check or remove the MCP connection at any time:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
codex mcp list
|
|
251
|
+
codex mcp remove session-steward
|
|
252
|
+
|
|
253
|
+
claude mcp list
|
|
254
|
+
claude mcp remove --scope user session-steward
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
For another MCP client, add a local server named `session-steward`:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"mcpServers": {
|
|
262
|
+
"session-steward": {
|
|
263
|
+
"command": "session-steward-mcp"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The MCP server uses the same provider folders selected in the browser or
|
|
270
|
+
terminal.
|
|
271
|
+
|
|
272
|
+
### Privacy
|
|
273
|
+
|
|
274
|
+
The MCP server runs locally. Session details can include messages, commands,
|
|
275
|
+
file names, and workspace paths, and your MCP client may send that information
|
|
276
|
+
to its AI provider.
|
|
277
|
+
|
|
209
278
|
## Use a custom provider folder
|
|
210
279
|
|
|
211
280
|
The browser interface displays the active provider folder. Select **Change** to choose another existing folder and remember it for later browser and terminal sessions.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
|
|
5
|
+
import packageMetadata from "../package.json" with { type: "json" };
|
|
6
|
+
import { assertSupportedNode } from "../lib/runtime.mjs";
|
|
7
|
+
|
|
8
|
+
assertSupportedNode();
|
|
9
|
+
|
|
10
|
+
const { values } = parseArgs({
|
|
11
|
+
allowPositionals: false,
|
|
12
|
+
options: {
|
|
13
|
+
"claude-home": { type: "string" },
|
|
14
|
+
"codex-home": { type: "string" },
|
|
15
|
+
help: { short: "h", type: "boolean" },
|
|
16
|
+
version: { short: "v", type: "boolean" },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (values.help) {
|
|
21
|
+
process.stdout.write(`Usage: session-steward-mcp [options]
|
|
22
|
+
|
|
23
|
+
Run Session Steward's MCP server over stdio. MCP clients start and stop this
|
|
24
|
+
process automatically. Destructive tools use the client's normal approval flow.
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--codex-home <path> Use a custom Codex session folder
|
|
28
|
+
--claude-home <path> Use a custom Claude session folder
|
|
29
|
+
-h, --help Show this help
|
|
30
|
+
-v, --version Show the installed version
|
|
31
|
+
`);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (values.version) {
|
|
36
|
+
process.stdout.write(`${packageMetadata.version}\n`);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { createProviderSettings } = await import("../lib/settings.mjs");
|
|
41
|
+
const { serveMcp } = await import("../lib/mcp.mjs");
|
|
42
|
+
const providerHomeOverrides = {};
|
|
43
|
+
|
|
44
|
+
if (values["codex-home"] !== undefined) {
|
|
45
|
+
providerHomeOverrides.codex = values["codex-home"];
|
|
46
|
+
}
|
|
47
|
+
if (values["claude-home"] !== undefined) {
|
|
48
|
+
providerHomeOverrides["claude-code"] = values["claude-home"];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const settings = await createProviderSettings({ providerHomeOverrides });
|
|
52
|
+
const handle = serveMcp({
|
|
53
|
+
onerror: (error) => process.stderr.write(`Session Steward MCP error: ${error.message}\n`),
|
|
54
|
+
settings,
|
|
55
|
+
});
|
|
56
|
+
let closing = false;
|
|
57
|
+
|
|
58
|
+
async function close() {
|
|
59
|
+
if (closing) return;
|
|
60
|
+
closing = true;
|
|
61
|
+
await handle.close();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
process.once("SIGINT", () => void close());
|
|
65
|
+
process.once("SIGTERM", () => void close());
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
|
|
5
|
+
import { assertSupportedNode } from "../lib/runtime.mjs";
|
|
6
|
+
|
|
7
|
+
assertSupportedNode();
|
|
8
|
+
|
|
9
|
+
const { values } = parseArgs({
|
|
10
|
+
allowPositionals: false,
|
|
11
|
+
options: {
|
|
12
|
+
"config-directory": { type: "string" },
|
|
13
|
+
help: { short: "h", type: "boolean" },
|
|
14
|
+
"run-due": { type: "boolean" },
|
|
15
|
+
start: { type: "boolean" },
|
|
16
|
+
status: { type: "boolean" },
|
|
17
|
+
stop: { type: "boolean" },
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (values.help) {
|
|
22
|
+
process.stdout.write(`Usage: session-steward-scheduler <option>
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--start Start automatic cleanup and enable it after restart
|
|
26
|
+
--stop Stop automatic cleanup and remove its startup task
|
|
27
|
+
--status Show whether automatic cleanup is running
|
|
28
|
+
--run-due Run due cleanup schedules once and exit
|
|
29
|
+
--config-directory <path> Use a specific Session Steward settings folder
|
|
30
|
+
-h, --help Show this help
|
|
31
|
+
`);
|
|
32
|
+
} else if ([values["run-due"], values.start, values.status, values.stop].filter(Boolean).length !== 1) {
|
|
33
|
+
process.stderr.write("Choose exactly one of --start, --stop, --status, or --run-due.\n");
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
} else if (values["run-due"]) {
|
|
36
|
+
const { createCleanupScheduleStore, runDueCleanupSchedules } = await import(
|
|
37
|
+
"../lib/cleanup-schedules.mjs"
|
|
38
|
+
);
|
|
39
|
+
const { createProviderSettings } = await import("../lib/settings.mjs");
|
|
40
|
+
const settings = await createProviderSettings({
|
|
41
|
+
configDirectory: values["config-directory"],
|
|
42
|
+
});
|
|
43
|
+
const scheduleStore = createCleanupScheduleStore({
|
|
44
|
+
configDirectory: settings.getConfigDirectory(),
|
|
45
|
+
});
|
|
46
|
+
const results = await runDueCleanupSchedules({ scheduleStore, settings });
|
|
47
|
+
if (results.some((result) => ["failed", "recovery-failed"].includes(result.status))) {
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
const { createCleanupSchedulerService } = await import(
|
|
52
|
+
"../lib/cleanup-scheduler-service.mjs"
|
|
53
|
+
);
|
|
54
|
+
const service = createCleanupSchedulerService({
|
|
55
|
+
configDirectory: values["config-directory"],
|
|
56
|
+
});
|
|
57
|
+
const status = values.start
|
|
58
|
+
? await service.start()
|
|
59
|
+
: values.stop ? await service.stop() : await service.status();
|
|
60
|
+
process.stdout.write(`Automatic cleanup is ${status.running ? "running" : "stopped"}.\n`);
|
|
61
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
|
|
8
|
+
import { getDefaultConfigDirectory } from "./platform.mjs";
|
|
9
|
+
|
|
10
|
+
const execFile = promisify(execFileCallback);
|
|
11
|
+
const LABEL = "com.mallikcheripally.session-steward.cleanup";
|
|
12
|
+
const WINDOWS_TASK_NAME = "Session Steward Cleanup";
|
|
13
|
+
const RUNNER_PATH = fileURLToPath(new URL("../bin/session-steward-scheduler.mjs", import.meta.url));
|
|
14
|
+
|
|
15
|
+
function xml(value) {
|
|
16
|
+
return String(value)
|
|
17
|
+
.replaceAll("&", "&")
|
|
18
|
+
.replaceAll("<", "<")
|
|
19
|
+
.replaceAll(">", ">")
|
|
20
|
+
.replaceAll('"', """)
|
|
21
|
+
.replaceAll("'", "'");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function systemdArgument(value) {
|
|
25
|
+
return `"${String(value)
|
|
26
|
+
.replaceAll("%", "%%")
|
|
27
|
+
.replaceAll("\\", "\\\\")
|
|
28
|
+
.replaceAll('"', '\\"')
|
|
29
|
+
.replaceAll("$", "\\$")
|
|
30
|
+
.replaceAll("`", "\\`")}"`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function windowsArgument(value) {
|
|
34
|
+
return `"${String(value).replaceAll(/(\\*)"/gu, "$1$1\\\"").replaceAll(/(\\+)$/gu, "$1$1")}"`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function writePrivateFile(filePath, contents) {
|
|
38
|
+
await fs.mkdir(path.dirname(filePath), { mode: 0o700, recursive: true });
|
|
39
|
+
await fs.writeFile(filePath, contents, { encoding: "utf8", mode: 0o600 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function exists(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
await fs.access(filePath);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createCleanupSchedulerService({
|
|
52
|
+
configDirectory = getDefaultConfigDirectory(),
|
|
53
|
+
environment = process.env,
|
|
54
|
+
execute = execFile,
|
|
55
|
+
home = os.homedir(),
|
|
56
|
+
nodePath = process.execPath,
|
|
57
|
+
platform = process.platform,
|
|
58
|
+
runnerPath = RUNNER_PATH,
|
|
59
|
+
userId = typeof process.getuid === "function" ? process.getuid() : null,
|
|
60
|
+
} = {}) {
|
|
61
|
+
const launchAgentPath = path.join(home, "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
62
|
+
const xdgConfigHome = path.isAbsolute(environment.XDG_CONFIG_HOME || "")
|
|
63
|
+
? environment.XDG_CONFIG_HOME
|
|
64
|
+
: path.join(home, ".config");
|
|
65
|
+
const systemdDirectory = path.join(xdgConfigHome, "systemd", "user");
|
|
66
|
+
const systemdServicePath = path.join(systemdDirectory, "session-steward-cleanup.service");
|
|
67
|
+
const systemdTimerPath = path.join(systemdDirectory, "session-steward-cleanup.timer");
|
|
68
|
+
|
|
69
|
+
async function command(commandName, args, { allowFailure = false } = {}) {
|
|
70
|
+
try {
|
|
71
|
+
await execute(commandName, args, { windowsHide: true });
|
|
72
|
+
return true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (allowFailure) return false;
|
|
75
|
+
throw new Error("Session Steward could not update the automatic cleanup scheduler.", {
|
|
76
|
+
cause: error,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function start() {
|
|
82
|
+
if (platform === "darwin") {
|
|
83
|
+
if (!Number.isSafeInteger(userId)) throw new Error("Session Steward could not identify this user.");
|
|
84
|
+
const target = `gui/${userId}`;
|
|
85
|
+
await writePrivateFile(launchAgentPath, `<?xml version="1.0" encoding="UTF-8"?>
|
|
86
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
87
|
+
<plist version="1.0"><dict>
|
|
88
|
+
<key>Label</key><string>${LABEL}</string>
|
|
89
|
+
<key>ProgramArguments</key><array>
|
|
90
|
+
<string>${xml(nodePath)}</string>
|
|
91
|
+
<string>${xml(runnerPath)}</string>
|
|
92
|
+
<string>--run-due</string>
|
|
93
|
+
<string>--config-directory</string>
|
|
94
|
+
<string>${xml(configDirectory)}</string>
|
|
95
|
+
</array>
|
|
96
|
+
<key>RunAtLoad</key><true/>
|
|
97
|
+
<key>StartInterval</key><integer>900</integer>
|
|
98
|
+
</dict></plist>
|
|
99
|
+
`);
|
|
100
|
+
await command("launchctl", ["bootout", `${target}/${LABEL}`], { allowFailure: true });
|
|
101
|
+
await command("launchctl", ["bootstrap", target, launchAgentPath]);
|
|
102
|
+
await command("launchctl", ["enable", `${target}/${LABEL}`]);
|
|
103
|
+
return status();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (platform === "linux") {
|
|
107
|
+
await writePrivateFile(systemdServicePath, `[Unit]
|
|
108
|
+
Description=Session Steward automatic cleanup
|
|
109
|
+
|
|
110
|
+
[Service]
|
|
111
|
+
Type=oneshot
|
|
112
|
+
ExecStart=${systemdArgument(nodePath)} ${systemdArgument(runnerPath)} --run-due --config-directory ${systemdArgument(configDirectory)}
|
|
113
|
+
`);
|
|
114
|
+
await writePrivateFile(systemdTimerPath, `[Unit]
|
|
115
|
+
Description=Run Session Steward automatic cleanup
|
|
116
|
+
|
|
117
|
+
[Timer]
|
|
118
|
+
OnBootSec=2min
|
|
119
|
+
OnUnitActiveSec=15min
|
|
120
|
+
Persistent=true
|
|
121
|
+
|
|
122
|
+
[Install]
|
|
123
|
+
WantedBy=timers.target
|
|
124
|
+
`);
|
|
125
|
+
await command("systemctl", ["--user", "daemon-reload"]);
|
|
126
|
+
await command("systemctl", ["--user", "enable", "--now", "session-steward-cleanup.timer"]);
|
|
127
|
+
return status();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (platform === "win32") {
|
|
131
|
+
const taskCommand = [
|
|
132
|
+
nodePath,
|
|
133
|
+
runnerPath,
|
|
134
|
+
"--run-due",
|
|
135
|
+
"--config-directory",
|
|
136
|
+
configDirectory,
|
|
137
|
+
].map(windowsArgument).join(" ");
|
|
138
|
+
await command("schtasks", [
|
|
139
|
+
"/Create", "/F", "/SC", "MINUTE", "/MO", "15",
|
|
140
|
+
"/TN", WINDOWS_TASK_NAME,
|
|
141
|
+
"/TR", taskCommand,
|
|
142
|
+
]);
|
|
143
|
+
return status();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error(`Automatic cleanup is not supported on ${platform}.`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function stop() {
|
|
150
|
+
if (platform === "darwin") {
|
|
151
|
+
if (Number.isSafeInteger(userId)) {
|
|
152
|
+
await command("launchctl", ["bootout", `gui/${userId}/${LABEL}`], {
|
|
153
|
+
allowFailure: true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
await fs.rm(launchAgentPath, { force: true });
|
|
157
|
+
return status();
|
|
158
|
+
}
|
|
159
|
+
if (platform === "linux") {
|
|
160
|
+
await command("systemctl", ["--user", "disable", "--now", "session-steward-cleanup.timer"], {
|
|
161
|
+
allowFailure: true,
|
|
162
|
+
});
|
|
163
|
+
await Promise.all([
|
|
164
|
+
fs.rm(systemdServicePath, { force: true }),
|
|
165
|
+
fs.rm(systemdTimerPath, { force: true }),
|
|
166
|
+
]);
|
|
167
|
+
await command("systemctl", ["--user", "daemon-reload"], { allowFailure: true });
|
|
168
|
+
return status();
|
|
169
|
+
}
|
|
170
|
+
if (platform === "win32") {
|
|
171
|
+
await command("schtasks", ["/Delete", "/F", "/TN", WINDOWS_TASK_NAME], {
|
|
172
|
+
allowFailure: true,
|
|
173
|
+
});
|
|
174
|
+
return status();
|
|
175
|
+
}
|
|
176
|
+
return { platform, running: false, supported: false };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function status() {
|
|
180
|
+
if (platform === "darwin") {
|
|
181
|
+
const running = Number.isSafeInteger(userId)
|
|
182
|
+
? await command("launchctl", ["print", `gui/${userId}/${LABEL}`], { allowFailure: true })
|
|
183
|
+
: false;
|
|
184
|
+
return { platform, running, supported: true };
|
|
185
|
+
}
|
|
186
|
+
if (platform === "linux") {
|
|
187
|
+
const configured = await exists(systemdTimerPath);
|
|
188
|
+
const running = configured
|
|
189
|
+
? await command("systemctl", ["--user", "is-active", "--quiet", "session-steward-cleanup.timer"], {
|
|
190
|
+
allowFailure: true,
|
|
191
|
+
})
|
|
192
|
+
: false;
|
|
193
|
+
return { platform, running, supported: true };
|
|
194
|
+
}
|
|
195
|
+
if (platform === "win32") {
|
|
196
|
+
const running = await command("schtasks", ["/Query", "/TN", WINDOWS_TASK_NAME], {
|
|
197
|
+
allowFailure: true,
|
|
198
|
+
});
|
|
199
|
+
return { platform, running, supported: true };
|
|
200
|
+
}
|
|
201
|
+
return { platform, running: false, supported: false };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { start, status, stop };
|
|
205
|
+
}
|