tines 0.0.152 → 0.0.154
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/dist/index.js +66 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4198,6 +4198,9 @@ async function listAll(fetchPage, opts = {}) {
|
|
|
4198
4198
|
return items;
|
|
4199
4199
|
}
|
|
4200
4200
|
|
|
4201
|
+
// ../shared/src/routing.ts
|
|
4202
|
+
var INHERIT_RUNNER_ID = "*";
|
|
4203
|
+
|
|
4201
4204
|
// src/format.ts
|
|
4202
4205
|
function timestamp(ms) {
|
|
4203
4206
|
return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
|
|
@@ -4308,6 +4311,9 @@ function runnerStatusLabel(runner) {
|
|
|
4308
4311
|
}
|
|
4309
4312
|
function ruleTargetsLabel(rule) {
|
|
4310
4313
|
if (rule.targets.length === 0) return "(no targets)";
|
|
4314
|
+
if (rule.targets.length === 1 && rule.targets[0]?.runner_id === "*") {
|
|
4315
|
+
return `*:${rule.targets[0].tier} (inherited runners)`;
|
|
4316
|
+
}
|
|
4311
4317
|
return rule.targets.map(
|
|
4312
4318
|
(t) => `${t.runner_name}${t.tier ? `:${t.tier}` : ""}${t.runner_status === "paused" ? " (paused)" : ""}`
|
|
4313
4319
|
).join(" \u2192 ");
|
|
@@ -5497,6 +5503,8 @@ pinned to ${ex.pin.runner_name ?? ex.pin.runner_id}${ex.pin.tier ? `:${ex.pin.ti
|
|
|
5497
5503
|
} else if (ex.matched_rule) {
|
|
5498
5504
|
console.log(`
|
|
5499
5505
|
matched rule: ${ex.matched_rule.scope_label}`);
|
|
5506
|
+
if (ex.tier_override) console.log(`tier override: ${ex.tier_override}`);
|
|
5507
|
+
if (ex.runner_rule) console.log(`runner source: ${ex.runner_rule.scope_label}`);
|
|
5500
5508
|
}
|
|
5501
5509
|
if (ex.targets.length > 0) {
|
|
5502
5510
|
console.log("targets (preference order):");
|
|
@@ -6720,16 +6728,29 @@ function register7(program3) {
|
|
|
6720
6728
|
);
|
|
6721
6729
|
withCommon(
|
|
6722
6730
|
routing.command("set <target...>").description(
|
|
6723
|
-
"Create or replace
|
|
6731
|
+
"Create or replace a rule with <runner>[:tier] targets, or scoped '*:smartest' tier inheritance"
|
|
6724
6732
|
).option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-l, --label <name>", "scope: issue label name or id")
|
|
6725
6733
|
).action(async (targetSpecs, opts) => {
|
|
6734
|
+
const parsed = targetSpecs.map(parseTargetSpec);
|
|
6735
|
+
const wildcard = parsed.filter((target) => target.name === INHERIT_RUNNER_ID);
|
|
6736
|
+
if (wildcard.length > 0) {
|
|
6737
|
+
if (parsed.length !== 1) die("'*:tier' cannot be mixed with runner targets");
|
|
6738
|
+
if (!wildcard[0].tier)
|
|
6739
|
+
die("the '*' target requires an explicit tier, for example '*:smartest'");
|
|
6740
|
+
if (opts.project === void 0 && opts.state === void 0 && opts.label === void 0) {
|
|
6741
|
+
die("a tier-only rule requires --project, --state, or --label");
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6726
6744
|
const api = client(opts);
|
|
6727
6745
|
const scope = await resolveRoutingScope(api, opts);
|
|
6728
6746
|
const targets = [];
|
|
6729
|
-
for (const
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6747
|
+
for (const { name: name2, tier } of parsed) {
|
|
6748
|
+
if (name2 === INHERIT_RUNNER_ID) {
|
|
6749
|
+
targets.push({ runner_id: INHERIT_RUNNER_ID, tier });
|
|
6750
|
+
} else {
|
|
6751
|
+
const runner = await resolveRunner(api, name2);
|
|
6752
|
+
targets.push(tier ? { runner_id: runner.id, tier } : { runner_id: runner.id });
|
|
6753
|
+
}
|
|
6733
6754
|
}
|
|
6734
6755
|
const { items } = await api.listRoutingRules();
|
|
6735
6756
|
const existing = items.find(
|
|
@@ -6748,7 +6769,7 @@ function register7(program3) {
|
|
|
6748
6769
|
for (const warning of rule.warnings) console.log(`warning: ${warning.message}`);
|
|
6749
6770
|
});
|
|
6750
6771
|
withCommon(
|
|
6751
|
-
routing.command("clear").description("Delete the rule at a scope (issues
|
|
6772
|
+
routing.command("clear").description("Delete the rule at a scope (matching issues are re-evaluated)").option("-p, --project <name>", "scope: project name or id").option("-s, --state <workflow/state>", "scope: workflow-qualified state").option("-l, --label <name>", "scope: issue label name or id")
|
|
6752
6773
|
).action(async (opts) => {
|
|
6753
6774
|
const api = client(opts);
|
|
6754
6775
|
const scope = await resolveRoutingScope(api, opts);
|
|
@@ -6914,6 +6935,23 @@ var USAGE_LIMIT_PREFIXES = [
|
|
|
6914
6935
|
];
|
|
6915
6936
|
var USAGE_LIMIT_PATTERN = /^Fable(?: [^·\n]{1,40})? requires usage credits\./;
|
|
6916
6937
|
var ANSI = /\x1b\[[0-9;]*m/g;
|
|
6938
|
+
var PROVIDER_ERROR = /(?:\bAPI Error:\s*5\d{2}\b|\bECONNRESET\b|\bconnection reset(?: by peer)?\b|\bsocket hang up\b)/i;
|
|
6939
|
+
function providerErrorDetail(value) {
|
|
6940
|
+
if (typeof value !== "string") return null;
|
|
6941
|
+
const clean = value.replace(ANSI, "").trim();
|
|
6942
|
+
return clean && PROVIDER_ERROR.test(clean) ? clean : null;
|
|
6943
|
+
}
|
|
6944
|
+
function providerErrorFromStreamEvent(event) {
|
|
6945
|
+
if (typeof event !== "object" || event === null) return null;
|
|
6946
|
+
const ev = event;
|
|
6947
|
+
if (ev.type !== "result" || ev.is_error !== true) return null;
|
|
6948
|
+
const detail = providerErrorDetail(ev.result);
|
|
6949
|
+
return detail ? { source: "stream", detail } : null;
|
|
6950
|
+
}
|
|
6951
|
+
function providerErrorFromStderrLine(line) {
|
|
6952
|
+
const detail = providerErrorDetail(line);
|
|
6953
|
+
return detail ? { source: "stderr", detail } : null;
|
|
6954
|
+
}
|
|
6917
6955
|
function toEpochMs(value) {
|
|
6918
6956
|
return value < 1e12 ? Math.round(value * 1e3) : Math.round(value);
|
|
6919
6957
|
}
|
|
@@ -7030,10 +7068,14 @@ function parseResetTime(text2, now) {
|
|
|
7030
7068
|
var RateLimitDetector = class {
|
|
7031
7069
|
stream = null;
|
|
7032
7070
|
stderr = null;
|
|
7071
|
+
providerStream = null;
|
|
7072
|
+
providerStderr = null;
|
|
7033
7073
|
pending = "";
|
|
7034
7074
|
noteStreamEvent(event) {
|
|
7035
7075
|
const signal = rateLimitFromStreamEvent(event);
|
|
7036
7076
|
if (signal) this.stream = signal;
|
|
7077
|
+
const provider = providerErrorFromStreamEvent(event);
|
|
7078
|
+
if (provider) this.providerStream = provider;
|
|
7037
7079
|
}
|
|
7038
7080
|
noteStderr(chunk, now = Date.now()) {
|
|
7039
7081
|
this.pending += chunk;
|
|
@@ -7055,9 +7097,15 @@ var RateLimitDetector = class {
|
|
|
7055
7097
|
signal() {
|
|
7056
7098
|
return this.stream ?? this.stderr;
|
|
7057
7099
|
}
|
|
7100
|
+
/** A structured provider error when present; stderr as a fallback. */
|
|
7101
|
+
providerError() {
|
|
7102
|
+
return this.providerStream ?? this.providerStderr;
|
|
7103
|
+
}
|
|
7058
7104
|
line(raw, now) {
|
|
7059
7105
|
const signal = rateLimitFromStderrLine(raw, now);
|
|
7060
7106
|
if (signal) this.stderr = signal;
|
|
7107
|
+
const provider = providerErrorFromStderrLine(raw);
|
|
7108
|
+
if (provider) this.providerStderr = provider;
|
|
7061
7109
|
}
|
|
7062
7110
|
};
|
|
7063
7111
|
|
|
@@ -7199,9 +7247,9 @@ var RunTable = class {
|
|
|
7199
7247
|
* (shutdown, restart) rather than the work failing: the supervisor then
|
|
7200
7248
|
* spares the issue a strike. Everything a run can do wrong to itself —
|
|
7201
7249
|
* a non-zero harness exit, a workspace that would not set up — must not
|
|
7202
|
-
* set it.
|
|
7203
|
-
*
|
|
7204
|
-
*
|
|
7250
|
+
* set it. Exceptions are provider-side failures: usage exhaustion uses
|
|
7251
|
+
* `{ judgment: 'rate_limited' }` with its reset time, while transient 5xx
|
|
7252
|
+
* and transport outages use `interrupted` and the normal short backoff.
|
|
7205
7253
|
*/
|
|
7206
7254
|
async finishAndCleanup(run, status, error, judgment) {
|
|
7207
7255
|
if (run.settled) return this.cleanup(run);
|
|
@@ -7965,6 +8013,7 @@ async function runDaemon(opts) {
|
|
|
7965
8013
|
void table2.finishAndCleanup(run, "completed");
|
|
7966
8014
|
} else {
|
|
7967
8015
|
const limited = signal ? null : run.limiter?.signal() ?? null;
|
|
8016
|
+
const providerError = signal ? null : run.limiter?.providerError() ?? null;
|
|
7968
8017
|
if (limited) {
|
|
7969
8018
|
log(
|
|
7970
8019
|
`run ${runId}: harness rate limited (${limited.detail}); reporting without a strike`
|
|
@@ -7973,6 +8022,13 @@ async function runDaemon(opts) {
|
|
|
7973
8022
|
judgment: "rate_limited",
|
|
7974
8023
|
...limited.resumeAt !== null ? { resume_at: limited.resumeAt } : {}
|
|
7975
8024
|
});
|
|
8025
|
+
} else if (providerError) {
|
|
8026
|
+
log(
|
|
8027
|
+
`run ${runId}: transient provider error (${providerError.detail}); reporting without a strike`
|
|
8028
|
+
);
|
|
8029
|
+
void table2.finishAndCleanup(run, "failed", `provider error: ${providerError.detail}`, {
|
|
8030
|
+
judgment: "interrupted"
|
|
8031
|
+
});
|
|
7976
8032
|
} else {
|
|
7977
8033
|
void table2.finishAndCleanup(
|
|
7978
8034
|
run,
|
|
@@ -8745,7 +8801,7 @@ function queueHeadline(block) {
|
|
|
8745
8801
|
case "no_rule":
|
|
8746
8802
|
return "no matching routing rule";
|
|
8747
8803
|
case "no_targets":
|
|
8748
|
-
return "the matching rule has no targets";
|
|
8804
|
+
return "the matching rule has no effective targets";
|
|
8749
8805
|
case "ambiguous_rule":
|
|
8750
8806
|
return "two routing rules tie";
|
|
8751
8807
|
case "pin_missing":
|