@provos/ironcurtain 0.5.1 → 0.7.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/README.md +85 -66
- package/dist/cli-help.d.ts +63 -0
- package/dist/cli-help.js +102 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.js +78 -52
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +136 -2
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/generated/tool-annotations.json +487 -338
- package/dist/config/index.d.ts +21 -1
- package/dist/config/index.js +12 -4
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-servers.json +2 -8
- package/dist/config/paths.d.ts +30 -0
- package/dist/config/paths.js +58 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/tool-description-hints.json +15 -0
- package/dist/config/types.d.ts +13 -0
- package/dist/config/user-config.d.ts +21 -0
- package/dist/config/user-config.js +8 -0
- package/dist/config/user-config.js.map +1 -1
- package/dist/cron/compile-task-policy.d.ts +17 -0
- package/dist/cron/compile-task-policy.js +39 -0
- package/dist/cron/compile-task-policy.js.map +1 -0
- package/dist/cron/cron-scheduler.d.ts +65 -0
- package/dist/cron/cron-scheduler.js +184 -0
- package/dist/cron/cron-scheduler.js.map +1 -0
- package/dist/cron/file-lock.d.ts +41 -0
- package/dist/cron/file-lock.js +156 -0
- package/dist/cron/file-lock.js.map +1 -0
- package/dist/cron/format-utils.d.ts +18 -0
- package/dist/cron/format-utils.js +145 -0
- package/dist/cron/format-utils.js.map +1 -0
- package/dist/cron/git-sync.d.ts +22 -0
- package/dist/cron/git-sync.js +121 -0
- package/dist/cron/git-sync.js.map +1 -0
- package/dist/cron/headless-transport.d.ts +27 -0
- package/dist/cron/headless-transport.js +43 -0
- package/dist/cron/headless-transport.js.map +1 -0
- package/dist/cron/job-commands.d.ts +39 -0
- package/dist/cron/job-commands.js +522 -0
- package/dist/cron/job-commands.js.map +1 -0
- package/dist/cron/job-store.d.ts +35 -0
- package/dist/cron/job-store.js +111 -0
- package/dist/cron/job-store.js.map +1 -0
- package/dist/cron/types.d.ts +127 -0
- package/dist/cron/types.js +25 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/daemon/control-socket.d.ts +127 -0
- package/dist/daemon/control-socket.js +261 -0
- package/dist/daemon/control-socket.js.map +1 -0
- package/dist/daemon/daemon-command.d.ts +13 -0
- package/dist/daemon/daemon-command.js +230 -0
- package/dist/daemon/daemon-command.js.map +1 -0
- package/dist/daemon/ironcurtain-daemon.d.ts +90 -0
- package/dist/daemon/ironcurtain-daemon.js +548 -0
- package/dist/daemon/ironcurtain-daemon.js.map +1 -0
- package/dist/docker/adapters/claude-code.js +8 -48
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/adapters/goose.d.ts +58 -0
- package/dist/docker/adapters/goose.js +250 -0
- package/dist/docker/adapters/goose.js.map +1 -0
- package/dist/docker/adapters/shared-scripts.d.ts +29 -0
- package/dist/docker/adapters/shared-scripts.js +80 -0
- package/dist/docker/adapters/shared-scripts.js.map +1 -0
- package/dist/docker/agent-adapter.d.ts +12 -0
- package/dist/docker/agent-adapter.js.map +1 -1
- package/dist/docker/agent-registry.d.ts +6 -1
- package/dist/docker/agent-registry.js +10 -1
- package/dist/docker/agent-registry.js.map +1 -1
- package/dist/docker/docker-agent-session.d.ts +2 -25
- package/dist/docker/docker-agent-session.js +7 -106
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +8 -0
- package/dist/docker/docker-infrastructure.js +22 -14
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/mitm-proxy.d.ts +5 -2
- package/dist/docker/mitm-proxy.js +169 -46
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +15 -2
- package/dist/docker/oauth-credentials.js +92 -8
- package/dist/docker/oauth-credentials.js.map +1 -1
- package/dist/docker/oauth-token-manager.d.ts +80 -0
- package/dist/docker/oauth-token-manager.js +133 -0
- package/dist/docker/oauth-token-manager.js.map +1 -0
- package/dist/docker/pty-session.d.ts +2 -0
- package/dist/docker/pty-session.js +17 -8
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/escalation/escalation-watcher.d.ts +2 -0
- package/dist/escalation/escalation-watcher.js +5 -0
- package/dist/escalation/escalation-watcher.js.map +1 -1
- package/dist/escalation/listener-command.js +9 -58
- package/dist/escalation/listener-command.js.map +1 -1
- package/dist/escalation/listener-lock.d.ts +20 -0
- package/dist/escalation/listener-lock.js +69 -0
- package/dist/escalation/listener-lock.js.map +1 -0
- package/dist/escalation/session-registry.js +1 -14
- package/dist/escalation/session-registry.js.map +1 -1
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/mux/mux-app.d.ts +24 -0
- package/dist/mux/mux-app.js +450 -0
- package/dist/mux/mux-app.js.map +1 -0
- package/dist/mux/mux-command.d.ts +7 -0
- package/dist/mux/mux-command.js +104 -0
- package/dist/mux/mux-command.js.map +1 -0
- package/dist/mux/mux-escalation-manager.d.ts +48 -0
- package/dist/mux/mux-escalation-manager.js +162 -0
- package/dist/mux/mux-escalation-manager.js.map +1 -0
- package/dist/mux/mux-input-handler.d.ts +54 -0
- package/dist/mux/mux-input-handler.js +512 -0
- package/dist/mux/mux-input-handler.js.map +1 -0
- package/dist/mux/mux-renderer.d.ts +106 -0
- package/dist/mux/mux-renderer.js +850 -0
- package/dist/mux/mux-renderer.js.map +1 -0
- package/dist/mux/mux-splash.d.ts +17 -0
- package/dist/mux/mux-splash.js +323 -0
- package/dist/mux/mux-splash.js.map +1 -0
- package/dist/mux/paste-interceptor.d.ts +22 -0
- package/dist/mux/paste-interceptor.js +143 -0
- package/dist/mux/paste-interceptor.js.map +1 -0
- package/dist/mux/pty-bridge.d.ts +79 -0
- package/dist/mux/pty-bridge.js +151 -0
- package/dist/mux/pty-bridge.js.map +1 -0
- package/dist/mux/trusted-input.d.ts +27 -0
- package/dist/mux/trusted-input.js +27 -0
- package/dist/mux/trusted-input.js.map +1 -0
- package/dist/mux/types.d.ts +99 -0
- package/dist/mux/types.js +57 -0
- package/dist/mux/types.js.map +1 -0
- package/dist/pipeline/annotate.js +2 -1
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/compile.d.ts +4 -34
- package/dist/pipeline/compile.js +29 -568
- package/dist/pipeline/compile.js.map +1 -1
- package/dist/pipeline/constitution-compiler.js +1 -1
- package/dist/pipeline/mcp-connections.d.ts +16 -0
- package/dist/pipeline/mcp-connections.js +61 -0
- package/dist/pipeline/mcp-connections.js.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +86 -0
- package/dist/pipeline/pipeline-runner.js +554 -0
- package/dist/pipeline/pipeline-runner.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +29 -2
- package/dist/pipeline/pipeline-shared.js +68 -12
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/refresh-lists.js +38 -12
- package/dist/pipeline/refresh-lists.js.map +1 -1
- package/dist/pipeline/scenario-generator.js +2 -1
- package/dist/pipeline/scenario-generator.js.map +1 -1
- package/dist/pipeline/tool-annotator.d.ts +3 -3
- package/dist/pipeline/tool-annotator.js +96 -4
- package/dist/pipeline/tool-annotator.js.map +1 -1
- package/dist/pipeline/types.d.ts +52 -0
- package/dist/sandbox/index.js +40 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/session/agent-session.d.ts +3 -0
- package/dist/session/agent-session.js +13 -1
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/index.js +65 -9
- package/dist/session/index.js.map +1 -1
- package/dist/session/preflight.js +26 -6
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/prompts.d.ts +14 -0
- package/dist/session/prompts.js +39 -2
- package/dist/session/prompts.js.map +1 -1
- package/dist/session/session-manager.d.ts +105 -0
- package/dist/session/session-manager.js +134 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +33 -0
- package/dist/session/workspace-validation.d.ts +19 -0
- package/dist/session/workspace-validation.js +66 -0
- package/dist/session/workspace-validation.js.map +1 -0
- package/dist/signal/bot-command.d.ts +2 -0
- package/dist/signal/bot-command.js +8 -0
- package/dist/signal/bot-command.js.map +1 -1
- package/dist/signal/format.d.ts +27 -0
- package/dist/signal/format.js +59 -0
- package/dist/signal/format.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +27 -25
- package/dist/signal/signal-bot-daemon.js +145 -130
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-container.js +63 -19
- package/dist/signal/signal-container.js.map +1 -1
- package/dist/signal/signal-transport.js +3 -8
- package/dist/signal/signal-transport.js.map +1 -1
- package/dist/trusted-process/auto-approver.d.ts +13 -4
- package/dist/trusted-process/auto-approver.js +12 -5
- package/dist/trusted-process/auto-approver.js.map +1 -1
- package/dist/trusted-process/domain-utils.d.ts +38 -4
- package/dist/trusted-process/domain-utils.js +118 -11
- package/dist/trusted-process/domain-utils.js.map +1 -1
- package/dist/trusted-process/escalation.js +5 -0
- package/dist/trusted-process/escalation.js.map +1 -1
- package/dist/trusted-process/index.js +6 -2
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/mcp-client-manager.js +11 -5
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-proxy-server.d.ts +2 -0
- package/dist/trusted-process/mcp-proxy-server.js +98 -32
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/permissive-output-validator.d.ts +23 -0
- package/dist/trusted-process/permissive-output-validator.js +10 -0
- package/dist/trusted-process/permissive-output-validator.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +27 -4
- package/dist/trusted-process/policy-engine.js +152 -57
- package/dist/trusted-process/policy-engine.js.map +1 -1
- package/dist/trusted-process/tool-description-hints.d.ts +19 -0
- package/dist/trusted-process/tool-description-hints.js +53 -0
- package/dist/trusted-process/tool-description-hints.js.map +1 -0
- package/dist/types/argument-roles.d.ts +33 -1
- package/dist/types/argument-roles.js +129 -1
- package/dist/types/argument-roles.js.map +1 -1
- package/docker/Dockerfile.goose +34 -0
- package/docker/entrypoint-goose.sh +30 -0
- package/package.json +6 -1
- package/src/config/generated/tool-annotations.json +487 -338
- package/src/config/mcp-servers.json +2 -8
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CronScheduler -- manages cron schedules for all enabled jobs.
|
|
3
|
+
*
|
|
4
|
+
* Uses a polling approach with 60-second intervals to check
|
|
5
|
+
* whether each job's cron expression matches the current time.
|
|
6
|
+
* When node-cron is available, the implementation can be swapped
|
|
7
|
+
* to use it for more efficient scheduling.
|
|
8
|
+
*/
|
|
9
|
+
import * as logger from '../logger.js';
|
|
10
|
+
/**
|
|
11
|
+
* Thrown when a cron expression cannot be parsed.
|
|
12
|
+
*/
|
|
13
|
+
export class InvalidCronExpressionError extends Error {
|
|
14
|
+
expression;
|
|
15
|
+
constructor(expression, cause) {
|
|
16
|
+
super(`Invalid cron expression "${expression}": ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
17
|
+
this.expression = expression;
|
|
18
|
+
this.name = 'InvalidCronExpressionError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function parseField(field, min, max) {
|
|
22
|
+
const values = new Set();
|
|
23
|
+
for (const part of field.split(',')) {
|
|
24
|
+
const stepMatch = part.match(/^(.+)\/(\d+)$/);
|
|
25
|
+
const step = stepMatch ? parseInt(stepMatch[2], 10) : 1;
|
|
26
|
+
if (stepMatch && (isNaN(step) || step < 1)) {
|
|
27
|
+
throw new Error(`Invalid step "${stepMatch[2]}" in "${part}" (expected integer >= 1)`);
|
|
28
|
+
}
|
|
29
|
+
const range = stepMatch ? stepMatch[1] : part;
|
|
30
|
+
if (range === '*') {
|
|
31
|
+
for (let i = min; i <= max; i += step)
|
|
32
|
+
values.add(i);
|
|
33
|
+
}
|
|
34
|
+
else if (range.includes('-')) {
|
|
35
|
+
const [startStr, endStr] = range.split('-');
|
|
36
|
+
const start = parseInt(startStr, 10);
|
|
37
|
+
const end = parseInt(endStr, 10);
|
|
38
|
+
if (isNaN(start) || isNaN(end) || start < min || end > max || start > end) {
|
|
39
|
+
throw new Error(`Invalid range "${range}" (expected ${min}-${max})`);
|
|
40
|
+
}
|
|
41
|
+
for (let i = start; i <= end; i += step)
|
|
42
|
+
values.add(i);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const val = parseInt(range, 10);
|
|
46
|
+
if (isNaN(val) || val < min || val > max) {
|
|
47
|
+
throw new Error(`Invalid value "${range}" (expected ${min}-${max})`);
|
|
48
|
+
}
|
|
49
|
+
values.add(val);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return values;
|
|
53
|
+
}
|
|
54
|
+
function parseCronExpression(expression) {
|
|
55
|
+
const parts = expression.trim().split(/\s+/);
|
|
56
|
+
if (parts.length !== 5) {
|
|
57
|
+
throw new Error(`Expected 5 fields (min hour dom mon dow), got ${parts.length}`);
|
|
58
|
+
}
|
|
59
|
+
const daysOfWeek = parseField(parts[4], 0, 7); // 0 and 7 both mean Sunday
|
|
60
|
+
if (daysOfWeek.has(7)) {
|
|
61
|
+
daysOfWeek.add(0);
|
|
62
|
+
daysOfWeek.delete(7);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
minutes: parseField(parts[0], 0, 59),
|
|
66
|
+
hours: parseField(parts[1], 0, 23),
|
|
67
|
+
daysOfMonth: parseField(parts[2], 1, 31),
|
|
68
|
+
months: parseField(parts[3], 1, 12),
|
|
69
|
+
daysOfWeek,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function cronMatchesTime(fields, date) {
|
|
73
|
+
const minute = date.getMinutes();
|
|
74
|
+
const hour = date.getHours();
|
|
75
|
+
const dayOfMonth = date.getDate();
|
|
76
|
+
const month = date.getMonth() + 1; // 0-indexed -> 1-indexed
|
|
77
|
+
const dayOfWeek = date.getDay(); // 0 = Sunday
|
|
78
|
+
if (!fields.minutes.has(minute))
|
|
79
|
+
return false;
|
|
80
|
+
if (!fields.hours.has(hour))
|
|
81
|
+
return false;
|
|
82
|
+
if (!fields.months.has(month))
|
|
83
|
+
return false;
|
|
84
|
+
// Standard cron: if both dom and dow are restricted (not *), match either
|
|
85
|
+
const domIsWildcard = fields.daysOfMonth.size === 31;
|
|
86
|
+
const dowIsWildcard = fields.daysOfWeek.size === 7;
|
|
87
|
+
if (domIsWildcard && dowIsWildcard)
|
|
88
|
+
return true;
|
|
89
|
+
if (domIsWildcard)
|
|
90
|
+
return fields.daysOfWeek.has(dayOfWeek);
|
|
91
|
+
if (dowIsWildcard)
|
|
92
|
+
return fields.daysOfMonth.has(dayOfMonth);
|
|
93
|
+
// Both restricted: OR logic (standard cron behavior)
|
|
94
|
+
return fields.daysOfMonth.has(dayOfMonth) || fields.daysOfWeek.has(dayOfWeek);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Calculates the next time a cron expression would fire after the given date.
|
|
98
|
+
* Scans forward minute-by-minute (up to 366 days).
|
|
99
|
+
*/
|
|
100
|
+
function getNextFireTime(fields, after) {
|
|
101
|
+
const candidate = new Date(after);
|
|
102
|
+
// Start from the next minute
|
|
103
|
+
candidate.setSeconds(0, 0);
|
|
104
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
105
|
+
const maxIterations = 366 * 24 * 60; // One year of minutes
|
|
106
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
107
|
+
if (cronMatchesTime(fields, candidate)) {
|
|
108
|
+
return candidate;
|
|
109
|
+
}
|
|
110
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
111
|
+
}
|
|
112
|
+
// Should not happen for valid expressions, but return a far-future date
|
|
113
|
+
return new Date(after.getTime() + 366 * 24 * 60 * 60 * 1000);
|
|
114
|
+
}
|
|
115
|
+
/** Creates a CronScheduler with built-in cron expression parsing. */
|
|
116
|
+
export function createCronScheduler() {
|
|
117
|
+
const jobs = new Map();
|
|
118
|
+
let pollInterval = null;
|
|
119
|
+
function startPolling() {
|
|
120
|
+
if (pollInterval)
|
|
121
|
+
return;
|
|
122
|
+
// Poll every 30 seconds (within the 60-second cron granularity)
|
|
123
|
+
pollInterval = setInterval(() => tick(), 30_000);
|
|
124
|
+
pollInterval.unref(); // Don't keep the process alive just for polling
|
|
125
|
+
}
|
|
126
|
+
function stopPolling() {
|
|
127
|
+
if (pollInterval) {
|
|
128
|
+
clearInterval(pollInterval);
|
|
129
|
+
pollInterval = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function tick() {
|
|
133
|
+
const now = new Date();
|
|
134
|
+
// Truncate to the current minute for comparison
|
|
135
|
+
now.setSeconds(0, 0);
|
|
136
|
+
for (const [jobId, scheduled] of jobs.entries()) {
|
|
137
|
+
if (scheduled.lastFired && scheduled.lastFired.getTime() === now.getTime()) {
|
|
138
|
+
continue; // Already fired this minute
|
|
139
|
+
}
|
|
140
|
+
if (cronMatchesTime(scheduled.fields, now)) {
|
|
141
|
+
scheduled.lastFired = now;
|
|
142
|
+
logger.info(`[CronScheduler] Firing job ${jobId} at ${now.toISOString()}`);
|
|
143
|
+
scheduled.callback(scheduled.job).catch((err) => {
|
|
144
|
+
logger.warn(`[CronScheduler] Job ${jobId} callback failed: ${String(err)}`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
schedule(job, onTrigger) {
|
|
151
|
+
let fields;
|
|
152
|
+
try {
|
|
153
|
+
fields = parseCronExpression(job.schedule);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
throw new InvalidCronExpressionError(job.schedule, err);
|
|
157
|
+
}
|
|
158
|
+
// Replace any existing schedule for this job
|
|
159
|
+
jobs.set(job.id, { job, fields, callback: onTrigger, lastFired: null });
|
|
160
|
+
if (jobs.size === 1) {
|
|
161
|
+
startPolling();
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
unschedule(jobId) {
|
|
165
|
+
jobs.delete(jobId);
|
|
166
|
+
if (jobs.size === 0) {
|
|
167
|
+
stopPolling();
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
unscheduleAll() {
|
|
171
|
+
jobs.clear();
|
|
172
|
+
stopPolling();
|
|
173
|
+
},
|
|
174
|
+
getNextRun(jobId) {
|
|
175
|
+
const scheduled = jobs.get(jobId);
|
|
176
|
+
if (!scheduled)
|
|
177
|
+
return undefined;
|
|
178
|
+
return getNextFireTime(scheduled.fields, new Date());
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Export for testing
|
|
183
|
+
export { parseCronExpression, cronMatchesTime, getNextFireTime };
|
|
184
|
+
//# sourceMappingURL=cron-scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-scheduler.js","sourceRoot":"","sources":["../../src/cron/cron-scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisory file locking for the job store.
|
|
3
|
+
*
|
|
4
|
+
* Uses mkdir (atomic on POSIX) to create a lock directory next to the
|
|
5
|
+
* target file. The lock directory contains a metadata file with the
|
|
6
|
+
* owning PID and timestamp, enabling stale lock detection when a
|
|
7
|
+
* process crashes without releasing.
|
|
8
|
+
*
|
|
9
|
+
* This is designed for short-lived critical sections (read-modify-write),
|
|
10
|
+
* not long-held process-level locks. For process singleton enforcement,
|
|
11
|
+
* see src/escalation/listener-lock.ts.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Releases an advisory lock. Safe to call even if the lock is not held.
|
|
15
|
+
*/
|
|
16
|
+
export declare function releaseLock(lockDir: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Acquires an advisory lock, retrying until timeout.
|
|
19
|
+
*
|
|
20
|
+
* @param lockDir Path to the lock directory (typically `{target}.lock`)
|
|
21
|
+
* @param staleMs Time in ms after which an unreleased lock is considered stale
|
|
22
|
+
* @returns true if acquired, false if timed out
|
|
23
|
+
*/
|
|
24
|
+
export declare function acquireLock(lockDir: string, staleMs?: number): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Executes a function while holding an advisory file lock.
|
|
27
|
+
*
|
|
28
|
+
* Acquires the lock, runs the function, and releases the lock in a
|
|
29
|
+
* finally block. Throws if the lock cannot be acquired within the
|
|
30
|
+
* timeout period.
|
|
31
|
+
*
|
|
32
|
+
* @param lockDir Path to the lock directory
|
|
33
|
+
* @param fn The function to execute under lock
|
|
34
|
+
* @param staleMs Stale lock timeout (default 30s)
|
|
35
|
+
*/
|
|
36
|
+
export declare function withFileLock<T>(lockDir: string, fn: () => T | Promise<T>, staleMs?: number): Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Returns the conventional lock directory path for a job directory.
|
|
39
|
+
* The lock is placed at `{jobDir}/job.json.lock/`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getJobLockDir(jobDir: string): string;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisory file locking for the job store.
|
|
3
|
+
*
|
|
4
|
+
* Uses mkdir (atomic on POSIX) to create a lock directory next to the
|
|
5
|
+
* target file. The lock directory contains a metadata file with the
|
|
6
|
+
* owning PID and timestamp, enabling stale lock detection when a
|
|
7
|
+
* process crashes without releasing.
|
|
8
|
+
*
|
|
9
|
+
* This is designed for short-lived critical sections (read-modify-write),
|
|
10
|
+
* not long-held process-level locks. For process singleton enforcement,
|
|
11
|
+
* see src/escalation/listener-lock.ts.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { resolve } from 'node:path';
|
|
15
|
+
import { isPidAlive } from '../escalation/listener-lock.js';
|
|
16
|
+
/** Default stale timeout: 30 seconds. */
|
|
17
|
+
const DEFAULT_STALE_MS = 30_000;
|
|
18
|
+
/** Retry interval when waiting for a lock. */
|
|
19
|
+
const RETRY_INTERVAL_MS = 50;
|
|
20
|
+
/** Maximum time to wait for a lock before giving up. */
|
|
21
|
+
const MAX_WAIT_MS = 10_000;
|
|
22
|
+
/**
|
|
23
|
+
* Attempts to acquire an advisory lock for the given path.
|
|
24
|
+
* Returns true if the lock was acquired, false otherwise.
|
|
25
|
+
*
|
|
26
|
+
* The lock is a directory at `{path}.lock/` containing a `meta.json`
|
|
27
|
+
* with the owning PID and creation timestamp.
|
|
28
|
+
*/
|
|
29
|
+
function tryAcquire(lockDir, staleMs) {
|
|
30
|
+
// Attempt atomic mkdir first — this is the authoritative lock acquisition.
|
|
31
|
+
// Only check for stale locks after EEXIST, avoiding a TOCTOU race.
|
|
32
|
+
try {
|
|
33
|
+
mkdirSync(lockDir);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err.code !== 'EEXIST') {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
// Lock dir exists — check if it's stale
|
|
40
|
+
if (isLockStale(lockDir, staleMs)) {
|
|
41
|
+
forceRelease(lockDir);
|
|
42
|
+
// Retry mkdir after releasing stale lock
|
|
43
|
+
try {
|
|
44
|
+
mkdirSync(lockDir);
|
|
45
|
+
}
|
|
46
|
+
catch (retryErr) {
|
|
47
|
+
if (retryErr.code === 'EEXIST') {
|
|
48
|
+
return false; // Another process grabbed it
|
|
49
|
+
}
|
|
50
|
+
throw retryErr;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Write metadata for stale detection
|
|
58
|
+
const meta = { pid: process.pid, timestamp: Date.now() };
|
|
59
|
+
try {
|
|
60
|
+
writeFileSync(resolve(lockDir, 'meta.json'), JSON.stringify(meta));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// If we can't write metadata, release and fail
|
|
64
|
+
forceRelease(lockDir);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Checks whether an existing lock is stale (owner dead or timed out).
|
|
71
|
+
*/
|
|
72
|
+
function isLockStale(lockDir, staleMs) {
|
|
73
|
+
const metaPath = resolve(lockDir, 'meta.json');
|
|
74
|
+
try {
|
|
75
|
+
const raw = readFileSync(metaPath, 'utf-8');
|
|
76
|
+
const meta = JSON.parse(raw);
|
|
77
|
+
// Check if the owning process is still alive
|
|
78
|
+
if (!isPidAlive(meta.pid)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
// Check if the lock has exceeded the stale timeout
|
|
82
|
+
if (Date.now() - meta.timestamp > staleMs) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// No metadata or unreadable -- treat as stale
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Force-removes a lock directory. Best-effort.
|
|
94
|
+
*/
|
|
95
|
+
function forceRelease(lockDir) {
|
|
96
|
+
try {
|
|
97
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* best effort */
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Releases an advisory lock. Safe to call even if the lock is not held.
|
|
105
|
+
*/
|
|
106
|
+
export function releaseLock(lockDir) {
|
|
107
|
+
forceRelease(lockDir);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Acquires an advisory lock, retrying until timeout.
|
|
111
|
+
*
|
|
112
|
+
* @param lockDir Path to the lock directory (typically `{target}.lock`)
|
|
113
|
+
* @param staleMs Time in ms after which an unreleased lock is considered stale
|
|
114
|
+
* @returns true if acquired, false if timed out
|
|
115
|
+
*/
|
|
116
|
+
export async function acquireLock(lockDir, staleMs = DEFAULT_STALE_MS) {
|
|
117
|
+
const deadline = Date.now() + MAX_WAIT_MS;
|
|
118
|
+
while (Date.now() < deadline) {
|
|
119
|
+
if (tryAcquire(lockDir, staleMs)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL_MS));
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Executes a function while holding an advisory file lock.
|
|
128
|
+
*
|
|
129
|
+
* Acquires the lock, runs the function, and releases the lock in a
|
|
130
|
+
* finally block. Throws if the lock cannot be acquired within the
|
|
131
|
+
* timeout period.
|
|
132
|
+
*
|
|
133
|
+
* @param lockDir Path to the lock directory
|
|
134
|
+
* @param fn The function to execute under lock
|
|
135
|
+
* @param staleMs Stale lock timeout (default 30s)
|
|
136
|
+
*/
|
|
137
|
+
export async function withFileLock(lockDir, fn, staleMs = DEFAULT_STALE_MS) {
|
|
138
|
+
const acquired = await acquireLock(lockDir, staleMs);
|
|
139
|
+
if (!acquired) {
|
|
140
|
+
throw new Error(`Failed to acquire file lock: ${lockDir} (timed out after ${MAX_WAIT_MS}ms)`);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
return await fn();
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
releaseLock(lockDir);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns the conventional lock directory path for a job directory.
|
|
151
|
+
* The lock is placed at `{jobDir}/job.json.lock/`.
|
|
152
|
+
*/
|
|
153
|
+
export function getJobLockDir(jobDir) {
|
|
154
|
+
return resolve(jobDir, 'job.json.lock');
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/cron/file-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for cron job CLI output.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Formats a duration in seconds as a compact string like "1d 2h 30m".
|
|
6
|
+
* Shows up to two units for brevity (e.g., "2h 15m" not "2h 15m 30s").
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatDuration(seconds: number): string;
|
|
9
|
+
/**
|
|
10
|
+
* Formats a date as a relative time string with an absolute time suffix.
|
|
11
|
+
* Examples: "in 2h 15m (Mar 5, 09:00)", "3h ago (Mar 5, 06:00)", "just now (Mar 5, 09:00)"
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatRelativeTime(date: Date, now?: Date): string;
|
|
14
|
+
/**
|
|
15
|
+
* Converts a 5-field cron expression into a human-readable English description.
|
|
16
|
+
* Covers common patterns well; exotic expressions get a best-effort description.
|
|
17
|
+
*/
|
|
18
|
+
export declare function describeCronExpression(expr: string): string;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for cron job CLI output.
|
|
3
|
+
*/
|
|
4
|
+
import { parseCronExpression } from './cron-scheduler.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Shared constants
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Duration and relative time display
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Formats a duration in seconds as a compact string like "1d 2h 30m".
|
|
14
|
+
* Shows up to two units for brevity (e.g., "2h 15m" not "2h 15m 30s").
|
|
15
|
+
*/
|
|
16
|
+
export function formatDuration(seconds) {
|
|
17
|
+
const minutes = Math.floor(seconds / 60);
|
|
18
|
+
const hours = Math.floor(minutes / 60);
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (days > 0)
|
|
22
|
+
parts.push(`${days}d`);
|
|
23
|
+
if (hours % 24 > 0)
|
|
24
|
+
parts.push(`${hours % 24}h`);
|
|
25
|
+
if (minutes % 60 > 0 && days === 0)
|
|
26
|
+
parts.push(`${minutes % 60}m`);
|
|
27
|
+
if (parts.length === 0)
|
|
28
|
+
parts.push('0m');
|
|
29
|
+
return parts.join(' ');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Formats a date as a relative time string with an absolute time suffix.
|
|
33
|
+
* Examples: "in 2h 15m (Mar 5, 09:00)", "3h ago (Mar 5, 06:00)", "just now (Mar 5, 09:00)"
|
|
34
|
+
*/
|
|
35
|
+
export function formatRelativeTime(date, now = new Date()) {
|
|
36
|
+
const diffMs = date.getTime() - now.getTime();
|
|
37
|
+
const absDiffSec = Math.floor(Math.abs(diffMs) / 1000);
|
|
38
|
+
const absolute = formatAbsoluteTime(date);
|
|
39
|
+
if (absDiffSec < 60)
|
|
40
|
+
return `just now (${absolute})`;
|
|
41
|
+
const dur = formatDuration(absDiffSec);
|
|
42
|
+
const relative = diffMs >= 0 ? `in ${dur}` : `${dur} ago`;
|
|
43
|
+
return `${relative} (${absolute})`;
|
|
44
|
+
}
|
|
45
|
+
function formatAbsoluteTime(date) {
|
|
46
|
+
const month = MONTH_NAMES[date.getMonth()];
|
|
47
|
+
const day = date.getDate();
|
|
48
|
+
const hours = date.getHours().toString().padStart(2, '0');
|
|
49
|
+
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
50
|
+
return `${month} ${day}, ${hours}:${minutes}`;
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Cron expression description
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
56
|
+
/**
|
|
57
|
+
* Converts a 5-field cron expression into a human-readable English description.
|
|
58
|
+
* Covers common patterns well; exotic expressions get a best-effort description.
|
|
59
|
+
*/
|
|
60
|
+
export function describeCronExpression(expr) {
|
|
61
|
+
let fields;
|
|
62
|
+
try {
|
|
63
|
+
fields = parseCronExpression(expr);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return expr; // fallback to raw expression if unparseable
|
|
67
|
+
}
|
|
68
|
+
const parts = [];
|
|
69
|
+
// Time part
|
|
70
|
+
parts.push(describeTimePart(expr, fields));
|
|
71
|
+
// Day-of-week part
|
|
72
|
+
const dowPart = describeDaysOfWeek(fields);
|
|
73
|
+
if (dowPart)
|
|
74
|
+
parts.push(dowPart);
|
|
75
|
+
// Day-of-month part
|
|
76
|
+
const domPart = describeDaysOfMonth(fields);
|
|
77
|
+
if (domPart)
|
|
78
|
+
parts.push(domPart);
|
|
79
|
+
// Month part
|
|
80
|
+
const monthPart = describeMonths(fields);
|
|
81
|
+
if (monthPart)
|
|
82
|
+
parts.push(monthPart);
|
|
83
|
+
return parts.join(', ');
|
|
84
|
+
}
|
|
85
|
+
function describeTimePart(expr, fields) {
|
|
86
|
+
const rawParts = expr.trim().split(/\s+/);
|
|
87
|
+
const minField = rawParts[0];
|
|
88
|
+
const hourField = rawParts[1];
|
|
89
|
+
// Every N minutes
|
|
90
|
+
const minStep = minField.match(/^\*\/(\d+)$/);
|
|
91
|
+
if (minStep && hourField === '*') {
|
|
92
|
+
return `Every ${minStep[1]} minutes`;
|
|
93
|
+
}
|
|
94
|
+
// Every hour at specific minute
|
|
95
|
+
if (fields.minutes.size === 1 && hourField === '*') {
|
|
96
|
+
const min = [...fields.minutes][0];
|
|
97
|
+
return min === 0 ? 'Every hour' : `Every hour at :${min.toString().padStart(2, '0')}`;
|
|
98
|
+
}
|
|
99
|
+
// Every N hours
|
|
100
|
+
const hourStep = hourField.match(/^\*\/(\d+)$/);
|
|
101
|
+
if (hourStep && fields.minutes.size === 1) {
|
|
102
|
+
const min = [...fields.minutes][0];
|
|
103
|
+
return `Every ${hourStep[1]} hours at :${min.toString().padStart(2, '0')}`;
|
|
104
|
+
}
|
|
105
|
+
// Specific time(s)
|
|
106
|
+
if (fields.hours.size <= 3 && fields.minutes.size === 1) {
|
|
107
|
+
const min = [...fields.minutes][0];
|
|
108
|
+
const times = [...fields.hours]
|
|
109
|
+
.sort((a, b) => a - b)
|
|
110
|
+
.map((h) => `${h.toString().padStart(2, '0')}:${min.toString().padStart(2, '0')}`);
|
|
111
|
+
return `At ${times.join(', ')}`;
|
|
112
|
+
}
|
|
113
|
+
// Fallback: describe minutes and hours separately
|
|
114
|
+
return `At minute ${[...fields.minutes].sort((a, b) => a - b).join(',')} of hour ${[...fields.hours].sort((a, b) => a - b).join(',')}`;
|
|
115
|
+
}
|
|
116
|
+
function describeDaysOfWeek(fields) {
|
|
117
|
+
if (fields.daysOfWeek.size === 7)
|
|
118
|
+
return null; // wildcard
|
|
119
|
+
const days = [...fields.daysOfWeek].sort((a, b) => a - b);
|
|
120
|
+
// Check for contiguous range
|
|
121
|
+
if (days.length >= 2 && isContiguous(days)) {
|
|
122
|
+
return `${DAY_NAMES[days[0]]}--${DAY_NAMES[days[days.length - 1]]}`;
|
|
123
|
+
}
|
|
124
|
+
return days.map((d) => DAY_NAMES[d]).join(', ');
|
|
125
|
+
}
|
|
126
|
+
function describeDaysOfMonth(fields) {
|
|
127
|
+
if (fields.daysOfMonth.size === 31)
|
|
128
|
+
return null; // wildcard
|
|
129
|
+
const days = [...fields.daysOfMonth].sort((a, b) => a - b);
|
|
130
|
+
return `on day ${days.join(', ')} of the month`;
|
|
131
|
+
}
|
|
132
|
+
function describeMonths(fields) {
|
|
133
|
+
if (fields.months.size === 12)
|
|
134
|
+
return null; // wildcard
|
|
135
|
+
const months = [...fields.months].sort((a, b) => a - b);
|
|
136
|
+
return `in ${months.map((m) => MONTH_NAMES[m - 1]).join(', ')}`;
|
|
137
|
+
}
|
|
138
|
+
function isContiguous(sorted) {
|
|
139
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
140
|
+
if (sorted[i] !== sorted[i - 1] + 1)
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=format-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-utils.js","sourceRoot":"","sources":["../../src/cron/format-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a git URI uses a safe transport protocol.
|
|
3
|
+
*
|
|
4
|
+
* Rejects dangerous protocols like ext:: which allow arbitrary
|
|
5
|
+
* command execution, and any other unrecognized scheme.
|
|
6
|
+
*
|
|
7
|
+
* @throws {Error} if the URI uses a disallowed protocol
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateGitUri(uri: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Clones or refreshes a git repository in the given directory.
|
|
12
|
+
*
|
|
13
|
+
* - First call: clones the repo into dir (dir must exist and be empty)
|
|
14
|
+
* - Subsequent calls: fetches from origin and resets tracked files to
|
|
15
|
+
* FETCH_HEAD, leaving untracked files (agent artifacts) intact.
|
|
16
|
+
*
|
|
17
|
+
* @param verbose Pass true to inherit stdio (show git output in terminal)
|
|
18
|
+
* @returns diff --stat of tracked-file changes discarded by a hard reset,
|
|
19
|
+
* or null on first clone / when there are no local changes.
|
|
20
|
+
* @throws {Error} if the URI uses a disallowed protocol
|
|
21
|
+
*/
|
|
22
|
+
export declare function syncGitRepo(uri: string, dir: string, verbose?: boolean): string | null;
|