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