crewly 1.20.245 → 1.20.247
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/dist/backend/backend/src/constants.d.ts +20 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +20 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +2 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +22 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/system/scheduled-commands.service.d.ts +130 -0
- package/dist/backend/backend/src/services/system/scheduled-commands.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/system/scheduled-commands.service.js +286 -0
- package/dist/backend/backend/src/services/system/scheduled-commands.service.js.map +1 -0
- package/dist/backend/backend/src/services/worktree/docker-reclaim.d.ts +54 -0
- package/dist/backend/backend/src/services/worktree/docker-reclaim.d.ts.map +1 -0
- package/dist/backend/backend/src/services/worktree/docker-reclaim.js +102 -0
- package/dist/backend/backend/src/services/worktree/docker-reclaim.js.map +1 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +8 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +23 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +20 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +20 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled commands runner.
|
|
3
|
+
*
|
|
4
|
+
* Runs host commands listed in `<CREWLY_HOME>/scheduled-commands.json` on an
|
|
5
|
+
* interval. It exists because launchd jobs cannot read ~/Desktop (TCC), while
|
|
6
|
+
* the backend can. Design rules:
|
|
7
|
+
* - The list is read from disk only. There is deliberately no API route or
|
|
8
|
+
* trigger action that adds an entry: that would be arbitrary shell
|
|
9
|
+
* execution through the API.
|
|
10
|
+
* - No file = no jobs = feature off.
|
|
11
|
+
* - Each run is spawned DETACHED (own process group, unref'd, output to a log
|
|
12
|
+
* file) so a Crewly restart cannot kill it mid-way (a rollout cut in half
|
|
13
|
+
* leaves a mixed fleet).
|
|
14
|
+
* - A run never starts while the previous one is alive: checked by the pid we
|
|
15
|
+
* spawned and, when `lockFile` is set, by the pid inside the command's own
|
|
16
|
+
* lock file (a stale pid counts as dead).
|
|
17
|
+
* - Every skip is logged at debug; a run is logged at info only when spawned.
|
|
18
|
+
*
|
|
19
|
+
* Config shape: `[{name, cwd, command, args?, intervalMinutes, enabled?, lockFile?}]`.
|
|
20
|
+
*
|
|
21
|
+
* @module services/system/scheduled-commands
|
|
22
|
+
*/
|
|
23
|
+
import { type ChildProcess, type SpawnOptions } from 'child_process';
|
|
24
|
+
/** One validated entry from the config file. */
|
|
25
|
+
export interface ScheduledCommand {
|
|
26
|
+
/** Unique name (used in the log file name) */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Absolute working directory */
|
|
29
|
+
cwd: string;
|
|
30
|
+
/** Executable, run without a shell */
|
|
31
|
+
command: string;
|
|
32
|
+
/** Arguments */
|
|
33
|
+
args: string[];
|
|
34
|
+
/** Minutes between runs */
|
|
35
|
+
intervalMinutes: number;
|
|
36
|
+
/** Absolute path of the command's own lock file (holds a pid), optional */
|
|
37
|
+
lockFile?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Result of reading the config file. */
|
|
40
|
+
export interface LoadedScheduledCommands {
|
|
41
|
+
/** Enabled, valid entries */
|
|
42
|
+
entries: ScheduledCommand[];
|
|
43
|
+
/** Entries in the file that were valid but disabled */
|
|
44
|
+
disabled: number;
|
|
45
|
+
/** Why the file or an entry was not used (one line each) */
|
|
46
|
+
warnings: string[];
|
|
47
|
+
}
|
|
48
|
+
/** Minimal logger contract. */
|
|
49
|
+
export interface ScheduledCommandsLogger {
|
|
50
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
51
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
52
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
53
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
54
|
+
}
|
|
55
|
+
/** Injection points (tests replace the process and filesystem edges). */
|
|
56
|
+
export interface ScheduledCommandsDeps {
|
|
57
|
+
/** Path of the config file */
|
|
58
|
+
configPath: string;
|
|
59
|
+
/** Directory for per-command log files */
|
|
60
|
+
logDir: string;
|
|
61
|
+
logger: ScheduledCommandsLogger;
|
|
62
|
+
/** child_process.spawn */
|
|
63
|
+
spawn?: (command: string, args: string[], options: SpawnOptions) => ChildProcess;
|
|
64
|
+
/** True when a process with this pid exists */
|
|
65
|
+
isPidAlive?: (pid: number) => boolean;
|
|
66
|
+
/** Reads a text file; throws when missing */
|
|
67
|
+
readFile?: (file: string) => string;
|
|
68
|
+
/** True when the path exists */
|
|
69
|
+
pathExists?: (file: string) => boolean;
|
|
70
|
+
/** Opens the log file for appending and returns an fd */
|
|
71
|
+
openLog?: (file: string) => number;
|
|
72
|
+
/** Closes an fd */
|
|
73
|
+
closeLog?: (fd: number) => void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* True when a process with this pid exists (signal 0 probe).
|
|
77
|
+
*
|
|
78
|
+
* @param pid - Process id
|
|
79
|
+
* @returns Whether it is alive
|
|
80
|
+
*/
|
|
81
|
+
export declare function defaultIsPidAlive(pid: number): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Parse and validate the config text.
|
|
84
|
+
*
|
|
85
|
+
* Invalid input never throws: it yields zero entries and a warning.
|
|
86
|
+
*
|
|
87
|
+
* @param text - File contents
|
|
88
|
+
* @returns Entries, disabled count and warnings
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseScheduledCommands(text: string): LoadedScheduledCommands;
|
|
91
|
+
/** Outcome of one tick for one entry (also returned to tests). */
|
|
92
|
+
export type RunOutcome = 'spawned' | 'skipped-running' | 'skipped-lock' | 'skipped-no-cwd' | 'spawn-failed';
|
|
93
|
+
/**
|
|
94
|
+
* Interval runner for owner-configured host commands.
|
|
95
|
+
*/
|
|
96
|
+
export declare class ScheduledCommandsService {
|
|
97
|
+
private readonly deps;
|
|
98
|
+
private entries;
|
|
99
|
+
private timers;
|
|
100
|
+
/** Last spawned pid per entry name */
|
|
101
|
+
private readonly lastPid;
|
|
102
|
+
/**
|
|
103
|
+
* @param deps - Paths, logger and optional process/filesystem overrides
|
|
104
|
+
*/
|
|
105
|
+
constructor(deps: ScheduledCommandsDeps);
|
|
106
|
+
/**
|
|
107
|
+
* Load the config and schedule every enabled entry.
|
|
108
|
+
*
|
|
109
|
+
* Logs "N scheduled command(s) loaded" every time, including 0.
|
|
110
|
+
* A missing file is the normal off state (info); an unreadable or
|
|
111
|
+
* invalid file logs one warn line.
|
|
112
|
+
*
|
|
113
|
+
* @returns Number of jobs scheduled
|
|
114
|
+
*/
|
|
115
|
+
start(): number;
|
|
116
|
+
/** Cancel all timers. Spawned children are NOT touched (they are detached). */
|
|
117
|
+
stop(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Try to run one entry now.
|
|
120
|
+
*
|
|
121
|
+
* Skips (debug log) when the previous run or the command's own lock holder
|
|
122
|
+
* is alive, or the cwd is missing (warn). Never claims a run happened
|
|
123
|
+
* when it was skipped.
|
|
124
|
+
*
|
|
125
|
+
* @param entry - Entry to run
|
|
126
|
+
* @returns What happened
|
|
127
|
+
*/
|
|
128
|
+
runEntry(entry: ScheduledCommand): RunOutcome;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=scheduled-commands.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-commands.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/system/scheduled-commands.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled commands runner.
|
|
3
|
+
*
|
|
4
|
+
* Runs host commands listed in `<CREWLY_HOME>/scheduled-commands.json` on an
|
|
5
|
+
* interval. It exists because launchd jobs cannot read ~/Desktop (TCC), while
|
|
6
|
+
* the backend can. Design rules:
|
|
7
|
+
* - The list is read from disk only. There is deliberately no API route or
|
|
8
|
+
* trigger action that adds an entry: that would be arbitrary shell
|
|
9
|
+
* execution through the API.
|
|
10
|
+
* - No file = no jobs = feature off.
|
|
11
|
+
* - Each run is spawned DETACHED (own process group, unref'd, output to a log
|
|
12
|
+
* file) so a Crewly restart cannot kill it mid-way (a rollout cut in half
|
|
13
|
+
* leaves a mixed fleet).
|
|
14
|
+
* - A run never starts while the previous one is alive: checked by the pid we
|
|
15
|
+
* spawned and, when `lockFile` is set, by the pid inside the command's own
|
|
16
|
+
* lock file (a stale pid counts as dead).
|
|
17
|
+
* - Every skip is logged at debug; a run is logged at info only when spawned.
|
|
18
|
+
*
|
|
19
|
+
* Config shape: `[{name, cwd, command, args?, intervalMinutes, enabled?, lockFile?}]`.
|
|
20
|
+
*
|
|
21
|
+
* @module services/system/scheduled-commands
|
|
22
|
+
*/
|
|
23
|
+
import { spawn as nodeSpawn } from 'child_process';
|
|
24
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync } from 'fs';
|
|
25
|
+
import * as os from 'os';
|
|
26
|
+
import * as path from 'path';
|
|
27
|
+
import { SCHEDULED_COMMANDS } from '../../constants.js';
|
|
28
|
+
/**
|
|
29
|
+
* Expand a leading `~` to the home directory.
|
|
30
|
+
*
|
|
31
|
+
* @param p - Path from the config file
|
|
32
|
+
* @returns Path with `~` expanded
|
|
33
|
+
*/
|
|
34
|
+
function expandHome(p) {
|
|
35
|
+
if (p === '~')
|
|
36
|
+
return os.homedir();
|
|
37
|
+
if (p.startsWith('~/'))
|
|
38
|
+
return path.join(os.homedir(), p.slice(2));
|
|
39
|
+
return p;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* True when a process with this pid exists (signal 0 probe).
|
|
43
|
+
*
|
|
44
|
+
* @param pid - Process id
|
|
45
|
+
* @returns Whether it is alive
|
|
46
|
+
*/
|
|
47
|
+
export function defaultIsPidAlive(pid) {
|
|
48
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
49
|
+
return false;
|
|
50
|
+
try {
|
|
51
|
+
process.kill(pid, 0);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// EPERM = exists but not ours: still alive.
|
|
56
|
+
return err.code === 'EPERM';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse and validate the config text.
|
|
61
|
+
*
|
|
62
|
+
* Invalid input never throws: it yields zero entries and a warning.
|
|
63
|
+
*
|
|
64
|
+
* @param text - File contents
|
|
65
|
+
* @returns Entries, disabled count and warnings
|
|
66
|
+
*/
|
|
67
|
+
export function parseScheduledCommands(text) {
|
|
68
|
+
const out = { entries: [], disabled: 0, warnings: [] };
|
|
69
|
+
let raw;
|
|
70
|
+
try {
|
|
71
|
+
raw = JSON.parse(text);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
out.warnings.push(`config is not valid JSON (${err instanceof Error ? err.message : String(err)}); 0 jobs`);
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(raw)) {
|
|
78
|
+
out.warnings.push('config must be a JSON array; 0 jobs');
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
raw.forEach((item, i) => {
|
|
83
|
+
const label = `entry ${i}`;
|
|
84
|
+
if (!item || typeof item !== 'object') {
|
|
85
|
+
out.warnings.push(`${label}: not an object; skipped`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const e = item;
|
|
89
|
+
if (typeof e.name !== 'string' || !SCHEDULED_COMMANDS.NAME_PATTERN.test(e.name)) {
|
|
90
|
+
out.warnings.push(`${label}: name must match ${SCHEDULED_COMMANDS.NAME_PATTERN}; skipped`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (seen.has(e.name)) {
|
|
94
|
+
out.warnings.push(`${label} (${e.name}): duplicate name; skipped`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (typeof e.command !== 'string' || e.command.trim() === '') {
|
|
98
|
+
out.warnings.push(`${label} (${e.name}): command is required; skipped`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (typeof e.cwd !== 'string' || !path.isAbsolute(expandHome(e.cwd))) {
|
|
102
|
+
out.warnings.push(`${label} (${e.name}): cwd must be an absolute path; skipped`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (e.args !== undefined && (!Array.isArray(e.args) || e.args.some((a) => typeof a !== 'string'))) {
|
|
106
|
+
out.warnings.push(`${label} (${e.name}): args must be an array of strings; skipped`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (typeof e.intervalMinutes !== 'number' ||
|
|
110
|
+
!Number.isFinite(e.intervalMinutes) ||
|
|
111
|
+
e.intervalMinutes < SCHEDULED_COMMANDS.MIN_INTERVAL_MINUTES) {
|
|
112
|
+
out.warnings.push(`${label} (${e.name}): intervalMinutes must be a number >= ${SCHEDULED_COMMANDS.MIN_INTERVAL_MINUTES}; skipped`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (e.lockFile !== undefined && (typeof e.lockFile !== 'string' || !path.isAbsolute(expandHome(e.lockFile)))) {
|
|
116
|
+
out.warnings.push(`${label} (${e.name}): lockFile must be an absolute path; skipped`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
seen.add(e.name);
|
|
120
|
+
if (e.enabled === false) {
|
|
121
|
+
out.disabled++;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
out.entries.push({
|
|
125
|
+
name: e.name,
|
|
126
|
+
cwd: expandHome(e.cwd),
|
|
127
|
+
command: e.command,
|
|
128
|
+
args: e.args ?? [],
|
|
129
|
+
intervalMinutes: e.intervalMinutes,
|
|
130
|
+
lockFile: typeof e.lockFile === 'string' ? expandHome(e.lockFile) : undefined,
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Interval runner for owner-configured host commands.
|
|
137
|
+
*/
|
|
138
|
+
export class ScheduledCommandsService {
|
|
139
|
+
deps;
|
|
140
|
+
entries = [];
|
|
141
|
+
timers = [];
|
|
142
|
+
/** Last spawned pid per entry name */
|
|
143
|
+
lastPid = new Map();
|
|
144
|
+
/**
|
|
145
|
+
* @param deps - Paths, logger and optional process/filesystem overrides
|
|
146
|
+
*/
|
|
147
|
+
constructor(deps) {
|
|
148
|
+
this.deps = {
|
|
149
|
+
spawn: nodeSpawn,
|
|
150
|
+
isPidAlive: defaultIsPidAlive,
|
|
151
|
+
readFile: (f) => readFileSync(f, 'utf-8'),
|
|
152
|
+
pathExists: existsSync,
|
|
153
|
+
openLog: (f) => {
|
|
154
|
+
mkdirSync(path.dirname(f), { recursive: true });
|
|
155
|
+
return openSync(f, 'a');
|
|
156
|
+
},
|
|
157
|
+
closeLog: closeSync,
|
|
158
|
+
...deps,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Load the config and schedule every enabled entry.
|
|
163
|
+
*
|
|
164
|
+
* Logs "N scheduled command(s) loaded" every time, including 0.
|
|
165
|
+
* A missing file is the normal off state (info); an unreadable or
|
|
166
|
+
* invalid file logs one warn line.
|
|
167
|
+
*
|
|
168
|
+
* @returns Number of jobs scheduled
|
|
169
|
+
*/
|
|
170
|
+
start() {
|
|
171
|
+
this.stop();
|
|
172
|
+
let text;
|
|
173
|
+
try {
|
|
174
|
+
text = this.deps.readFile(this.deps.configPath);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
if (err.code === 'ENOENT') {
|
|
178
|
+
this.deps.logger.info('0 scheduled command(s) loaded (no config file)', { path: this.deps.configPath });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.deps.logger.warn('0 scheduled command(s) loaded: config unreadable', {
|
|
182
|
+
path: this.deps.configPath,
|
|
183
|
+
error: err instanceof Error ? err.message : String(err),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
const loaded = parseScheduledCommands(text);
|
|
189
|
+
loaded.warnings.forEach((w) => this.deps.logger.warn(`scheduled-commands: ${w}`));
|
|
190
|
+
this.entries = loaded.entries;
|
|
191
|
+
this.deps.logger.info(`${loaded.entries.length} scheduled command(s) loaded`, {
|
|
192
|
+
disabled: loaded.disabled,
|
|
193
|
+
invalid: loaded.warnings.length,
|
|
194
|
+
names: loaded.entries.map((e) => e.name),
|
|
195
|
+
});
|
|
196
|
+
for (const entry of this.entries) {
|
|
197
|
+
const every = entry.intervalMinutes * SCHEDULED_COMMANDS.MS_PER_MINUTE;
|
|
198
|
+
const first = setTimeout(() => this.runEntry(entry), SCHEDULED_COMMANDS.INITIAL_DELAY_MS);
|
|
199
|
+
const repeat = setInterval(() => this.runEntry(entry), every);
|
|
200
|
+
first.unref();
|
|
201
|
+
repeat.unref();
|
|
202
|
+
this.timers.push(first, repeat);
|
|
203
|
+
}
|
|
204
|
+
return this.entries.length;
|
|
205
|
+
}
|
|
206
|
+
/** Cancel all timers. Spawned children are NOT touched (they are detached). */
|
|
207
|
+
stop() {
|
|
208
|
+
for (const t of this.timers) {
|
|
209
|
+
clearTimeout(t);
|
|
210
|
+
clearInterval(t);
|
|
211
|
+
}
|
|
212
|
+
this.timers = [];
|
|
213
|
+
this.entries = [];
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Try to run one entry now.
|
|
217
|
+
*
|
|
218
|
+
* Skips (debug log) when the previous run or the command's own lock holder
|
|
219
|
+
* is alive, or the cwd is missing (warn). Never claims a run happened
|
|
220
|
+
* when it was skipped.
|
|
221
|
+
*
|
|
222
|
+
* @param entry - Entry to run
|
|
223
|
+
* @returns What happened
|
|
224
|
+
*/
|
|
225
|
+
runEntry(entry) {
|
|
226
|
+
const { logger, isPidAlive } = this.deps;
|
|
227
|
+
const prev = this.lastPid.get(entry.name);
|
|
228
|
+
if (prev !== undefined && isPidAlive(prev)) {
|
|
229
|
+
logger.debug(`scheduled-commands: ${entry.name} skipped, previous run pid ${prev} still alive`);
|
|
230
|
+
return 'skipped-running';
|
|
231
|
+
}
|
|
232
|
+
if (entry.lockFile) {
|
|
233
|
+
let holder = NaN;
|
|
234
|
+
try {
|
|
235
|
+
holder = parseInt(this.deps.readFile(entry.lockFile).trim(), 10);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// No lock file = nobody holds it.
|
|
239
|
+
}
|
|
240
|
+
if (Number.isInteger(holder) && isPidAlive(holder)) {
|
|
241
|
+
logger.debug(`scheduled-commands: ${entry.name} skipped, lock held by pid ${holder}`);
|
|
242
|
+
return 'skipped-lock';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (!this.deps.pathExists(entry.cwd)) {
|
|
246
|
+
logger.warn(`scheduled-commands: ${entry.name} not run, cwd missing`, { cwd: entry.cwd });
|
|
247
|
+
return 'skipped-no-cwd';
|
|
248
|
+
}
|
|
249
|
+
let fd = -1;
|
|
250
|
+
try {
|
|
251
|
+
const logFile = path.join(this.deps.logDir, SCHEDULED_COMMANDS.LOG_FILE_TEMPLATE.replace('{name}', entry.name));
|
|
252
|
+
fd = this.deps.openLog(logFile);
|
|
253
|
+
const child = this.deps.spawn(entry.command, entry.args, {
|
|
254
|
+
cwd: entry.cwd,
|
|
255
|
+
detached: true,
|
|
256
|
+
stdio: ['ignore', fd, fd],
|
|
257
|
+
});
|
|
258
|
+
child.on('error', (err) => logger.error(`scheduled-commands: ${entry.name} failed to start`, { error: err.message }));
|
|
259
|
+
child.unref();
|
|
260
|
+
if (child.pid === undefined) {
|
|
261
|
+
logger.error(`scheduled-commands: ${entry.name} did not start (no pid)`);
|
|
262
|
+
return 'spawn-failed';
|
|
263
|
+
}
|
|
264
|
+
this.lastPid.set(entry.name, child.pid);
|
|
265
|
+
logger.info(`scheduled-commands: ${entry.name} started`, { pid: child.pid, cwd: entry.cwd });
|
|
266
|
+
return 'spawned';
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
logger.error(`scheduled-commands: ${entry.name} spawn threw`, {
|
|
270
|
+
error: err instanceof Error ? err.message : String(err),
|
|
271
|
+
});
|
|
272
|
+
return 'spawn-failed';
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
if (fd >= 0) {
|
|
276
|
+
try {
|
|
277
|
+
this.deps.closeLog(fd);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// Already closed.
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=scheduled-commands.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-commands.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/system/scheduled-commands.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAwC,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAyDxD;;;;;GAKG;AACH,SAAS,UAAU,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACV,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC5C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,4CAA4C;QAC5C,OAAQ,GAA6B,CAAC,IAAI,KAAK,OAAO,CAAC;IACxD,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IAClD,MAAM,GAAG,GAA4B,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAChF,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5G,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACzD,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAC;YACtD,OAAO;QACR,CAAC;QACD,MAAM,CAAC,GAAG,IAA+B,CAAC;QAC1C,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACjF,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,qBAAqB,kBAAkB,CAAC,YAAY,WAAW,CAAC,CAAC;YAC3F,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,4BAA4B,CAAC,CAAC;YACnE,OAAO;QACR,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC9D,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC;YACxE,OAAO;QACR,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,0CAA0C,CAAC,CAAC;YACjF,OAAO;QACR,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC;YACnG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,8CAA8C,CAAC,CAAC;YACrF,OAAO;QACR,CAAC;QACD,IACC,OAAO,CAAC,CAAC,eAAe,KAAK,QAAQ;YACrC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC;YACnC,CAAC,CAAC,eAAe,GAAG,kBAAkB,CAAC,oBAAoB,EAC1D,CAAC;YACF,GAAG,CAAC,QAAQ,CAAC,IAAI,CAChB,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,0CAA0C,kBAAkB,CAAC,oBAAoB,WAAW,CAC/G,CAAC;YACF,OAAO;QACR,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9G,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,+CAA+C,CAAC,CAAC;YACtF,OAAO;QACR,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACzB,GAAG,CAAC,QAAQ,EAAE,CAAC;YACf,OAAO;QACR,CAAC;QACD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;YACtB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,IAAI,EAAG,CAAC,CAAC,IAA6B,IAAI,EAAE;YAC5C,eAAe,EAAE,CAAC,CAAC,eAAe;YAClC,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;SAC7E,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACZ,CAAC;AAKD;;GAEG;AACH,MAAM,OAAO,wBAAwB;IACnB,IAAI,CAAkC;IAC/C,OAAO,GAAuB,EAAE,CAAC;IACjC,MAAM,GAAqB,EAAE,CAAC;IACtC,sCAAsC;IACrB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD;;OAEG;IACH,YAAY,IAA2B;QACtC,IAAI,CAAC,IAAI,GAAG;YACX,KAAK,EAAE,SAAgE;YACvE,UAAU,EAAE,iBAAiB;YAC7B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;YACzC,UAAU,EAAE,UAAU;YACtB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACd,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChD,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACzB,CAAC;YACD,QAAQ,EAAE,SAAS;YACnB,GAAG,IAAI;SACP,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK;QACJ,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACJ,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gDAAgD,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACzG,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kDAAkD,EAAE;oBACzE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU;oBAC1B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACvD,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,CAAC;QACV,CAAC;QACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,8BAA8B,EAAE;YAC7E,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;YAC/B,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SACxC,CAAC,CAAC;QACH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,GAAG,kBAAkB,CAAC,aAAa,CAAC;YACvE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;YAC1F,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;YAC9D,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,+EAA+E;IAC/E,IAAI;QACH,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,aAAa,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACnB,CAAC;IAED;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAuB;QAC/B,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,IAAI,8BAA8B,IAAI,cAAc,CAAC,CAAC;YAChG,OAAO,iBAAiB,CAAC;QAC1B,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,MAAM,GAAG,GAAG,CAAC;YACjB,IAAI,CAAC;gBACJ,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAClE,CAAC;YAAC,MAAM,CAAC;gBACR,kCAAkC;YACnC,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpD,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,IAAI,8BAA8B,MAAM,EAAE,CAAC,CAAC;gBACtF,OAAO,cAAc,CAAC;YACvB,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,IAAI,uBAAuB,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAC1F,OAAO,gBAAgB,CAAC;QACzB,CAAC;QACD,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACxB,IAAI,CAAC,IAAI,CAAC,MAAM,EAChB,kBAAkB,CAAC,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAClE,CAAC;YACF,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE;gBACxD,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC;aACzB,CAAC,CAAC;YACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CACzB,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,IAAI,kBAAkB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CACzF,CAAC;YACF,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,IAAI,yBAAyB,CAAC,CAAC;gBACzE,OAAO,cAAc,CAAC;YACvB,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,IAAI,UAAU,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAC7F,OAAO,SAAS,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,IAAI,cAAc,EAAE;gBAC7D,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACvD,CAAC,CAAC;YACH,OAAO,cAAc,CAAC;QACvB,CAAC;gBAAS,CAAC;YACV,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACb,IAAI,CAAC;oBACJ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACR,kBAAkB;gBACnB,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker reclaim for the low-disk pass.
|
|
3
|
+
*
|
|
4
|
+
* Agents that deploy build images on this machine all day; Docker Desktop's
|
|
5
|
+
* disk grew 23 GB in one night (2026-10-07) and took the Mac to 2.3 GB free
|
|
6
|
+
* while the worktree janitor found "nothing safe to delete". Only things that
|
|
7
|
+
* can be rebuilt or pulled again are removed:
|
|
8
|
+
*
|
|
9
|
+
* - older tags of registry images (`host/repo:tag`) — the newest tag of
|
|
10
|
+
* each repository stays, so the next build still has its layers; an image
|
|
11
|
+
* a container uses cannot be removed (docker refuses, we move on)
|
|
12
|
+
* - dangling images
|
|
13
|
+
* - build cache beyond a few GB, for the default builder and every
|
|
14
|
+
* docker-container (buildx) builder
|
|
15
|
+
*
|
|
16
|
+
* Never volumes, never containers. Skipped when the docker CLI or daemon is
|
|
17
|
+
* not there. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* @module services/worktree/docker-reclaim
|
|
20
|
+
*/
|
|
21
|
+
/** Runs a docker command; resolves stdout, rejects on failure. */
|
|
22
|
+
export type DockerExec = (args: string[], timeoutMs: number) => Promise<string>;
|
|
23
|
+
/** What a reclaim did. */
|
|
24
|
+
export interface DockerReclaimResult {
|
|
25
|
+
/** Docker was reachable */
|
|
26
|
+
ran: boolean;
|
|
27
|
+
/** Image tags removed */
|
|
28
|
+
removedImages: string[];
|
|
29
|
+
}
|
|
30
|
+
/** Build cache kept per builder. */
|
|
31
|
+
export declare const DOCKER_RECLAIM_KEEP_STORAGE = "3gb";
|
|
32
|
+
/**
|
|
33
|
+
* Older tags of each registry repository, newest first in `docker images`
|
|
34
|
+
* output (which lists newest first).
|
|
35
|
+
*
|
|
36
|
+
* @param listing - `docker images --format '{{.Repository}} {{.Tag}}'` output
|
|
37
|
+
* @returns `repo:tag` names to remove
|
|
38
|
+
*/
|
|
39
|
+
export declare function olderRegistryTags(listing: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Names of docker-container (buildx) builders.
|
|
42
|
+
*
|
|
43
|
+
* @param listing - `docker buildx ls` output
|
|
44
|
+
* @returns Builder names
|
|
45
|
+
*/
|
|
46
|
+
export declare function containerBuilders(listing: string): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Free what Docker holds that can be rebuilt or pulled again.
|
|
49
|
+
*
|
|
50
|
+
* @param exec - docker runner (tests)
|
|
51
|
+
* @returns What was done
|
|
52
|
+
*/
|
|
53
|
+
export declare function reclaimDocker(exec?: DockerExec): Promise<DockerReclaimResult>;
|
|
54
|
+
//# sourceMappingURL=docker-reclaim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-reclaim.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/docker-reclaim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEhF,0BAA0B;AAC1B,MAAM,WAAW,mBAAmB;IACnC,2BAA2B;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,yBAAyB;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,oCAAoC;AACpC,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AAUjD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY3D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ3D;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE,UAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoBhG"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docker reclaim for the low-disk pass.
|
|
3
|
+
*
|
|
4
|
+
* Agents that deploy build images on this machine all day; Docker Desktop's
|
|
5
|
+
* disk grew 23 GB in one night (2026-10-07) and took the Mac to 2.3 GB free
|
|
6
|
+
* while the worktree janitor found "nothing safe to delete". Only things that
|
|
7
|
+
* can be rebuilt or pulled again are removed:
|
|
8
|
+
*
|
|
9
|
+
* - older tags of registry images (`host/repo:tag`) — the newest tag of
|
|
10
|
+
* each repository stays, so the next build still has its layers; an image
|
|
11
|
+
* a container uses cannot be removed (docker refuses, we move on)
|
|
12
|
+
* - dangling images
|
|
13
|
+
* - build cache beyond a few GB, for the default builder and every
|
|
14
|
+
* docker-container (buildx) builder
|
|
15
|
+
*
|
|
16
|
+
* Never volumes, never containers. Skipped when the docker CLI or daemon is
|
|
17
|
+
* not there. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* @module services/worktree/docker-reclaim
|
|
20
|
+
*/
|
|
21
|
+
import { execFile } from 'child_process';
|
|
22
|
+
/** Build cache kept per builder. */
|
|
23
|
+
export const DOCKER_RECLAIM_KEEP_STORAGE = '3gb';
|
|
24
|
+
const defaultExec = (args, timeoutMs) => new Promise((resolve, reject) => {
|
|
25
|
+
execFile('docker', args, { timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 }, (err, stdout) => {
|
|
26
|
+
if (err)
|
|
27
|
+
reject(err);
|
|
28
|
+
else
|
|
29
|
+
resolve(String(stdout));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Older tags of each registry repository, newest first in `docker images`
|
|
34
|
+
* output (which lists newest first).
|
|
35
|
+
*
|
|
36
|
+
* @param listing - `docker images --format '{{.Repository}} {{.Tag}}'` output
|
|
37
|
+
* @returns `repo:tag` names to remove
|
|
38
|
+
*/
|
|
39
|
+
export function olderRegistryTags(listing) {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const line of listing.split('\n')) {
|
|
43
|
+
const [repo, tag] = line.trim().split(/\s+/);
|
|
44
|
+
if (!repo || !tag || tag === '<none>' || repo === '<none>')
|
|
45
|
+
continue;
|
|
46
|
+
// Only images that live in a registry (`host/...`): they can be pulled again.
|
|
47
|
+
if (!repo.includes('/') || !repo.split('/')[0].includes('.'))
|
|
48
|
+
continue;
|
|
49
|
+
if (seen.has(repo))
|
|
50
|
+
out.push(`${repo}:${tag}`);
|
|
51
|
+
else
|
|
52
|
+
seen.add(repo);
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Names of docker-container (buildx) builders.
|
|
58
|
+
*
|
|
59
|
+
* @param listing - `docker buildx ls` output
|
|
60
|
+
* @returns Builder names
|
|
61
|
+
*/
|
|
62
|
+
export function containerBuilders(listing) {
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const line of listing.split('\n')) {
|
|
65
|
+
if (/^\s|\\_/.test(line))
|
|
66
|
+
continue;
|
|
67
|
+
const [name, driver] = line.trim().split(/\s+/);
|
|
68
|
+
if (name && driver === 'docker-container')
|
|
69
|
+
out.push(name.replace(/\*$/, ''));
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Free what Docker holds that can be rebuilt or pulled again.
|
|
75
|
+
*
|
|
76
|
+
* @param exec - docker runner (tests)
|
|
77
|
+
* @returns What was done
|
|
78
|
+
*/
|
|
79
|
+
export async function reclaimDocker(exec = defaultExec) {
|
|
80
|
+
const result = { ran: false, removedImages: [] };
|
|
81
|
+
try {
|
|
82
|
+
await exec(['info', '--format', '{{.ServerVersion}}'], 15_000);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
result.ran = true;
|
|
88
|
+
const listing = await exec(['images', '--format', '{{.Repository}} {{.Tag}}'], 30_000).catch(() => '');
|
|
89
|
+
for (const ref of olderRegistryTags(listing)) {
|
|
90
|
+
const ok = await exec(['rmi', ref], 60_000).then(() => true, () => false);
|
|
91
|
+
if (ok)
|
|
92
|
+
result.removedImages.push(ref);
|
|
93
|
+
}
|
|
94
|
+
await exec(['image', 'prune', '-f'], 120_000).catch(() => '');
|
|
95
|
+
await exec(['builder', 'prune', '-f', '--keep-storage', DOCKER_RECLAIM_KEEP_STORAGE], 300_000).catch(() => '');
|
|
96
|
+
const builders = containerBuilders(await exec(['buildx', 'ls'], 30_000).catch(() => ''));
|
|
97
|
+
for (const b of builders) {
|
|
98
|
+
await exec(['buildx', 'prune', '--builder', b, '-f', '--keep-storage', DOCKER_RECLAIM_KEEP_STORAGE], 300_000).catch(() => '');
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=docker-reclaim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-reclaim.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/docker-reclaim.ts"],"names":[],"mappings":"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"}
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
*/
|
|
73
73
|
import { type StatFsFn } from './low-disk-guard.js';
|
|
74
74
|
import { type ScratchSweepSummary } from './scratch-janitor.js';
|
|
75
|
+
import { type DockerReclaimResult } from './docker-reclaim.js';
|
|
75
76
|
import { type TraceDiskResult } from '../trace/trace-store.js';
|
|
76
77
|
/**
|
|
77
78
|
* Why a worktree is kept, or why it may go. (`not-agent-worktree` is retired:
|
|
@@ -200,6 +201,8 @@ export interface WorktreeJanitorOptions {
|
|
|
200
201
|
sizeOf?: (p: string) => Promise<number | null>;
|
|
201
202
|
/** Low-disk notice state file (default `<CREWLY_HOME>/disk-janitor-state.json`); null = in memory */
|
|
202
203
|
statePath?: string | null;
|
|
204
|
+
/** Docker reclaim on low disk (default: the real one, except under jest); null = never */
|
|
205
|
+
reclaimDocker?: (() => Promise<DockerReclaimResult>) | null;
|
|
203
206
|
/** Environment for the kill switch (default process.env) */
|
|
204
207
|
env?: NodeJS.ProcessEnv;
|
|
205
208
|
/** Logger (default: component logger `WorktreeJanitor`) */
|
|
@@ -314,6 +317,11 @@ export declare class WorktreeJanitorService {
|
|
|
314
317
|
private readNoticeState;
|
|
315
318
|
private writeNoticeState;
|
|
316
319
|
private doCheckDisk;
|
|
320
|
+
/**
|
|
321
|
+
* Docker images and build cache that can be pulled or rebuilt again
|
|
322
|
+
* (docker-reclaim.ts). Never throws.
|
|
323
|
+
*/
|
|
324
|
+
private reclaimDockerSpace;
|
|
317
325
|
/** Sizes of everything a pass left on disk that the owner could act on. */
|
|
318
326
|
private keptItems;
|
|
319
327
|
private git;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktree-janitor.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/worktree-janitor.service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"worktree-janitor.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/worktree-janitor.service.ts"],"names":[],"mappings":"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"}
|