@sigil0/looking-glass 0.1.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/LICENSE +21 -0
- package/README.md +572 -0
- package/dist/agents/coordinator.d.ts +21 -0
- package/dist/agents/coordinator.d.ts.map +1 -0
- package/dist/agents/coordinator.js +171 -0
- package/dist/agents/coordinator.js.map +1 -0
- package/dist/app.d.ts +44 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +215 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +419 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/engine/context.d.ts +6 -0
- package/dist/engine/context.d.ts.map +1 -0
- package/dist/engine/context.js +65 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/engine.d.ts +74 -0
- package/dist/engine/engine.d.ts.map +1 -0
- package/dist/engine/engine.js +573 -0
- package/dist/engine/engine.js.map +1 -0
- package/dist/engine/types.d.ts +35 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +2 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/instructions.d.ts +8 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +60 -0
- package/dist/instructions.js.map +1 -0
- package/dist/model/codex-lb.d.ts +141 -0
- package/dist/model/codex-lb.d.ts.map +1 -0
- package/dist/model/codex-lb.js +471 -0
- package/dist/model/codex-lb.js.map +1 -0
- package/dist/paths.d.ts +7 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +41 -0
- package/dist/paths.js.map +1 -0
- package/dist/scheduler/daemon.d.ts +40 -0
- package/dist/scheduler/daemon.d.ts.map +1 -0
- package/dist/scheduler/daemon.js +189 -0
- package/dist/scheduler/daemon.js.map +1 -0
- package/dist/scheduler/index.d.ts +8 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +6 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/runner.d.ts +18 -0
- package/dist/scheduler/runner.d.ts.map +1 -0
- package/dist/scheduler/runner.js +173 -0
- package/dist/scheduler/runner.js.map +1 -0
- package/dist/scheduler/schedule.d.ts +7 -0
- package/dist/scheduler/schedule.d.ts.map +1 -0
- package/dist/scheduler/schedule.js +94 -0
- package/dist/scheduler/schedule.js.map +1 -0
- package/dist/scheduler/store.d.ts +55 -0
- package/dist/scheduler/store.d.ts.map +1 -0
- package/dist/scheduler/store.js +1203 -0
- package/dist/scheduler/store.js.map +1 -0
- package/dist/scheduler/systemd.d.ts +7 -0
- package/dist/scheduler/systemd.d.ts.map +1 -0
- package/dist/scheduler/systemd.js +97 -0
- package/dist/scheduler/systemd.js.map +1 -0
- package/dist/scheduler/types.d.ts +145 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +7 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/security.d.ts +6 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +27 -0
- package/dist/security.js.map +1 -0
- package/dist/storage/artifact-store.d.ts +19 -0
- package/dist/storage/artifact-store.d.ts.map +1 -0
- package/dist/storage/artifact-store.js +47 -0
- package/dist/storage/artifact-store.js.map +1 -0
- package/dist/storage/database.d.ts +5 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +395 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/session-store.d.ts +114 -0
- package/dist/storage/session-store.d.ts.map +1 -0
- package/dist/storage/session-store.js +731 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/tools/agents.d.ts +14 -0
- package/dist/tools/agents.d.ts.map +1 -0
- package/dist/tools/agents.js +35 -0
- package/dist/tools/agents.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +7 -0
- package/dist/tools/apply-patch.d.ts.map +1 -0
- package/dist/tools/apply-patch.js +429 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/ask.d.ts +8 -0
- package/dist/tools/ask.d.ts.map +1 -0
- package/dist/tools/ask.js +27 -0
- package/dist/tools/ask.js.map +1 -0
- package/dist/tools/bash.d.ts +9 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +98 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/paths.d.ts +3 -0
- package/dist/tools/paths.d.ts.map +1 -0
- package/dist/tools/paths.js +34 -0
- package/dist/tools/paths.js.map +1 -0
- package/dist/tools/process.d.ts +21 -0
- package/dist/tools/process.d.ts.map +1 -0
- package/dist/tools/process.js +104 -0
- package/dist/tools/process.js.map +1 -0
- package/dist/tools/read.d.ts +9 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +95 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/registry.d.ts +19 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +188 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/safety.d.ts +9 -0
- package/dist/tools/safety.d.ts.map +1 -0
- package/dist/tools/safety.js +199 -0
- package/dist/tools/safety.js.map +1 -0
- package/dist/tools/schedule.d.ts +4 -0
- package/dist/tools/schedule.d.ts.map +1 -0
- package/dist/tools/schedule.js +191 -0
- package/dist/tools/schedule.js.map +1 -0
- package/dist/tools/search.d.ts +16 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +75 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/types.d.ts +54 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +10 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/stdio.d.ts +5 -0
- package/dist/ui/stdio.d.ts.map +1 -0
- package/dist/ui/stdio.js +65 -0
- package/dist/ui/stdio.js.map +1 -0
- package/dist/ui/tui.d.ts +93 -0
- package/dist/ui/tui.d.ts.map +1 -0
- package/dist/ui/tui.js +2246 -0
- package/dist/ui/tui.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import { CommandRunner, SessionPromptRunner } from "./runner.js";
|
|
4
|
+
function positiveInteger(value, name, maximum = 2_147_483_647) {
|
|
5
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
|
|
6
|
+
throw new Error(`${name} must be an integer between 1 and ${maximum}`);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
function wait(ms, signal) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
if (signal.aborted) {
|
|
13
|
+
resolve();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const timer = setTimeout(done, ms);
|
|
17
|
+
signal.addEventListener("abort", done, { once: true });
|
|
18
|
+
function done() {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
signal.removeEventListener("abort", done);
|
|
21
|
+
resolve();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export class SchedulerDaemon {
|
|
26
|
+
store;
|
|
27
|
+
owner;
|
|
28
|
+
bootId;
|
|
29
|
+
runner;
|
|
30
|
+
sessionPromptRunner;
|
|
31
|
+
pollIntervalMs;
|
|
32
|
+
leaseMs;
|
|
33
|
+
maxConcurrentCommands;
|
|
34
|
+
materializeBatchSize;
|
|
35
|
+
running = new Set();
|
|
36
|
+
commandSignal;
|
|
37
|
+
commandAbort;
|
|
38
|
+
firstWorkerError;
|
|
39
|
+
hasWorkerError = false;
|
|
40
|
+
constructor(store, options) {
|
|
41
|
+
this.store = store;
|
|
42
|
+
this.pollIntervalMs = positiveInteger(options.pollIntervalMs, "pollIntervalMs");
|
|
43
|
+
this.leaseMs = positiveInteger(options.leaseMs, "leaseMs");
|
|
44
|
+
this.maxConcurrentCommands = positiveInteger(options.maxConcurrentCommands, "maxConcurrentCommands", 1_000);
|
|
45
|
+
this.materializeBatchSize = positiveInteger(options.materializeBatchSize ?? 100, "materializeBatchSize", 10_000);
|
|
46
|
+
this.owner = options.owner ?? `${hostname()}:${process.pid}`;
|
|
47
|
+
this.bootId = options.bootId ?? randomUUID();
|
|
48
|
+
this.runner = new CommandRunner(store, options.sensitiveEnvKeys);
|
|
49
|
+
this.sessionPromptRunner = options.sessionPromptHandler
|
|
50
|
+
? new SessionPromptRunner(store, options.sessionPromptHandler, options.sensitiveEnvKeys)
|
|
51
|
+
: undefined;
|
|
52
|
+
}
|
|
53
|
+
async run(signal) {
|
|
54
|
+
if (this.running.size > 0 || this.commandAbort)
|
|
55
|
+
throw new Error("Scheduler daemon is already running");
|
|
56
|
+
this.firstWorkerError = undefined;
|
|
57
|
+
this.hasWorkerError = false;
|
|
58
|
+
const startedAt = Date.now();
|
|
59
|
+
if (!this.store.acquireLease(this.owner, this.bootId, startedAt, this.leaseMs)) {
|
|
60
|
+
throw new Error("Another scheduler daemon holds the lease");
|
|
61
|
+
}
|
|
62
|
+
const internalAbort = new AbortController();
|
|
63
|
+
this.commandAbort = internalAbort;
|
|
64
|
+
this.commandSignal = AbortSignal.any([signal, internalAbort.signal]);
|
|
65
|
+
const heartbeatInterval = Math.max(1, Math.floor(this.leaseMs / 3));
|
|
66
|
+
let nextPoll = startedAt;
|
|
67
|
+
let nextHeartbeat = startedAt + heartbeatInterval;
|
|
68
|
+
let loopError;
|
|
69
|
+
let hasLoopError = false;
|
|
70
|
+
let cleanupError;
|
|
71
|
+
let hasCleanupError = false;
|
|
72
|
+
try {
|
|
73
|
+
while (!this.commandSignal.aborted) {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
if (now >= nextHeartbeat) {
|
|
76
|
+
if (!this.store.renewLease(this.owner, this.bootId, now, this.leaseMs)) {
|
|
77
|
+
internalAbort.abort();
|
|
78
|
+
throw new Error("Scheduler daemon lost its lease");
|
|
79
|
+
}
|
|
80
|
+
nextHeartbeat = now + heartbeatInterval;
|
|
81
|
+
}
|
|
82
|
+
if (now >= nextPoll) {
|
|
83
|
+
await this.tick();
|
|
84
|
+
nextPoll = Date.now() + this.pollIntervalMs;
|
|
85
|
+
}
|
|
86
|
+
await wait(Math.max(1, Math.min(nextPoll, nextHeartbeat) - Date.now()), this.commandSignal);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
loopError = error;
|
|
91
|
+
hasLoopError = true;
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
internalAbort.abort();
|
|
95
|
+
try {
|
|
96
|
+
await this.settleWorkersWithLease();
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
cleanupError = error;
|
|
100
|
+
hasCleanupError = true;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
this.store.releaseLease(this.owner, this.bootId);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
cleanupError = error;
|
|
107
|
+
hasCleanupError = true;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
this.commandSignal = undefined;
|
|
111
|
+
this.commandAbort = undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (this.hasWorkerError)
|
|
115
|
+
throw this.firstWorkerError;
|
|
116
|
+
if (hasLoopError)
|
|
117
|
+
throw loopError;
|
|
118
|
+
if (hasCleanupError)
|
|
119
|
+
throw cleanupError;
|
|
120
|
+
}
|
|
121
|
+
async tick(now) {
|
|
122
|
+
if (this.hasWorkerError)
|
|
123
|
+
throw this.firstWorkerError;
|
|
124
|
+
const materialized = this.store.materialize(now ?? Date.now(), this.materializeBatchSize);
|
|
125
|
+
const available = this.maxConcurrentCommands - this.running.size;
|
|
126
|
+
if (available <= 0)
|
|
127
|
+
return { materialized: materialized.length, claimed: 0 };
|
|
128
|
+
const claimNow = now ?? Date.now();
|
|
129
|
+
const commandClaims = this.store.claimCommands(this.owner, this.bootId, claimNow, this.leaseMs, available);
|
|
130
|
+
for (const claim of commandClaims) {
|
|
131
|
+
let task;
|
|
132
|
+
task = this.runner.run(claim, this.commandSignal)
|
|
133
|
+
.then(() => undefined)
|
|
134
|
+
.catch((error) => this.captureWorkerError(error))
|
|
135
|
+
.finally(() => this.running.delete(task));
|
|
136
|
+
this.running.add(task);
|
|
137
|
+
}
|
|
138
|
+
const sessionAvailable = available - commandClaims.length;
|
|
139
|
+
const sessionClaims = this.sessionPromptRunner && sessionAvailable > 0
|
|
140
|
+
? this.store.claimSessionPrompts(this.owner, this.bootId, claimNow, this.leaseMs, sessionAvailable)
|
|
141
|
+
: [];
|
|
142
|
+
for (const claim of sessionClaims) {
|
|
143
|
+
let task;
|
|
144
|
+
task = this.sessionPromptRunner.run(claim, this.commandSignal)
|
|
145
|
+
.then(() => undefined)
|
|
146
|
+
.catch((error) => this.captureWorkerError(error))
|
|
147
|
+
.finally(() => this.running.delete(task));
|
|
148
|
+
this.running.add(task);
|
|
149
|
+
}
|
|
150
|
+
return { materialized: materialized.length, claimed: commandClaims.length + sessionClaims.length };
|
|
151
|
+
}
|
|
152
|
+
async waitForIdle() {
|
|
153
|
+
await this.settleWorkers();
|
|
154
|
+
if (this.hasWorkerError)
|
|
155
|
+
throw this.firstWorkerError;
|
|
156
|
+
}
|
|
157
|
+
captureWorkerError(error) {
|
|
158
|
+
if (!this.hasWorkerError) {
|
|
159
|
+
this.firstWorkerError = error;
|
|
160
|
+
this.hasWorkerError = true;
|
|
161
|
+
}
|
|
162
|
+
this.commandAbort?.abort();
|
|
163
|
+
}
|
|
164
|
+
async settleWorkers() {
|
|
165
|
+
await Promise.all([...this.running]);
|
|
166
|
+
}
|
|
167
|
+
async settleWorkersWithLease() {
|
|
168
|
+
const heartbeatInterval = Math.max(1, Math.floor(this.leaseMs / 3));
|
|
169
|
+
while (this.running.size > 0) {
|
|
170
|
+
let timer;
|
|
171
|
+
await Promise.race([
|
|
172
|
+
Promise.all([...this.running]),
|
|
173
|
+
new Promise((resolve) => {
|
|
174
|
+
timer = setTimeout(resolve, heartbeatInterval);
|
|
175
|
+
timer.unref();
|
|
176
|
+
}),
|
|
177
|
+
]);
|
|
178
|
+
if (timer)
|
|
179
|
+
clearTimeout(timer);
|
|
180
|
+
if (this.running.size > 0) {
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
if (!this.store.renewLease(this.owner, this.bootId, now, this.leaseMs)) {
|
|
183
|
+
throw new Error("Scheduler daemon lost its lease while stopping command workers");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/scheduler/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { SchedulerDaemon } from "./daemon.js";
|
|
2
|
+
export type { SchedulerDaemonOptions, SchedulerTickResult } from "./daemon.js";
|
|
3
|
+
export { CommandRunner, scheduledSessionReadOnly, SessionPromptRunner } from "./runner.js";
|
|
4
|
+
export { initialDue, nextCronDue, parseOnceSchedule, validateSchedule, validateTimezone } from "./schedule.js";
|
|
5
|
+
export { SchedulerStore } from "./store.js";
|
|
6
|
+
export { installService, renderUnit, serviceStatus, uninstallService, userUnitPath } from "./systemd.js";
|
|
7
|
+
export type { ClaimedCommand, ClaimedSessionPrompt, CommandCompletion, CreateCommandInput, CreateReminderInput, CreateSessionPromptInput, DaemonLease, InboxRecord, JobKind, Occurrence, OccurrenceState, ScheduleKind, SchedulerJob, SessionPromptCompletion, SessionPromptHandler, } from "./types.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzG,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,WAAW,EACX,WAAW,EACX,OAAO,EACP,UAAU,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { SchedulerDaemon } from "./daemon.js";
|
|
2
|
+
export { CommandRunner, scheduledSessionReadOnly, SessionPromptRunner } from "./runner.js";
|
|
3
|
+
export { initialDue, nextCronDue, parseOnceSchedule, validateSchedule, validateTimezone } from "./schedule.js";
|
|
4
|
+
export { SchedulerStore } from "./store.js";
|
|
5
|
+
export { installService, renderUnit, serviceStatus, uninstallService, userUnitPath } from "./systemd.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scheduler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ClaimedCommand, ClaimedSessionPrompt, Occurrence, SessionPromptHandler } from "./types.js";
|
|
2
|
+
import { SchedulerStore } from "./store.js";
|
|
3
|
+
import type { ApprovalMode } from "../types.js";
|
|
4
|
+
export declare function scheduledSessionReadOnly(approvalMode: ApprovalMode): boolean;
|
|
5
|
+
export declare class CommandRunner {
|
|
6
|
+
private readonly store;
|
|
7
|
+
private readonly sensitiveEnvKeys;
|
|
8
|
+
constructor(store: SchedulerStore, sensitiveEnvKeys?: readonly string[]);
|
|
9
|
+
run(claim: ClaimedCommand, signal?: AbortSignal): Promise<Occurrence>;
|
|
10
|
+
}
|
|
11
|
+
export declare class SessionPromptRunner {
|
|
12
|
+
private readonly store;
|
|
13
|
+
private readonly handler;
|
|
14
|
+
private readonly sensitiveEnvKeys;
|
|
15
|
+
constructor(store: SchedulerStore, handler: SessionPromptHandler, sensitiveEnvKeys?: readonly string[]);
|
|
16
|
+
run(claim: ClaimedSessionPrompt, signal?: AbortSignal): Promise<Occurrence>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/scheduler/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EAEpB,UAAU,EAEV,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAE5E;AAkBD,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADhB,KAAK,EAAE,cAAc,EACrB,gBAAgB,GAAE,SAAS,MAAM,EAAO;IAGrD,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CA6E5E;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAFhB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,GAAE,SAAS,MAAM,EAAO;IAGrD,GAAG,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAqElF"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { runProcess } from "../tools/process.js";
|
|
2
|
+
import { shellEnvironment } from "../tools/safety.js";
|
|
3
|
+
import { sessionPromptReservation } from "./types.js";
|
|
4
|
+
import { redactSensitiveText } from "../security.js";
|
|
5
|
+
export function scheduledSessionReadOnly(approvalMode) {
|
|
6
|
+
return approvalMode !== "unrestricted";
|
|
7
|
+
}
|
|
8
|
+
function failedCompletion(error) {
|
|
9
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10
|
+
return {
|
|
11
|
+
state: "failed",
|
|
12
|
+
exitCode: null,
|
|
13
|
+
signal: null,
|
|
14
|
+
reason: `Failed to start command: ${message}`,
|
|
15
|
+
stdout: Buffer.alloc(0),
|
|
16
|
+
stderr: Buffer.alloc(0),
|
|
17
|
+
stdoutBytes: 0,
|
|
18
|
+
stderrBytes: 0,
|
|
19
|
+
stdoutTruncated: false,
|
|
20
|
+
stderrTruncated: false,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export class CommandRunner {
|
|
24
|
+
store;
|
|
25
|
+
sensitiveEnvKeys;
|
|
26
|
+
constructor(store, sensitiveEnvKeys = []) {
|
|
27
|
+
this.store = store;
|
|
28
|
+
this.sensitiveEnvKeys = sensitiveEnvKeys;
|
|
29
|
+
}
|
|
30
|
+
async run(claim, signal) {
|
|
31
|
+
const { job, occurrence } = claim;
|
|
32
|
+
const token = occurrence.claimToken;
|
|
33
|
+
const started = this.store.startClaimedCommand(occurrence.id, token, occurrence.claimOwner, occurrence.claimBootId);
|
|
34
|
+
if (!started)
|
|
35
|
+
throw new Error("Scheduler command lost its lease or claim before start");
|
|
36
|
+
if (started.state !== "running")
|
|
37
|
+
return started;
|
|
38
|
+
let completion;
|
|
39
|
+
if (signal?.aborted) {
|
|
40
|
+
completion = {
|
|
41
|
+
state: "cancelled",
|
|
42
|
+
exitCode: null,
|
|
43
|
+
signal: null,
|
|
44
|
+
reason: "Scheduler daemon stopped before the command started",
|
|
45
|
+
stdout: Buffer.alloc(0),
|
|
46
|
+
stderr: Buffer.alloc(0),
|
|
47
|
+
stdoutBytes: 0,
|
|
48
|
+
stderrBytes: 0,
|
|
49
|
+
stdoutTruncated: false,
|
|
50
|
+
stderrTruncated: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
try {
|
|
55
|
+
const result = await runProcess("/bin/bash", ["--noprofile", "--norc", "-c", job.command], {
|
|
56
|
+
cwd: job.cwd,
|
|
57
|
+
env: shellEnvironment({ ...process.env, ...job.env }, this.sensitiveEnvKeys),
|
|
58
|
+
timeoutMs: job.timeoutMs ?? 1,
|
|
59
|
+
captureBytes: job.outputBytes ?? 0,
|
|
60
|
+
...(signal ? { signal } : {}),
|
|
61
|
+
detached: true,
|
|
62
|
+
});
|
|
63
|
+
const state = signal?.aborted
|
|
64
|
+
? "unknown"
|
|
65
|
+
: result.timedOut
|
|
66
|
+
? "timed_out"
|
|
67
|
+
: result.exitCode === 0 && result.signal === null
|
|
68
|
+
? "succeeded"
|
|
69
|
+
: "failed";
|
|
70
|
+
let reason = null;
|
|
71
|
+
if (state === "unknown")
|
|
72
|
+
reason = "Scheduler daemon stopped while the command was running; its outcome is unknown";
|
|
73
|
+
else if (state === "timed_out")
|
|
74
|
+
reason = `Command exceeded its ${job.timeoutMs ?? 0}ms timeout`;
|
|
75
|
+
else if (state === "failed" && result.signal)
|
|
76
|
+
reason = `Command terminated by ${result.signal}`;
|
|
77
|
+
else if (state === "failed")
|
|
78
|
+
reason = `Command exited with code ${result.exitCode ?? "unknown"}`;
|
|
79
|
+
const secrets = this.sensitiveEnvKeys
|
|
80
|
+
.map((key) => process.env[key])
|
|
81
|
+
.filter((value) => typeof value === "string" && value.length >= 8);
|
|
82
|
+
completion = {
|
|
83
|
+
state,
|
|
84
|
+
exitCode: result.exitCode,
|
|
85
|
+
signal: result.signal,
|
|
86
|
+
reason,
|
|
87
|
+
stdout: Buffer.from(redactSensitiveText(result.stdout.toString("utf8"), secrets)),
|
|
88
|
+
stderr: Buffer.from(redactSensitiveText(result.stderr.toString("utf8"), secrets)),
|
|
89
|
+
stdoutBytes: result.stdoutBytes,
|
|
90
|
+
stderrBytes: result.stderrBytes,
|
|
91
|
+
stdoutTruncated: result.stdoutTruncated,
|
|
92
|
+
stderrTruncated: result.stderrTruncated,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
completion = failedCompletion(error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const finished = this.store.finishCommand(occurrence.id, token, occurrence.claimOwner, occurrence.claimBootId, completion);
|
|
100
|
+
if (!finished)
|
|
101
|
+
throw new Error("Scheduler command result could not be persisted after lease or claim loss");
|
|
102
|
+
return finished;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export class SessionPromptRunner {
|
|
106
|
+
store;
|
|
107
|
+
handler;
|
|
108
|
+
sensitiveEnvKeys;
|
|
109
|
+
constructor(store, handler, sensitiveEnvKeys = []) {
|
|
110
|
+
this.store = store;
|
|
111
|
+
this.handler = handler;
|
|
112
|
+
this.sensitiveEnvKeys = sensitiveEnvKeys;
|
|
113
|
+
}
|
|
114
|
+
async run(claim, signal) {
|
|
115
|
+
const { job, occurrence } = claim;
|
|
116
|
+
const token = occurrence.claimToken;
|
|
117
|
+
const started = this.store.startClaimedSessionPrompt(occurrence.id, token, occurrence.claimOwner, occurrence.claimBootId);
|
|
118
|
+
if (!started)
|
|
119
|
+
throw new Error("Scheduler session prompt lost its lease or claim before start");
|
|
120
|
+
if (started.state !== "running")
|
|
121
|
+
return started;
|
|
122
|
+
const reservation = sessionPromptReservation(occurrence.claimOwner, occurrence.claimBootId, occurrence.claimToken);
|
|
123
|
+
const handlerSignal = signal ?? new AbortController().signal;
|
|
124
|
+
let completion;
|
|
125
|
+
try {
|
|
126
|
+
if (handlerSignal.aborted) {
|
|
127
|
+
completion = {
|
|
128
|
+
state: "cancelled",
|
|
129
|
+
reason: "Scheduler daemon stopped before the session prompt started",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
try {
|
|
134
|
+
const secrets = this.sensitiveEnvKeys
|
|
135
|
+
.map((key) => process.env[key])
|
|
136
|
+
.filter((value) => typeof value === "string" && value.length >= 8);
|
|
137
|
+
const output = redactSensitiveText(await this.handler(job, handlerSignal, reservation), secrets);
|
|
138
|
+
completion = handlerSignal.aborted
|
|
139
|
+
? {
|
|
140
|
+
state: "unknown",
|
|
141
|
+
reason: "Scheduler daemon stopped while the session prompt was running; its outcome is unknown",
|
|
142
|
+
}
|
|
143
|
+
: { state: "succeeded", output, reason: null };
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
const secrets = this.sensitiveEnvKeys
|
|
147
|
+
.map((key) => process.env[key])
|
|
148
|
+
.filter((value) => typeof value === "string" && value.length >= 8);
|
|
149
|
+
const message = redactSensitiveText(error instanceof Error ? error.message : String(error), secrets);
|
|
150
|
+
completion = handlerSignal.aborted
|
|
151
|
+
? {
|
|
152
|
+
state: "unknown",
|
|
153
|
+
error: message,
|
|
154
|
+
reason: "Scheduler daemon stopped while the session prompt was running; its outcome is unknown",
|
|
155
|
+
}
|
|
156
|
+
: {
|
|
157
|
+
state: "failed",
|
|
158
|
+
error: message,
|
|
159
|
+
reason: `Session prompt failed: ${message}`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
this.store.releaseSessionPromptReservation(job.sessionId, reservation.owner, reservation.token);
|
|
166
|
+
}
|
|
167
|
+
const finished = this.store.finishSessionPrompt(occurrence.id, token, occurrence.claimOwner, occurrence.claimBootId, completion);
|
|
168
|
+
if (!finished)
|
|
169
|
+
throw new Error("Scheduler session prompt result could not be persisted after lease or claim loss");
|
|
170
|
+
return finished;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/scheduler/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function rejectNul(value: string, field: string): void;
|
|
2
|
+
export declare function validateTimezone(timezone: string): void;
|
|
3
|
+
export declare function parseOnceSchedule(schedule: string): number;
|
|
4
|
+
export declare function validateSchedule(kind: "once" | "cron", schedule: string, timezone: string): void;
|
|
5
|
+
export declare function initialDue(kind: "once" | "cron", schedule: string, timezone: string, now: number): number;
|
|
6
|
+
export declare function nextCronDue(schedule: string, timezone: string, scheduledAt: number): number;
|
|
7
|
+
//# sourceMappingURL=schedule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/scheduler/schedule.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQvD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuC1D;AAgBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQhG;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIzG;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAM3F"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Cron } from "croner";
|
|
2
|
+
const ISO_WITH_OFFSET = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
3
|
+
export function rejectNul(value, field) {
|
|
4
|
+
if (value.includes("\0"))
|
|
5
|
+
throw new Error(`${field} must not contain NUL bytes`);
|
|
6
|
+
}
|
|
7
|
+
export function validateTimezone(timezone) {
|
|
8
|
+
rejectNul(timezone, "timezone");
|
|
9
|
+
if (timezone.length === 0)
|
|
10
|
+
throw new Error("timezone must not be empty");
|
|
11
|
+
try {
|
|
12
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format(0);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error(`Invalid timezone: ${timezone}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function parseOnceSchedule(schedule) {
|
|
19
|
+
rejectNul(schedule, "schedule");
|
|
20
|
+
const match = ISO_WITH_OFFSET.exec(schedule);
|
|
21
|
+
if (!match)
|
|
22
|
+
throw new Error("One-shot schedule must be an ISO datetime with Z or an explicit offset");
|
|
23
|
+
const year = Number(match[1]);
|
|
24
|
+
const month = Number(match[2]);
|
|
25
|
+
const day = Number(match[3]);
|
|
26
|
+
const hour = Number(match[4]);
|
|
27
|
+
const minute = Number(match[5]);
|
|
28
|
+
const second = Number(match[6] ?? "0");
|
|
29
|
+
const millisecond = Number((match[7] ?? "").padEnd(3, "0"));
|
|
30
|
+
const offset = match[8];
|
|
31
|
+
if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) {
|
|
32
|
+
throw new Error(`Invalid one-shot schedule: ${schedule}`);
|
|
33
|
+
}
|
|
34
|
+
if (offset !== "Z") {
|
|
35
|
+
const offsetHour = Number(offset?.slice(1, 3));
|
|
36
|
+
const offsetMinute = Number(offset?.slice(4, 6));
|
|
37
|
+
if (offsetHour > 23 || offsetMinute > 59)
|
|
38
|
+
throw new Error(`Invalid one-shot schedule: ${schedule}`);
|
|
39
|
+
}
|
|
40
|
+
const local = new Date(0);
|
|
41
|
+
local.setUTCFullYear(year, month - 1, day);
|
|
42
|
+
local.setUTCHours(hour, minute, second, millisecond);
|
|
43
|
+
if (local.getUTCFullYear() !== year
|
|
44
|
+
|| local.getUTCMonth() !== month - 1
|
|
45
|
+
|| local.getUTCDate() !== day
|
|
46
|
+
|| local.getUTCHours() !== hour
|
|
47
|
+
|| local.getUTCMinutes() !== minute
|
|
48
|
+
|| local.getUTCSeconds() !== second) {
|
|
49
|
+
throw new Error(`Invalid one-shot schedule: ${schedule}`);
|
|
50
|
+
}
|
|
51
|
+
const timestamp = Date.parse(schedule);
|
|
52
|
+
if (!Number.isFinite(timestamp))
|
|
53
|
+
throw new Error(`Invalid one-shot schedule: ${schedule}`);
|
|
54
|
+
return timestamp;
|
|
55
|
+
}
|
|
56
|
+
function cron(schedule, timezone) {
|
|
57
|
+
rejectNul(schedule, "schedule");
|
|
58
|
+
validateTimezone(timezone);
|
|
59
|
+
if (schedule.trim().split(/\s+/).length !== 5) {
|
|
60
|
+
throw new Error("Cron schedule must contain exactly five fields");
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
return new Cron(schedule, { timezone, mode: "5-part", paused: true });
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
67
|
+
throw new Error(`Invalid cron schedule: ${detail}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function validateSchedule(kind, schedule, timezone) {
|
|
71
|
+
if (kind !== "once" && kind !== "cron")
|
|
72
|
+
throw new Error(`Invalid schedule kind: ${String(kind)}`);
|
|
73
|
+
validateTimezone(timezone);
|
|
74
|
+
if (kind === "once") {
|
|
75
|
+
parseOnceSchedule(schedule);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
cron(schedule, timezone);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function initialDue(kind, schedule, timezone, now) {
|
|
82
|
+
validateSchedule(kind, schedule, timezone);
|
|
83
|
+
if (kind === "once")
|
|
84
|
+
return parseOnceSchedule(schedule);
|
|
85
|
+
return nextCronDue(schedule, timezone, now);
|
|
86
|
+
}
|
|
87
|
+
export function nextCronDue(schedule, timezone, scheduledAt) {
|
|
88
|
+
const next = cron(schedule, timezone).nextRun(new Date(scheduledAt));
|
|
89
|
+
if (!next || !Number.isFinite(next.getTime()) || next.getTime() <= scheduledAt) {
|
|
90
|
+
throw new Error("Cron schedule has no next occurrence");
|
|
91
|
+
}
|
|
92
|
+
return next.getTime();
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/scheduler/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,eAAe,GAAG,2FAA2F,CAAC;AAEpH,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,KAAa;IACpD,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACzE,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAEtG,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,IAAI,UAAU,GAAG,EAAE,IAAI,YAAY,GAAG,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IACtG,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3C,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IACrD,IACE,KAAK,CAAC,cAAc,EAAE,KAAK,IAAI;WAC5B,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,GAAG,CAAC;WACjC,KAAK,CAAC,UAAU,EAAE,KAAK,GAAG;WAC1B,KAAK,CAAC,WAAW,EAAE,KAAK,IAAI;WAC5B,KAAK,CAAC,aAAa,EAAE,KAAK,MAAM;WAChC,KAAK,CAAC,aAAa,EAAE,KAAK,MAAM,EACnC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAC3F,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,IAAI,CAAC,QAAgB,EAAE,QAAgB;IAC9C,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAqB,EAAE,QAAgB,EAAE,QAAgB;IACxF,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAqB,EAAE,QAAgB,EAAE,QAAgB,EAAE,GAAW;IAC/F,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxD,OAAO,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,QAAgB,EAAE,WAAmB;IACjF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC;QAC/E,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { GlassDatabase } from "../storage/database.js";
|
|
2
|
+
import type { ClaimedCommand, ClaimedSessionPrompt, CommandCompletion, CreateCommandInput, CreateReminderInput, CreateSessionPromptInput, DaemonLease, InboxRecord, Occurrence, SchedulerJob, SessionPromptCompletion } from "./types.js";
|
|
3
|
+
export declare class SchedulerStore {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: GlassDatabase);
|
|
6
|
+
createReminder(input: CreateReminderInput, now?: number): SchedulerJob;
|
|
7
|
+
createCommand(input: CreateCommandInput, now?: number): SchedulerJob;
|
|
8
|
+
createSessionPrompt(input: CreateSessionPromptInput, now?: number): SchedulerJob;
|
|
9
|
+
listJobs(includeDeleted?: boolean): SchedulerJob[];
|
|
10
|
+
listJobsForSession(sessionId: string): SchedulerJob[];
|
|
11
|
+
setSessionJobsEnabled(sessionId: string, enabled: boolean, now?: number): SchedulerJob[];
|
|
12
|
+
getJob(id: string): SchedulerJob | null;
|
|
13
|
+
pauseJob(id: string, now?: number): SchedulerJob;
|
|
14
|
+
resumeJob(id: string): SchedulerJob;
|
|
15
|
+
acknowledgeUnknown(id: string, now?: number): SchedulerJob;
|
|
16
|
+
deleteJob(id: string, now?: number): SchedulerJob;
|
|
17
|
+
listInbox(options?: {
|
|
18
|
+
unackedOnly?: boolean;
|
|
19
|
+
limit?: number;
|
|
20
|
+
}): InboxRecord[];
|
|
21
|
+
listInboxForJobs(jobIds: string[], options?: {
|
|
22
|
+
unackedOnly?: boolean;
|
|
23
|
+
limit?: number;
|
|
24
|
+
}): InboxRecord[];
|
|
25
|
+
getInbox(id: number): InboxRecord | null;
|
|
26
|
+
ackInbox(id: number, now?: number): boolean;
|
|
27
|
+
listRuns(jobId?: string, limit?: number): Occurrence[];
|
|
28
|
+
runNow(jobId: string, now?: number): Occurrence;
|
|
29
|
+
materialize(now?: number, batchSize?: number): Occurrence[];
|
|
30
|
+
tick(now?: number, batchSize?: number): Occurrence[];
|
|
31
|
+
acquireLease(owner: string, bootId: string, now?: number, leaseMs?: number): boolean;
|
|
32
|
+
renewLease(owner: string, bootId: string, now?: number, leaseMs?: number): boolean;
|
|
33
|
+
releaseLease(owner: string, bootId: string, now?: number): boolean;
|
|
34
|
+
getLease(): DaemonLease | null;
|
|
35
|
+
claimCommands(owner: string, bootId: string, now?: number, leaseMs?: number, limit?: number): ClaimedCommand[];
|
|
36
|
+
startClaimedCommand(occurrenceId: number, claimToken: string, owner: string, bootId: string, now?: number): Occurrence | null;
|
|
37
|
+
releaseSessionPromptReservation(sessionId: string, owner: string, token: string): boolean;
|
|
38
|
+
finishCommand(occurrenceId: number, claimToken: string, owner: string, bootId: string, completion: CommandCompletion, now?: number): Occurrence | null;
|
|
39
|
+
claimSessionPrompts(owner: string, bootId: string, now?: number, leaseMs?: number, limit?: number): ClaimedSessionPrompt[];
|
|
40
|
+
startClaimedSessionPrompt(occurrenceId: number, claimToken: string, owner: string, bootId: string, now?: number): Occurrence | null;
|
|
41
|
+
finishSessionPrompt(occurrenceId: number, claimToken: string, owner: string, bootId: string, completion: SessionPromptCompletion, now?: number): Occurrence | null;
|
|
42
|
+
private cancelUnstarted;
|
|
43
|
+
private recoverInterrupted;
|
|
44
|
+
private disableCompletedOneShot;
|
|
45
|
+
private blockUnknownJob;
|
|
46
|
+
private insertOccurrence;
|
|
47
|
+
private insertInbox;
|
|
48
|
+
private hasActiveOccurrence;
|
|
49
|
+
private requireJob;
|
|
50
|
+
private requireCommandJob;
|
|
51
|
+
private requireSessionPromptJob;
|
|
52
|
+
private isPersistentSession;
|
|
53
|
+
private requireOccurrence;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/scheduler/store.ts"],"names":[],"mappings":"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"}
|