@lchase/kiln 0.9.0 → 0.11.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 +5 -5
- package/assets/serve.css +195 -472
- package/assets/serve.js +121 -6
- package/dist/attention.js +13 -4
- package/dist/board-view.js +688 -0
- package/dist/board.js +0 -97
- package/dist/cli.js +63 -6
- package/dist/config.js +25 -1
- package/dist/driver.js +117 -16
- package/dist/events.js +40 -1
- package/dist/failure.js +140 -0
- package/dist/fix-commit.js +73 -0
- package/dist/ledger.js +11 -3
- package/dist/project.js +20 -4
- package/dist/rails.js +164 -0
- package/dist/reconcile.js +29 -3
- package/dist/reliability.js +53 -25
- package/dist/report.js +19 -5
- package/dist/schemas.js +35 -5
- package/dist/serve-page.js +396 -316
- package/dist/serve.js +252 -13
- package/dist/settings.js +9 -1
- package/package.json +4 -3
- package/workflows/fix.yaml +1 -1
package/assets/serve.js
CHANGED
|
@@ -605,6 +605,8 @@
|
|
|
605
605
|
base: repo.base || "",
|
|
606
606
|
prTitle: repo.prTitle || "",
|
|
607
607
|
branch: repo.branch || "",
|
|
608
|
+
maxAttempts: repo.maxAttempts != null ? String(repo.maxAttempts) : "",
|
|
609
|
+
maxFixRounds: repo.maxFixRounds != null ? String(repo.maxFixRounds) : "",
|
|
608
610
|
};
|
|
609
611
|
}
|
|
610
612
|
return out;
|
|
@@ -625,7 +627,11 @@
|
|
|
625
627
|
var attempts = document.getElementById("hint-attempts");
|
|
626
628
|
var stall = document.getElementById("hint-stall");
|
|
627
629
|
var rounds = document.getElementById("hint-fix");
|
|
628
|
-
if (attempts) attempts.textContent = "Default " + (settingsDefaults.maxAttempts != null ? settingsDefaults.maxAttempts : 3) + ". Transient failures before the job
|
|
630
|
+
if (attempts) attempts.textContent = "Default " + (settingsDefaults.maxAttempts != null ? settingsDefaults.maxAttempts : 3) + ". Transient failures before the job needs a person.";
|
|
631
|
+
var fingerprint = document.getElementById("hint-fingerprint");
|
|
632
|
+
var cap = document.getElementById("hint-cap");
|
|
633
|
+
if (fingerprint) fingerprint.textContent = "Default " + (settingsDefaults.maxSameFingerprint != null ? settingsDefaults.maxSameFingerprint : 3) + ". Identical fingerprints before the job needs a person.";
|
|
634
|
+
if (cap) cap.textContent = "Empty means no daily cap. Recorded dollars, local day.";
|
|
629
635
|
if (stall) stall.textContent = "Default " + (settingsDefaults.stallTimeoutMs != null ? settingsDefaults.stallTimeoutMs : 900000) + " (15 minutes). 0 disables stall detection.";
|
|
630
636
|
if (rounds) rounds.textContent = "Default " + (settingsDefaults.maxFixRounds != null ? settingsDefaults.maxFixRounds : 2) + ". Fix rounds before the job needs a person.";
|
|
631
637
|
var attemptInput = document.getElementById("max-attempts");
|
|
@@ -634,6 +640,8 @@
|
|
|
634
640
|
if (attemptInput) attemptInput.placeholder = String(settingsDefaults.maxAttempts != null ? settingsDefaults.maxAttempts : 3);
|
|
635
641
|
if (stallInput) stallInput.placeholder = String(settingsDefaults.stallTimeoutMs != null ? settingsDefaults.stallTimeoutMs : 900000);
|
|
636
642
|
if (roundInput) roundInput.placeholder = String(settingsDefaults.maxFixRounds != null ? settingsDefaults.maxFixRounds : 2);
|
|
643
|
+
var fingerprintInput = document.getElementById("max-same-fingerprint");
|
|
644
|
+
if (fingerprintInput) fingerprintInput.placeholder = String(settingsDefaults.maxSameFingerprint != null ? settingsDefaults.maxSameFingerprint : 3);
|
|
637
645
|
}
|
|
638
646
|
|
|
639
647
|
function fillGlobal(data) {
|
|
@@ -644,6 +652,10 @@
|
|
|
644
652
|
if (attempts) attempts.value = data.maxAttempts != null ? String(data.maxAttempts) : "";
|
|
645
653
|
if (stall) stall.value = data.stallTimeoutMs != null ? String(data.stallTimeoutMs) : "";
|
|
646
654
|
if (rounds) rounds.value = data.maxFixRounds != null ? String(data.maxFixRounds) : "";
|
|
655
|
+
var fingerprint = document.getElementById("max-same-fingerprint");
|
|
656
|
+
var cap = document.getElementById("daily-dollar-cap");
|
|
657
|
+
if (fingerprint) fingerprint.value = data.maxSameFingerprint != null ? String(data.maxSameFingerprint) : "";
|
|
658
|
+
if (cap) cap.value = data.dailyDollarCap != null ? String(data.dailyDollarCap) : "";
|
|
647
659
|
setPressed("#sandbox-mode [data-sandbox]", "data-sandbox", data.sandbox && data.sandbox.mode === "docker" ? "docker" : "off");
|
|
648
660
|
var domains = document.getElementById("sandbox-domains");
|
|
649
661
|
if (domains) domains.value = data.sandbox && data.sandbox.allowDomains ? data.sandbox.allowDomains.join("\n") : "";
|
|
@@ -674,12 +686,16 @@
|
|
|
674
686
|
var baseInput = document.getElementById("repo-base");
|
|
675
687
|
var titleInput = document.getElementById("repo-pr-title");
|
|
676
688
|
var branchInput = document.getElementById("repo-branch");
|
|
689
|
+
var attemptsInput = document.getElementById("repo-max-attempts");
|
|
690
|
+
var roundsInput = document.getElementById("repo-max-fix-rounds");
|
|
677
691
|
if (nameInput) nameInput.value = name || "";
|
|
678
692
|
if (pathInput) pathInput.value = repo.path || "";
|
|
679
693
|
if (testInput) testInput.value = repo.test || "";
|
|
680
694
|
if (baseInput) baseInput.value = repo.base || "";
|
|
681
695
|
if (titleInput) titleInput.value = repo.prTitle || "";
|
|
682
696
|
if (branchInput) branchInput.value = repo.branch || "";
|
|
697
|
+
if (attemptsInput) attemptsInput.value = repo.maxAttempts != null && repo.maxAttempts !== "" ? String(repo.maxAttempts) : "";
|
|
698
|
+
if (roundsInput) roundsInput.value = repo.maxFixRounds != null && repo.maxFixRounds !== "" ? String(repo.maxFixRounds) : "";
|
|
683
699
|
setPressed("#repo-deliver [data-deliver]", "data-deliver", repo.deliver === "pr" ? "pr" : "local");
|
|
684
700
|
var browser = document.getElementById("repo-browser");
|
|
685
701
|
if (browser) { browser.hidden = true; browser.innerHTML = ""; }
|
|
@@ -698,6 +714,8 @@
|
|
|
698
714
|
base: value("repo-base"),
|
|
699
715
|
prTitle: value("repo-pr-title"),
|
|
700
716
|
branch: value("repo-branch"),
|
|
717
|
+
maxAttempts: value("repo-max-attempts"),
|
|
718
|
+
maxFixRounds: value("repo-max-fix-rounds"),
|
|
701
719
|
};
|
|
702
720
|
}
|
|
703
721
|
|
|
@@ -763,6 +781,13 @@
|
|
|
763
781
|
renderRepoList();
|
|
764
782
|
}
|
|
765
783
|
|
|
784
|
+
function optionalNumber(raw) {
|
|
785
|
+
var text = String(raw == null ? "" : raw).trim();
|
|
786
|
+
if (!text) return undefined;
|
|
787
|
+
if (!/^\d+(\.\d+)?$/.test(text)) return text;
|
|
788
|
+
return Number(text);
|
|
789
|
+
}
|
|
790
|
+
|
|
766
791
|
function optionalInt(raw) {
|
|
767
792
|
var text = String(raw == null ? "" : raw).trim();
|
|
768
793
|
if (!text) return undefined;
|
|
@@ -791,6 +816,10 @@
|
|
|
791
816
|
if (repo.base) entry.base = repo.base;
|
|
792
817
|
if (repo.prTitle) entry.prTitle = repo.prTitle;
|
|
793
818
|
if (repo.branch) entry.branch = repo.branch;
|
|
819
|
+
var repoAttempts = optionalInt(repo.maxAttempts);
|
|
820
|
+
var repoRounds = optionalInt(repo.maxFixRounds);
|
|
821
|
+
if (repoAttempts !== undefined) entry.maxAttempts = repoAttempts;
|
|
822
|
+
if (repoRounds !== undefined) entry.maxFixRounds = repoRounds;
|
|
794
823
|
repos[name] = entry;
|
|
795
824
|
}
|
|
796
825
|
var body = { revision: settingsDoc ? settingsDoc.revision : "", repos: repos };
|
|
@@ -800,6 +829,10 @@
|
|
|
800
829
|
if (attempts !== undefined) body.maxAttempts = attempts;
|
|
801
830
|
if (stall !== undefined) body.stallTimeoutMs = stall;
|
|
802
831
|
if (rounds !== undefined) body.maxFixRounds = rounds;
|
|
832
|
+
var fingerprint = optionalInt(inputValue("max-same-fingerprint"));
|
|
833
|
+
var cap = optionalNumber(inputValue("daily-dollar-cap"));
|
|
834
|
+
if (fingerprint !== undefined) body.maxSameFingerprint = fingerprint;
|
|
835
|
+
if (cap !== undefined) body.dailyDollarCap = cap;
|
|
803
836
|
var mode = selectedAttr("#sandbox-mode [data-sandbox]", "data-sandbox", "off");
|
|
804
837
|
var domains = domainList(inputValue("sandbox-domains"));
|
|
805
838
|
var sandbox = {};
|
|
@@ -820,7 +853,8 @@
|
|
|
820
853
|
if (repoField) {
|
|
821
854
|
setSettingsTab("repos");
|
|
822
855
|
if (Object.prototype.hasOwnProperty.call(repoDraft, repoField[1])) startEdit(repoField[1]);
|
|
823
|
-
|
|
856
|
+
var slotName = repoField[2] === "maxAttempts" ? "repoAttempts" : repoField[2] === "maxFixRounds" ? "repoRounds" : repoField[2];
|
|
857
|
+
showSlot(slotName, message);
|
|
824
858
|
continue;
|
|
825
859
|
}
|
|
826
860
|
if (key.indexOf("sandbox.allowDomains") === 0) {
|
|
@@ -828,7 +862,7 @@
|
|
|
828
862
|
showSlot("sandbox.allowDomains", message);
|
|
829
863
|
continue;
|
|
830
864
|
}
|
|
831
|
-
if (key === "sandbox.mode" || key === "maxAttempts" || key === "stallTimeoutMs" || key === "maxFixRounds") {
|
|
865
|
+
if (key === "sandbox.mode" || key === "maxAttempts" || key === "stallTimeoutMs" || key === "maxFixRounds" || key === "maxSameFingerprint" || key === "dailyDollarCap") {
|
|
832
866
|
setSettingsTab("global");
|
|
833
867
|
showSlot(key, message);
|
|
834
868
|
continue;
|
|
@@ -977,8 +1011,9 @@
|
|
|
977
1011
|
document.addEventListener("click", function (event) {
|
|
978
1012
|
var target = event.target;
|
|
979
1013
|
if (!target || !target.closest) return;
|
|
980
|
-
|
|
981
|
-
|
|
1014
|
+
var settingsControl = target.closest("#open-settings");
|
|
1015
|
+
if (settingsControl) {
|
|
1016
|
+
if (settingsControl.tagName !== "A") openSettings();
|
|
982
1017
|
return;
|
|
983
1018
|
}
|
|
984
1019
|
if (target.closest("#add-job")) {
|
|
@@ -1135,7 +1170,7 @@
|
|
|
1135
1170
|
return;
|
|
1136
1171
|
}
|
|
1137
1172
|
el.className = "status";
|
|
1138
|
-
var jobs = document.querySelectorAll(".
|
|
1173
|
+
var jobs = document.querySelectorAll(".job").length;
|
|
1139
1174
|
el.textContent = jobs + (jobs === 1 ? " job · " : " jobs · ") + new Date().toLocaleTimeString();
|
|
1140
1175
|
localizeTimes(document);
|
|
1141
1176
|
}
|
|
@@ -1154,6 +1189,27 @@
|
|
|
1154
1189
|
location.reload();
|
|
1155
1190
|
return;
|
|
1156
1191
|
}
|
|
1192
|
+
var toggle = document.getElementById("pause-toggle");
|
|
1193
|
+
var form = document.getElementById("pause-form");
|
|
1194
|
+
if (toggle && form && data && typeof data.paused === "boolean") {
|
|
1195
|
+
toggle.textContent = data.paused ? "Resume" : "Pause";
|
|
1196
|
+
toggle.setAttribute("aria-pressed", data.paused ? "true" : "false");
|
|
1197
|
+
form.setAttribute("action", data.paused ? "/api/resume" : "/api/pause");
|
|
1198
|
+
var label = document.getElementById("run-label");
|
|
1199
|
+
var dot = document.getElementById("run-dot");
|
|
1200
|
+
if (label) label.textContent = data.paused ? "Paused" : "Running";
|
|
1201
|
+
if (dot) dot.classList.toggle("paused", data.paused);
|
|
1202
|
+
}
|
|
1203
|
+
var spend = document.getElementById("spend");
|
|
1204
|
+
if (spend && data && data.spend) {
|
|
1205
|
+
var capText = data.spend.cap == null ? "no cap" : "$" + Number(data.spend.cap).toFixed(2);
|
|
1206
|
+
spend.textContent = "today " + data.spend.day + " $" + Number(data.spend.usd).toFixed(2) + " / " + capText;
|
|
1207
|
+
var capNote = document.getElementById("cap-note");
|
|
1208
|
+
if (capNote) {
|
|
1209
|
+
var blocked = data.spend.blocked === true || (data.spend.cap != null && Number(data.spend.usd) >= Number(data.spend.cap));
|
|
1210
|
+
capNote.hidden = !blocked;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1157
1213
|
stamp(true);
|
|
1158
1214
|
})
|
|
1159
1215
|
.catch(function () { stamp(false); });
|
|
@@ -1191,11 +1247,70 @@
|
|
|
1191
1247
|
}
|
|
1192
1248
|
var previewBtn = document.getElementById("preview-job");
|
|
1193
1249
|
if (previewBtn) previewBtn.addEventListener("click", function () { submitIntake(true); });
|
|
1250
|
+
function readSeen() {
|
|
1251
|
+
try {
|
|
1252
|
+
var raw = localStorage.getItem("kiln-seen");
|
|
1253
|
+
var parsed = raw ? JSON.parse(raw) : {};
|
|
1254
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
return {};
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function writeSeen(map) {
|
|
1261
|
+
try { localStorage.setItem("kiln-seen", JSON.stringify(map)); } catch (err) { /* private mode */ }
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function paintCount(node, count) {
|
|
1265
|
+
if (!node) return;
|
|
1266
|
+
if (!count) {
|
|
1267
|
+
node.hidden = true;
|
|
1268
|
+
node.textContent = "";
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
node.hidden = false;
|
|
1272
|
+
node.textContent = count === 1 ? "1 change since you last looked" : count + " changes since you last looked";
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
function paintUnseen() {
|
|
1276
|
+
var seen = readSeen();
|
|
1277
|
+
var params = [];
|
|
1278
|
+
for (var id in seen) {
|
|
1279
|
+
if (!Object.prototype.hasOwnProperty.call(seen, id)) continue;
|
|
1280
|
+
if (typeof seen[id] !== "string" || !seen[id]) continue;
|
|
1281
|
+
params.push("seen=" + encodeURIComponent(id + "|" + seen[id]));
|
|
1282
|
+
}
|
|
1283
|
+
fetch("/api/unseen" + (params.length ? "?" + params.join("&") : ""), { cache: "no-store", headers: { Accept: "application/json" } })
|
|
1284
|
+
.then(function (response) { return response.ok ? response.json() : null; })
|
|
1285
|
+
.then(function (data) {
|
|
1286
|
+
if (!data) return;
|
|
1287
|
+
var counts = {};
|
|
1288
|
+
var jobs = data.jobs || [];
|
|
1289
|
+
for (var i = 0; i < jobs.length; i++) counts[jobs[i].jobId] = jobs[i].count;
|
|
1290
|
+
var nodes = document.querySelectorAll("[data-unseen-for]");
|
|
1291
|
+
for (var n = 0; n < nodes.length; n++) paintCount(nodes[n], counts[nodes[n].getAttribute("data-unseen-for")] || 0);
|
|
1292
|
+
paintCount(document.getElementById("inbox-unseen"), data.inbox || 0);
|
|
1293
|
+
})
|
|
1294
|
+
.catch(function () { /* the list still works without the badge */ });
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function markLooking() {
|
|
1298
|
+
var id = body.getAttribute("data-job");
|
|
1299
|
+
var latest = body.getAttribute("data-latest");
|
|
1300
|
+
if (!id || !latest) return;
|
|
1301
|
+
var seen = readSeen();
|
|
1302
|
+
seen[id] = latest;
|
|
1303
|
+
writeSeen(seen);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1194
1306
|
showFresh();
|
|
1195
1307
|
renderCompact();
|
|
1196
1308
|
notifyLabel();
|
|
1197
1309
|
applyRepo();
|
|
1198
1310
|
stamp(true);
|
|
1311
|
+
paintUnseen();
|
|
1312
|
+
markLooking();
|
|
1313
|
+
if (document.getElementById("settings-form")) loadSettings();
|
|
1199
1314
|
try {
|
|
1200
1315
|
var open = localStorage.getItem("kiln-drawer");
|
|
1201
1316
|
if (open) openJob(open);
|
package/dist/attention.js
CHANGED
|
@@ -24,6 +24,8 @@ const RETRYABLE_REASONS = new Set(["stalled", "crashed", "orphaned", "failed"]);
|
|
|
24
24
|
* so a waiting retry is not a call for a person.
|
|
25
25
|
*/
|
|
26
26
|
export function scheduledRetry(job) {
|
|
27
|
+
if (job.waitReason)
|
|
28
|
+
return false;
|
|
27
29
|
if (job.state !== "failed")
|
|
28
30
|
return false;
|
|
29
31
|
if (!job.nextRetryAt || !Number.isFinite(Date.parse(job.nextRetryAt)))
|
|
@@ -92,13 +94,18 @@ function toItem(job) {
|
|
|
92
94
|
reason: attentionReason(job, tail),
|
|
93
95
|
...(tail ? { errorTail: tail } : {}),
|
|
94
96
|
...(findings ? { findings } : {}),
|
|
95
|
-
retry: job.state === "failed" || job.state === "dead"
|
|
97
|
+
retry: (job.state === "failed" || job.state === "dead" || (job.state === "needs-human" && Boolean(job.stopReason))) &&
|
|
98
|
+
job.gates.length === 0,
|
|
96
99
|
...(since ? { since } : {}),
|
|
97
100
|
};
|
|
98
101
|
}
|
|
99
102
|
export function attentionReason(job, tail = errorTail(job)) {
|
|
100
103
|
switch (job.state) {
|
|
101
104
|
case "needs-human": {
|
|
105
|
+
if (job.stopReason === "attempt-cap")
|
|
106
|
+
return "Attempt cap reached";
|
|
107
|
+
if (job.stopReason === "fingerprint")
|
|
108
|
+
return "Same failure repeated";
|
|
102
109
|
if (job.feedbackReason)
|
|
103
110
|
return job.feedbackReason;
|
|
104
111
|
const question = gateQuestion(job);
|
|
@@ -109,6 +116,8 @@ export function attentionReason(job, tail = errorTail(job)) {
|
|
|
109
116
|
return AUTH_FAILED_LINE;
|
|
110
117
|
return job.sandboxError ? `Sandbox: ${oneLine(job.sandboxError)}` : "Run died before it finished";
|
|
111
118
|
case "failed": {
|
|
119
|
+
if (job.waitReason)
|
|
120
|
+
return job.waitReason;
|
|
112
121
|
if (job.lastFailureReason === "auth-failed")
|
|
113
122
|
return AUTH_FAILED_LINE;
|
|
114
123
|
if (job.sandboxError)
|
|
@@ -124,8 +133,6 @@ export function attentionReason(job, tail = errorTail(job)) {
|
|
|
124
133
|
return "In review";
|
|
125
134
|
case "invalid":
|
|
126
135
|
return job.invalidReason ? `Invalid: ${oneLine(job.invalidReason)}` : "Job file is invalid";
|
|
127
|
-
case "blocked":
|
|
128
|
-
return oneLine(waitingOn(job));
|
|
129
136
|
case "running": {
|
|
130
137
|
const current = job.steps.find((step) => step.phase === "current" || step.phase === "failed");
|
|
131
138
|
return current ? `Running ${current.id}` : "Running";
|
|
@@ -133,7 +140,9 @@ export function attentionReason(job, tail = errorTail(job)) {
|
|
|
133
140
|
case "approved":
|
|
134
141
|
return "Approved, still finishing";
|
|
135
142
|
case "queued":
|
|
136
|
-
return "Queued, ready to launch";
|
|
143
|
+
return job.waitReason ?? "Queued, ready to launch";
|
|
144
|
+
case "blocked":
|
|
145
|
+
return job.waitReason ?? oneLine(waitingOn(job));
|
|
137
146
|
case "done":
|
|
138
147
|
return job.prUrl ? "Merged" : "Done";
|
|
139
148
|
case "rejected":
|