@whittlelabs/sifter 0.36.0 → 0.37.1
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 +3 -1
- package/bin.js +231 -35
- package/bin.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,7 +122,9 @@ A running Sifter deals with Sift or `claude` going away on its own, and says wha
|
|
|
122
122
|
- **The pairing is revoked** (removed in Sift, or replaced by a newer pairing): it exits with code 3 and shows the two ways to pair again. A review it was running goes back to the queue for another Sifter.
|
|
123
123
|
- **It is decommissioned from Sift's settings page**: it finishes the review in hand and exits with code 0.
|
|
124
124
|
|
|
125
|
-
Ctrl-C stops it at any time, including while it waits.
|
|
125
|
+
Ctrl-C stops it at any time, including while it waits. A review in progress goes back to the queue for the next Sifter that checks in, and one that had already finished is submitted first; it quits within 20 seconds, or at once on a second Ctrl-C. Closing the terminal and `kill` do the same. If it stops without that chance (a crash, the machine asleep, the network gone), it stops checking in, and Jobs puts its reviews back in the queue once two minutes pass without a check-in (whittlelabs ADR 0129).
|
|
126
|
+
|
|
127
|
+
Each run is logged to `~/.sifter/sifter.log` as well as the terminal (`~/.sifter/<profile>/sifter.log` for a named profile), rotated to `sifter.log.1` past 5 MB. A hosted Sifter logs to stdout only.
|
|
126
128
|
|
|
127
129
|
## Check
|
|
128
130
|
|
package/bin.js
CHANGED
|
@@ -22204,6 +22204,7 @@ var require_client2 = __commonJS({
|
|
|
22204
22204
|
const body = {
|
|
22205
22205
|
jobPoolId,
|
|
22206
22206
|
title: options.title,
|
|
22207
|
+
progressChannel: options.progressChannel,
|
|
22207
22208
|
instructions: options.instructions,
|
|
22208
22209
|
maxAttempts: options.maxAttempts,
|
|
22209
22210
|
deadlineDuration: options.deadlineDuration,
|
|
@@ -22263,8 +22264,20 @@ var require_client2 = __commonJS({
|
|
|
22263
22264
|
* callers should be rare. Returns the dispatched-job context the
|
|
22264
22265
|
* subscriber needs to execute and submit.
|
|
22265
22266
|
*/
|
|
22266
|
-
async acceptDispatch(jobId) {
|
|
22267
|
-
return this.request("POST", `/api/jobs/${jobId}/attendance
|
|
22267
|
+
async acceptDispatch(jobId, options) {
|
|
22268
|
+
return this.request("POST", `/api/jobs/${jobId}/attendance`, options?.shiftId ? { shiftId: options.shiftId } : void 0);
|
|
22269
|
+
}
|
|
22270
|
+
// ── Shifts (whittlelabs ADR 0129) ────────────────────────────────
|
|
22271
|
+
/**
|
|
22272
|
+
* Start a shift for this process to claim on. A Jobs that predates shifts
|
|
22273
|
+
* answers 404; the subscribe loop then claims without one.
|
|
22274
|
+
*/
|
|
22275
|
+
async startShift() {
|
|
22276
|
+
return this.request("POST", "/api/my/shifts");
|
|
22277
|
+
}
|
|
22278
|
+
/** End a shift, releasing every claim still taken on it back to the queue. */
|
|
22279
|
+
async endShift(shiftId) {
|
|
22280
|
+
await this.request("DELETE", `/api/my/shifts/${shiftId}`);
|
|
22268
22281
|
}
|
|
22269
22282
|
async submit(jobId, options) {
|
|
22270
22283
|
return this.request("PUT", `/api/jobs/${jobId}/attendance`, options);
|
|
@@ -22286,12 +22299,15 @@ var require_client2 = __commonJS({
|
|
|
22286
22299
|
params.set("offset", String(options.offset));
|
|
22287
22300
|
if (options?.sort)
|
|
22288
22301
|
params.set("sort", options.sort);
|
|
22302
|
+
if (options?.shift)
|
|
22303
|
+
params.set("shift", options.shift);
|
|
22289
22304
|
const qs = params.toString();
|
|
22290
|
-
const { jobs, total, directives } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
|
|
22305
|
+
const { jobs, total, directives, shift } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
|
|
22291
22306
|
return {
|
|
22292
22307
|
data: jobs,
|
|
22293
22308
|
pagination: { total, limit: options?.limit ?? 25, offset: options?.offset ?? 0 },
|
|
22294
|
-
directives: directives ?? null
|
|
22309
|
+
directives: directives ?? null,
|
|
22310
|
+
shift: shift ?? null
|
|
22295
22311
|
};
|
|
22296
22312
|
}
|
|
22297
22313
|
// ── HTTP plumbing ────────────────────────────────────────────────
|
|
@@ -22382,6 +22398,17 @@ var require_subscribe = __commonJS({
|
|
|
22382
22398
|
const rest = seconds % 60;
|
|
22383
22399
|
return rest === 0 ? `${minutes}m` : `${minutes}m ${rest}s`;
|
|
22384
22400
|
}
|
|
22401
|
+
function statusOf(err) {
|
|
22402
|
+
const code = err?.statusCode;
|
|
22403
|
+
return typeof code === "number" ? code : void 0;
|
|
22404
|
+
}
|
|
22405
|
+
var HandBack = class {
|
|
22406
|
+
reason;
|
|
22407
|
+
constructor(reason) {
|
|
22408
|
+
this.reason = reason;
|
|
22409
|
+
}
|
|
22410
|
+
};
|
|
22411
|
+
var RELEASED = Symbol("released");
|
|
22385
22412
|
function newOutcome() {
|
|
22386
22413
|
let resolve;
|
|
22387
22414
|
let reject;
|
|
@@ -22422,7 +22449,10 @@ var require_subscribe = __commonJS({
|
|
|
22422
22449
|
let pollTimer = null;
|
|
22423
22450
|
let inflight = 0;
|
|
22424
22451
|
let resolvedPoolIds = [];
|
|
22425
|
-
const controllers = /* @__PURE__ */ new
|
|
22452
|
+
const controllers = /* @__PURE__ */ new Map();
|
|
22453
|
+
let shift = null;
|
|
22454
|
+
let shiftsSupported = true;
|
|
22455
|
+
let draining = null;
|
|
22426
22456
|
const holds = /* @__PURE__ */ new Set();
|
|
22427
22457
|
let fatalError = null;
|
|
22428
22458
|
let outcome = newOutcome();
|
|
@@ -22551,13 +22581,74 @@ var require_subscribe = __commonJS({
|
|
|
22551
22581
|
}
|
|
22552
22582
|
if (stopped || mySession !== session)
|
|
22553
22583
|
return;
|
|
22554
|
-
|
|
22584
|
+
try {
|
|
22585
|
+
const opened = await untilAvailable(() => startShift(), mySession);
|
|
22586
|
+
if (opened === STOPPED)
|
|
22587
|
+
return;
|
|
22588
|
+
} catch (err) {
|
|
22589
|
+
if (mySession === session) {
|
|
22590
|
+
stopped = true;
|
|
22591
|
+
outcome.reject(err);
|
|
22592
|
+
}
|
|
22593
|
+
throw err;
|
|
22594
|
+
}
|
|
22595
|
+
if (stopped || mySession !== session)
|
|
22596
|
+
return;
|
|
22597
|
+
logger.info("subscribe loop started", {
|
|
22598
|
+
pools: resolvedPoolIds,
|
|
22599
|
+
maxConcurrency,
|
|
22600
|
+
...shift ? { shiftId: shift.id } : {}
|
|
22601
|
+
});
|
|
22555
22602
|
schedulePoll(pollIntervalMs);
|
|
22556
22603
|
}
|
|
22604
|
+
async function startShift() {
|
|
22605
|
+
if (!shiftsSupported)
|
|
22606
|
+
return;
|
|
22607
|
+
try {
|
|
22608
|
+
shift = await opts.jobsClient.startShift();
|
|
22609
|
+
} catch (err) {
|
|
22610
|
+
if (isFatal(err) || isUnavailable(err) || isRateLimited(err))
|
|
22611
|
+
throw err;
|
|
22612
|
+
if (statusOf(err) === 404) {
|
|
22613
|
+
shiftsSupported = false;
|
|
22614
|
+
logger.debug?.("Jobs predates shifts; claims are held until each job's deadline");
|
|
22615
|
+
return;
|
|
22616
|
+
}
|
|
22617
|
+
logger.warn(`Could not start a shift (${describeOutage(err)}). Claims taken meanwhile are held until each job's deadline; the next poll tries again.`);
|
|
22618
|
+
}
|
|
22619
|
+
}
|
|
22620
|
+
async function endShift() {
|
|
22621
|
+
const ending = shift;
|
|
22622
|
+
shift = null;
|
|
22623
|
+
if (!ending)
|
|
22624
|
+
return;
|
|
22625
|
+
try {
|
|
22626
|
+
await opts.jobsClient.endShift(ending.id);
|
|
22627
|
+
} catch (err) {
|
|
22628
|
+
logger.debug?.("could not end the shift; it will lapse", {
|
|
22629
|
+
shiftId: ending.id,
|
|
22630
|
+
error: err instanceof Error ? err.message : String(err)
|
|
22631
|
+
});
|
|
22632
|
+
}
|
|
22633
|
+
}
|
|
22634
|
+
async function shiftReleased(ended) {
|
|
22635
|
+
const mine = [...controllers].filter(([, held]) => held.shiftId === ended.id);
|
|
22636
|
+
logger.warn(`Jobs ended this Sifter's shift (${ended.endReason ?? "ended"}) and released its claims` + (mine.length > 0 ? `; ${mine.length} job(s) running on it were handed back to the queue and stop here` : "") + ". Starting a new shift.", { shiftId: ended.id });
|
|
22637
|
+
for (const [controller] of mine)
|
|
22638
|
+
controller.abort(RELEASED);
|
|
22639
|
+
shift = null;
|
|
22640
|
+
try {
|
|
22641
|
+
await startShift();
|
|
22642
|
+
} catch (err) {
|
|
22643
|
+
if (isFatal(err))
|
|
22644
|
+
throw err;
|
|
22645
|
+
}
|
|
22646
|
+
}
|
|
22557
22647
|
async function finish() {
|
|
22558
22648
|
while (inflight > 0) {
|
|
22559
22649
|
await sleep(100);
|
|
22560
22650
|
}
|
|
22651
|
+
await endShift();
|
|
22561
22652
|
logger.info("subscribe loop stopped");
|
|
22562
22653
|
if (fatalError !== null)
|
|
22563
22654
|
outcome.reject(fatalError.error);
|
|
@@ -22572,45 +22663,61 @@ var require_subscribe = __commonJS({
|
|
|
22572
22663
|
}
|
|
22573
22664
|
wakeStartup?.();
|
|
22574
22665
|
}
|
|
22575
|
-
async function stop() {
|
|
22666
|
+
async function stop(options = {}) {
|
|
22667
|
+
if (options.handBack !== void 0) {
|
|
22668
|
+
for (const [controller] of controllers)
|
|
22669
|
+
controller.abort(new HandBack(options.handBack));
|
|
22670
|
+
}
|
|
22671
|
+
if (draining)
|
|
22672
|
+
return draining;
|
|
22576
22673
|
if (stopped)
|
|
22577
22674
|
return;
|
|
22578
22675
|
halt();
|
|
22579
|
-
|
|
22676
|
+
draining = finish().finally(() => {
|
|
22677
|
+
draining = null;
|
|
22678
|
+
});
|
|
22679
|
+
return draining;
|
|
22580
22680
|
}
|
|
22581
22681
|
async function fail(err) {
|
|
22582
22682
|
if (fatalError === null)
|
|
22583
22683
|
fatalError = { error: err };
|
|
22584
|
-
for (const controller of controllers)
|
|
22684
|
+
for (const [controller] of controllers)
|
|
22585
22685
|
controller.abort(err);
|
|
22686
|
+
if (draining)
|
|
22687
|
+
return draining;
|
|
22586
22688
|
if (stopped)
|
|
22587
22689
|
return;
|
|
22588
22690
|
halt();
|
|
22589
|
-
|
|
22691
|
+
draining = finish().finally(() => {
|
|
22692
|
+
draining = null;
|
|
22693
|
+
});
|
|
22694
|
+
return draining;
|
|
22590
22695
|
}
|
|
22591
22696
|
function schedulePoll(delayMs) {
|
|
22592
22697
|
const mySession = session;
|
|
22698
|
+
const checkInMs = shift ? shift.lapseAfterSeconds * 1e3 / 4 : Infinity;
|
|
22593
22699
|
pollTimer = setTimeout(() => {
|
|
22594
22700
|
void poll(mySession);
|
|
22595
|
-
}, delayMs);
|
|
22701
|
+
}, Math.min(delayMs, checkInMs));
|
|
22596
22702
|
}
|
|
22597
22703
|
async function poll(mySession) {
|
|
22598
22704
|
if (stopped || mySession !== session)
|
|
22599
22705
|
return;
|
|
22600
22706
|
let nextPollMs = pollIntervalMs;
|
|
22601
|
-
if (inflight >= maxConcurrency) {
|
|
22602
|
-
schedulePoll(pollIntervalMs);
|
|
22603
|
-
return;
|
|
22604
|
-
}
|
|
22605
22707
|
try {
|
|
22606
22708
|
const paused = holds.size > 0;
|
|
22607
22709
|
const free = maxConcurrency - inflight;
|
|
22608
|
-
|
|
22710
|
+
if (shift === null && shiftsSupported)
|
|
22711
|
+
await startShift();
|
|
22712
|
+
const { data, directives, shift: standing } = await opts.jobsClient.listDispatchableJobs({
|
|
22609
22713
|
pools: resolvedPoolIds,
|
|
22610
|
-
limit: paused ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
|
|
22611
|
-
sort: "priority:desc,queued_at:asc"
|
|
22714
|
+
limit: paused || free <= 0 ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
|
|
22715
|
+
sort: "priority:desc,queued_at:asc",
|
|
22716
|
+
...shift ? { shift: shift.id } : {}
|
|
22612
22717
|
});
|
|
22613
22718
|
noteReachable();
|
|
22719
|
+
if (shift && standing?.id === shift.id && standing.endedAt !== null)
|
|
22720
|
+
await shiftReleased(standing);
|
|
22614
22721
|
if (directives && Object.keys(directives).length > 0 && opts.onDirectives) {
|
|
22615
22722
|
try {
|
|
22616
22723
|
await opts.onDirectives(directives);
|
|
@@ -22658,8 +22765,9 @@ var require_subscribe = __commonJS({
|
|
|
22658
22765
|
}
|
|
22659
22766
|
async function runOnce(available) {
|
|
22660
22767
|
let dispatch;
|
|
22768
|
+
const shiftId = shift?.id ?? null;
|
|
22661
22769
|
try {
|
|
22662
|
-
dispatch = await opts.jobsClient.acceptDispatch(available.id);
|
|
22770
|
+
dispatch = await opts.jobsClient.acceptDispatch(available.id, shiftId ? { shiftId } : void 0);
|
|
22663
22771
|
} catch (err) {
|
|
22664
22772
|
if (isFatal(err)) {
|
|
22665
22773
|
void fail(err);
|
|
@@ -22676,8 +22784,13 @@ var require_subscribe = __commonJS({
|
|
|
22676
22784
|
});
|
|
22677
22785
|
return;
|
|
22678
22786
|
}
|
|
22787
|
+
logger.info(`Claimed job ${dispatch.job.id} (attempt ${dispatch.job.attempt})`, {
|
|
22788
|
+
jobId: dispatch.job.id,
|
|
22789
|
+
pool: dispatch.pool.name,
|
|
22790
|
+
...shiftId ? { shiftId } : {}
|
|
22791
|
+
});
|
|
22679
22792
|
const controller = new AbortController();
|
|
22680
|
-
controllers.
|
|
22793
|
+
controllers.set(controller, { shiftId });
|
|
22681
22794
|
if (fatalError !== null)
|
|
22682
22795
|
controller.abort(fatalError.error);
|
|
22683
22796
|
let quietTimer = null;
|
|
@@ -22717,9 +22830,20 @@ var require_subscribe = __commonJS({
|
|
|
22717
22830
|
controllers.delete(controller);
|
|
22718
22831
|
}
|
|
22719
22832
|
if (fatalError !== null) {
|
|
22720
|
-
logger.
|
|
22833
|
+
logger.error(shiftId ? `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim goes back to the queue when this Sifter's shift lapses.` : `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim is held until the job's deadline.`, { jobId: dispatch.job.id, status: result.status });
|
|
22721
22834
|
return;
|
|
22722
22835
|
}
|
|
22836
|
+
if (controller.signal.reason === RELEASED) {
|
|
22837
|
+
logger.warn(`Job ${dispatch.job.id} was handed back to the queue while it ran; its result is not submitted.`, {
|
|
22838
|
+
jobId: dispatch.job.id
|
|
22839
|
+
});
|
|
22840
|
+
return;
|
|
22841
|
+
}
|
|
22842
|
+
const handBack = controller.signal.reason instanceof HandBack ? controller.signal.reason.reason : null;
|
|
22843
|
+
if (handBack !== null && result.status !== "completed") {
|
|
22844
|
+
result = { status: "abandoned", reason: handBack };
|
|
22845
|
+
logger.warn(`Handing job ${dispatch.job.id} back to the queue: ${handBack}`, { jobId: dispatch.job.id });
|
|
22846
|
+
}
|
|
22723
22847
|
try {
|
|
22724
22848
|
for (const output of result.outputs ?? []) {
|
|
22725
22849
|
await opts.jobsClient.createOutput(dispatch.job.id, {
|
|
@@ -22741,7 +22865,12 @@ var require_subscribe = __commonJS({
|
|
|
22741
22865
|
jobId: dispatch.job.id,
|
|
22742
22866
|
error: err instanceof Error ? err.message : String(err)
|
|
22743
22867
|
});
|
|
22868
|
+
return;
|
|
22744
22869
|
}
|
|
22870
|
+
logger.info(`Submitted job ${dispatch.job.id} as ${result.status}`, {
|
|
22871
|
+
jobId: dispatch.job.id,
|
|
22872
|
+
...result.reason !== void 0 ? { reason: result.reason } : {}
|
|
22873
|
+
});
|
|
22745
22874
|
}
|
|
22746
22875
|
return {
|
|
22747
22876
|
start,
|
|
@@ -26382,9 +26511,12 @@ var require_logger = __commonJS({
|
|
|
26382
26511
|
"../../packages/shuttle/dist/logging/logger.js"(exports2) {
|
|
26383
26512
|
"use strict";
|
|
26384
26513
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
26514
|
+
exports2.LOG_FILE_MAX_BYTES = void 0;
|
|
26385
26515
|
exports2.createLogger = createLogger;
|
|
26386
26516
|
exports2.setGlobalLogger = setGlobalLogger;
|
|
26387
26517
|
exports2.getLogger = getLogger;
|
|
26518
|
+
var node_fs_1 = require("node:fs");
|
|
26519
|
+
var node_path_1 = require("node:path");
|
|
26388
26520
|
var LEVEL_ORDER = {
|
|
26389
26521
|
debug: 0,
|
|
26390
26522
|
info: 1,
|
|
@@ -26412,13 +26544,42 @@ var require_logger = __commonJS({
|
|
|
26412
26544
|
};
|
|
26413
26545
|
return JSON.stringify(entry);
|
|
26414
26546
|
}
|
|
26547
|
+
exports2.LOG_FILE_MAX_BYTES = 5 * 1024 * 1024;
|
|
26548
|
+
function fileSink(path) {
|
|
26549
|
+
let size = 0;
|
|
26550
|
+
let broken = false;
|
|
26551
|
+
try {
|
|
26552
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
26553
|
+
size = (0, node_fs_1.statSync)(path).size;
|
|
26554
|
+
} catch {
|
|
26555
|
+
size = 0;
|
|
26556
|
+
}
|
|
26557
|
+
return (line) => {
|
|
26558
|
+
if (broken)
|
|
26559
|
+
return;
|
|
26560
|
+
try {
|
|
26561
|
+
if (size > exports2.LOG_FILE_MAX_BYTES) {
|
|
26562
|
+
(0, node_fs_1.renameSync)(path, `${path}.1`);
|
|
26563
|
+
size = 0;
|
|
26564
|
+
}
|
|
26565
|
+
const entry = `${line}
|
|
26566
|
+
`;
|
|
26567
|
+
(0, node_fs_1.appendFileSync)(path, entry, { mode: 384 });
|
|
26568
|
+
size += Buffer.byteLength(entry);
|
|
26569
|
+
} catch {
|
|
26570
|
+
broken = true;
|
|
26571
|
+
}
|
|
26572
|
+
};
|
|
26573
|
+
}
|
|
26415
26574
|
function createLogger(options) {
|
|
26416
26575
|
const threshold = LEVEL_ORDER[options.level];
|
|
26417
26576
|
const format = options.format === "json" ? formatJson : formatPretty;
|
|
26577
|
+
const toFile = options.file ? fileSink(options.file) : null;
|
|
26418
26578
|
function emit(level, message, context) {
|
|
26419
26579
|
if (LEVEL_ORDER[level] < threshold)
|
|
26420
26580
|
return;
|
|
26421
26581
|
const line = format(level, message, context);
|
|
26582
|
+
toFile?.(line);
|
|
26422
26583
|
if (level === "error") {
|
|
26423
26584
|
console.error(line);
|
|
26424
26585
|
} else if (level === "warn") {
|
|
@@ -27714,6 +27875,7 @@ var require_shuttle = __commonJS({
|
|
|
27714
27875
|
exports2.mergeExecutorDefaults = mergeExecutorDefaults;
|
|
27715
27876
|
exports2.resolveAuth = resolveAuth;
|
|
27716
27877
|
var os_1 = require("os");
|
|
27878
|
+
var path_1 = require("path");
|
|
27717
27879
|
var jobs_1 = require_dist4();
|
|
27718
27880
|
var self_update_1 = require_self_update();
|
|
27719
27881
|
var decommission_1 = require_decommission();
|
|
@@ -27854,13 +28016,18 @@ var require_shuttle = __commonJS({
|
|
|
27854
28016
|
return this.spendTracker;
|
|
27855
28017
|
}
|
|
27856
28018
|
async start() {
|
|
28019
|
+
const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
|
|
28020
|
+
const logFile = this.config.credentials === void 0 ? (0, path_1.join)(paths.configDir, `${this.brand.product.id}.log`) : void 0;
|
|
27857
28021
|
(0, logger_1.setGlobalLogger)((0, logger_1.createLogger)({
|
|
27858
28022
|
level: this.config.logging.level,
|
|
27859
|
-
format: this.config.logging.format
|
|
28023
|
+
format: this.config.logging.format,
|
|
28024
|
+
...logFile ? { file: logFile } : {}
|
|
27860
28025
|
}));
|
|
27861
28026
|
const logger = (0, logger_1.getLogger)();
|
|
27862
|
-
logger.info(`${this.brand.product.title} starting
|
|
27863
|
-
|
|
28027
|
+
logger.info(`${this.brand.product.title} starting...`, {
|
|
28028
|
+
version: this.brand.product.version,
|
|
28029
|
+
...logFile ? { log: logFile } : {}
|
|
28030
|
+
});
|
|
27864
28031
|
this.broker = (0, credentials_1.createCredentialBroker)(this.config.credentials, this.credentialDeps);
|
|
27865
28032
|
const subscribeLogger = {
|
|
27866
28033
|
debug: (msg, meta) => logger.debug(msg, meta),
|
|
@@ -27998,7 +28165,7 @@ var require_shuttle = __commonJS({
|
|
|
27998
28165
|
const runOn = async (executor) => {
|
|
27999
28166
|
await observerChain.notify({ kind: "executor.dispatch.started", jobId, attendanceId, executor: executor.capability.id, strategy }, onObserverError);
|
|
28000
28167
|
const dispatchStartedAt = Date.now();
|
|
28001
|
-
const
|
|
28168
|
+
const ran = await (0, run_dispatch_1.runDispatch)(dispatch, executor, {
|
|
28002
28169
|
credentials: this.broker,
|
|
28003
28170
|
signal,
|
|
28004
28171
|
reportProgress: (channel, frames) => {
|
|
@@ -28006,6 +28173,8 @@ var require_shuttle = __commonJS({
|
|
|
28006
28173
|
});
|
|
28007
28174
|
}
|
|
28008
28175
|
});
|
|
28176
|
+
subscribeLogger.info(`Job ${jobId} ran on ${executor.capability.id} for ${formatDuration(Date.now() - dispatchStartedAt)}: ` + (ran.kind === "completed" ? "finished" : ran.kind === "gate-failed" ? "its output failed a gate" : "the executor failed"), { jobId, ...signal.aborted ? { aborted: true } : {} });
|
|
28177
|
+
const outcome = ran;
|
|
28009
28178
|
if (outcome.kind === "executor-failed") {
|
|
28010
28179
|
const err = outcome.error;
|
|
28011
28180
|
const reason = `executor failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -28165,14 +28334,21 @@ var require_shuttle = __commonJS({
|
|
|
28165
28334
|
logger.info(`${this.brand.product.title} stopping...`);
|
|
28166
28335
|
this.stopping.abort();
|
|
28167
28336
|
if (this.subscription) {
|
|
28168
|
-
await this.subscription.stop(
|
|
28337
|
+
await this.subscription.stop({
|
|
28338
|
+
handBack: `The ${this.brand.product.title} was stopped before it finished this job`
|
|
28339
|
+
});
|
|
28169
28340
|
}
|
|
28170
28341
|
logger.info(`${this.brand.product.title} stopped`);
|
|
28171
28342
|
this.lifetime.resolve();
|
|
28172
28343
|
}
|
|
28173
28344
|
/** Forward the current subscribe session's fatal, if one ends it, to the run's outcome. */
|
|
28174
28345
|
watchSession() {
|
|
28175
|
-
this.subscription?.done().catch((err) =>
|
|
28346
|
+
this.subscription?.done().catch((err) => {
|
|
28347
|
+
(0, logger_1.getLogger)().error(`${this.brand.product.title} stopped on a fatal error`, {
|
|
28348
|
+
error: err instanceof Error ? `${err.name}: ${err.message}` : String(err)
|
|
28349
|
+
});
|
|
28350
|
+
this.lifetime.reject(err);
|
|
28351
|
+
});
|
|
28176
28352
|
}
|
|
28177
28353
|
/**
|
|
28178
28354
|
* Note that an executor cannot run jobs on this machine, and keep checking
|
|
@@ -28336,29 +28512,49 @@ var require_shutdown = __commonJS({
|
|
|
28336
28512
|
"../../packages/shuttle/dist/lifecycle/shutdown.js"(exports2) {
|
|
28337
28513
|
"use strict";
|
|
28338
28514
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
28515
|
+
exports2.SHUTDOWN_DEADLINE_MS = void 0;
|
|
28339
28516
|
exports2.setupShutdownHandler = setupShutdownHandler;
|
|
28517
|
+
exports2.createShutdownHandler = createShutdownHandler;
|
|
28340
28518
|
var logger_1 = require_logger();
|
|
28519
|
+
exports2.SHUTDOWN_DEADLINE_MS = 2e4;
|
|
28341
28520
|
function setupShutdownHandler(onShutdown) {
|
|
28521
|
+
const handler = createShutdownHandler(onShutdown);
|
|
28522
|
+
process.on("SIGINT", () => handler("SIGINT"));
|
|
28523
|
+
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
28524
|
+
process.on("SIGHUP", () => handler("SIGHUP"));
|
|
28525
|
+
}
|
|
28526
|
+
function createShutdownHandler(onShutdown, options = {}) {
|
|
28527
|
+
const deadlineMs = options.deadlineMs ?? exports2.SHUTDOWN_DEADLINE_MS;
|
|
28528
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
28342
28529
|
let shuttingDown = false;
|
|
28343
28530
|
const handler = (signal) => {
|
|
28344
28531
|
const logger = (0, logger_1.getLogger)();
|
|
28345
28532
|
if (shuttingDown) {
|
|
28346
|
-
logger.warn("Forced shutdown", {
|
|
28347
|
-
|
|
28533
|
+
logger.warn("Forced shutdown: exiting now. Jobs releases any claim still held once this shift lapses.", {
|
|
28534
|
+
signal
|
|
28535
|
+
});
|
|
28536
|
+
exit(1);
|
|
28537
|
+
return;
|
|
28348
28538
|
}
|
|
28349
28539
|
shuttingDown = true;
|
|
28350
|
-
logger.info("Shutting down...", { signal });
|
|
28540
|
+
logger.info("Shutting down: handing any job in progress back to the queue...", { signal });
|
|
28541
|
+
const deadline = setTimeout(() => {
|
|
28542
|
+
logger.warn(`Shutdown did not finish within ${Math.round(deadlineMs / 1e3)}s; exiting. Jobs releases any claim still held once this shift lapses.`);
|
|
28543
|
+
exit(1);
|
|
28544
|
+
}, deadlineMs);
|
|
28545
|
+
deadline.unref?.();
|
|
28351
28546
|
onShutdown().then(() => {
|
|
28352
|
-
|
|
28547
|
+
clearTimeout(deadline);
|
|
28548
|
+
exit(0);
|
|
28353
28549
|
}).catch((err) => {
|
|
28550
|
+
clearTimeout(deadline);
|
|
28354
28551
|
logger.error("Shutdown error", {
|
|
28355
28552
|
error: err instanceof Error ? err.message : String(err)
|
|
28356
28553
|
});
|
|
28357
|
-
|
|
28554
|
+
exit(1);
|
|
28358
28555
|
});
|
|
28359
28556
|
};
|
|
28360
|
-
|
|
28361
|
-
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
28557
|
+
return handler;
|
|
28362
28558
|
}
|
|
28363
28559
|
}
|
|
28364
28560
|
});
|
|
@@ -29192,7 +29388,7 @@ var import_path = require("path");
|
|
|
29192
29388
|
var import_promises = require("fs/promises");
|
|
29193
29389
|
var import_yaml = __toESM(require_dist3());
|
|
29194
29390
|
var import_shuttle = __toESM(require_dist5());
|
|
29195
|
-
var buildVersion = true ? "0.
|
|
29391
|
+
var buildVersion = true ? "0.37.1" : pkg.version;
|
|
29196
29392
|
var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
|
|
29197
29393
|
var sifterBrand = {
|
|
29198
29394
|
product: {
|