tines 0.0.221 → 0.0.223
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 +86 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5623,6 +5623,15 @@ function runnerStatusLabel(runner) {
|
|
|
5623
5623
|
return "rate limited";
|
|
5624
5624
|
return runner.online ? "online" : "offline";
|
|
5625
5625
|
}
|
|
5626
|
+
function runnerConcurrencyLabel(runner) {
|
|
5627
|
+
if (runner.type !== "local" || !runner.concurrency_control) return `${runner.max_concurrent}`;
|
|
5628
|
+
const control = runner.concurrency_control;
|
|
5629
|
+
if (control.status === "applied")
|
|
5630
|
+
return `${runner.max_concurrent} applied \xB7 ceiling ${control.ceiling ?? "?"}`;
|
|
5631
|
+
if (control.status === "pending")
|
|
5632
|
+
return `${runner.max_concurrent} pending \xB7 confirmed ${control.applied_cap ?? "none"} \xB7 ceiling ${control.ceiling ?? "?"}`;
|
|
5633
|
+
return `${runner.max_concurrent} web off (${control.reason ?? "unavailable"})`;
|
|
5634
|
+
}
|
|
5626
5635
|
function ruleTargetsLabel(rule) {
|
|
5627
5636
|
if (rule.targets.length === 0) return "(no targets)";
|
|
5628
5637
|
if (rule.targets.length === 1 && rule.targets[0]?.runner_id === "*") {
|
|
@@ -10092,6 +10101,23 @@ function loadDaemonState(path2) {
|
|
|
10092
10101
|
function saveDaemonState(path2, runs) {
|
|
10093
10102
|
writeJsonFile(path2, { runs });
|
|
10094
10103
|
}
|
|
10104
|
+
function daemonDeclinesPath(dir, runnerId) {
|
|
10105
|
+
return join5(dir, `daemon-declines-${runnerId}.json`);
|
|
10106
|
+
}
|
|
10107
|
+
function loadDaemonDeclines(path2) {
|
|
10108
|
+
const state = readJsonFile(path2);
|
|
10109
|
+
if (!state || !Array.isArray(state.run_ids)) return [];
|
|
10110
|
+
return [
|
|
10111
|
+
...new Set(
|
|
10112
|
+
state.run_ids.filter(
|
|
10113
|
+
(id2) => typeof id2 === "string" && id2.length >= 1 && id2.length <= 128 && /^[\x21-\x7e]+$/.test(id2)
|
|
10114
|
+
)
|
|
10115
|
+
)
|
|
10116
|
+
];
|
|
10117
|
+
}
|
|
10118
|
+
function saveDaemonDeclines(path2, runIds) {
|
|
10119
|
+
writeJsonFile(path2, { run_ids: [...runIds] });
|
|
10120
|
+
}
|
|
10095
10121
|
function processStartTimeMs(pid) {
|
|
10096
10122
|
try {
|
|
10097
10123
|
const stat2 = readFileSync8(`/proc/${pid}/stat`, "utf8");
|
|
@@ -10198,7 +10224,7 @@ async function ensureRunnerCredentials(opts) {
|
|
|
10198
10224
|
name: opts.name,
|
|
10199
10225
|
harness: opts.harness,
|
|
10200
10226
|
...opts.command !== void 0 ? { command: opts.command } : {},
|
|
10201
|
-
max_concurrent: opts.maxConcurrent,
|
|
10227
|
+
max_concurrent: opts.allowRemoteConcurrency ? 1 : opts.maxConcurrent,
|
|
10202
10228
|
hostname: hostname(),
|
|
10203
10229
|
platform: `${platform()} ${arch()}`
|
|
10204
10230
|
});
|
|
@@ -10304,6 +10330,7 @@ async function runDaemon(opts) {
|
|
|
10304
10330
|
harness: opts.harness,
|
|
10305
10331
|
...opts.command !== void 0 ? { command: opts.command } : {},
|
|
10306
10332
|
maxConcurrent: opts.maxConcurrent,
|
|
10333
|
+
allowRemoteConcurrency: opts.allowRemoteConcurrency,
|
|
10307
10334
|
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
10308
10335
|
log
|
|
10309
10336
|
});
|
|
@@ -10316,7 +10343,12 @@ async function runDaemon(opts) {
|
|
|
10316
10343
|
}
|
|
10317
10344
|
const client2 = createApiClient({ baseUrl, apiKey: creds.token });
|
|
10318
10345
|
const statePath = daemonStatePath(opts.configDir, creds.runner_id);
|
|
10346
|
+
const declinesPath = daemonDeclinesPath(opts.configDir, creds.runner_id);
|
|
10319
10347
|
let shuttingDown = false;
|
|
10348
|
+
let effectiveConcurrency = opts.allowRemoteConcurrency ? 1 : opts.maxConcurrent;
|
|
10349
|
+
let appliedConcurrency;
|
|
10350
|
+
const declinedAssignments = new Set(loadDaemonDeclines(declinesPath));
|
|
10351
|
+
let warnedUnsupportedConcurrency = false;
|
|
10320
10352
|
const refresher = new CliRefresher(() => installAgentCli({ configDir: opts.configDir, log }), {
|
|
10321
10353
|
ttlMs: CLI_REFRESH_TTL_MS
|
|
10322
10354
|
});
|
|
@@ -10764,7 +10796,7 @@ async function runDaemon(opts) {
|
|
|
10764
10796
|
process.on("SIGINT", () => void shutdown());
|
|
10765
10797
|
process.on("SIGTERM", () => void shutdown());
|
|
10766
10798
|
log(
|
|
10767
|
-
`polling ${baseUrl} every ${Math.round(opts.pollIntervalMs / 1e3)}s (harness ${opts.harness},
|
|
10799
|
+
`polling ${baseUrl} every ${Math.round(opts.pollIntervalMs / 1e3)}s (harness ${opts.harness}, ${opts.allowRemoteConcurrency ? `web-adjustable with local ceiling ${opts.maxConcurrent}` : `local cap ${opts.maxConcurrent}`}) \u2014 Ctrl-C to stop`
|
|
10768
10800
|
);
|
|
10769
10801
|
let failures = 0;
|
|
10770
10802
|
while (!shuttingDown) {
|
|
@@ -10773,7 +10805,16 @@ async function runDaemon(opts) {
|
|
|
10773
10805
|
const res = await client2.pollRunner(creds.runner_id, {
|
|
10774
10806
|
instance_id: instanceId,
|
|
10775
10807
|
owned_runs: table2.ids(),
|
|
10776
|
-
|
|
10808
|
+
// Kept for older servers. Opted-in daemons advertise only the last
|
|
10809
|
+
// accepted scheduling cap, never the higher machine ceiling.
|
|
10810
|
+
max_concurrent: effectiveConcurrency,
|
|
10811
|
+
concurrency_control: {
|
|
10812
|
+
version: 1,
|
|
10813
|
+
allow_remote: opts.allowRemoteConcurrency,
|
|
10814
|
+
ceiling: opts.maxConcurrent,
|
|
10815
|
+
...appliedConcurrency ? { applied: appliedConcurrency } : {}
|
|
10816
|
+
},
|
|
10817
|
+
...declinedAssignments.size > 0 ? { declined_assignments: [...declinedAssignments].slice(0, 100) } : {},
|
|
10777
10818
|
...effortCapabilities ? { effort_capabilities: effortCapabilities } : {},
|
|
10778
10819
|
// Stated on every poll while pending; absent otherwise, which
|
|
10779
10820
|
// the server reads as "not draining" — so a daemon that died
|
|
@@ -10782,11 +10823,31 @@ async function runDaemon(opts) {
|
|
|
10782
10823
|
});
|
|
10783
10824
|
failures = 0;
|
|
10784
10825
|
for (const runId of res.cancels) killWithoutFinish(runId);
|
|
10826
|
+
let declinesChanged = false;
|
|
10827
|
+
for (const runId of res.released_assignments ?? []) {
|
|
10828
|
+
if (declinedAssignments.delete(runId)) declinesChanged = true;
|
|
10829
|
+
}
|
|
10830
|
+
if (declinesChanged) saveDaemonDeclines(declinesPath, declinedAssignments);
|
|
10831
|
+
if (res.concurrency_control) {
|
|
10832
|
+
const control = res.concurrency_control;
|
|
10833
|
+
if (opts.allowRemoteConcurrency && control.available && Number.isSafeInteger(control.revision) && Number.isInteger(control.cap) && control.cap >= 1 && control.cap <= opts.maxConcurrent) {
|
|
10834
|
+
effectiveConcurrency = control.cap;
|
|
10835
|
+
appliedConcurrency = { revision: control.revision, cap: control.cap };
|
|
10836
|
+
}
|
|
10837
|
+
} else if (opts.allowRemoteConcurrency && !warnedUnsupportedConcurrency) {
|
|
10838
|
+
warnedUnsupportedConcurrency = true;
|
|
10839
|
+
log(
|
|
10840
|
+
"web concurrency adjustment unavailable: upgrade the server; retaining conservative cap"
|
|
10841
|
+
);
|
|
10842
|
+
}
|
|
10785
10843
|
for (const assignment of res.assignments) {
|
|
10786
|
-
if (table2.size >= opts.maxConcurrent) {
|
|
10844
|
+
if (declinedAssignments.size > 0 || table2.size >= opts.maxConcurrent) {
|
|
10787
10845
|
log(
|
|
10788
|
-
`
|
|
10846
|
+
`run ${assignment.run.id}: refusing launch beyond local ceiling ${opts.maxConcurrent}; requesting safe release`
|
|
10789
10847
|
);
|
|
10848
|
+
declinedAssignments.add(assignment.run.id);
|
|
10849
|
+
saveDaemonDeclines(declinesPath, declinedAssignments);
|
|
10850
|
+
continue;
|
|
10790
10851
|
}
|
|
10791
10852
|
void launch(assignment);
|
|
10792
10853
|
}
|
|
@@ -10862,7 +10923,15 @@ function withDaemonFlags(cmd) {
|
|
|
10862
10923
|
).option("--harness <harness>", "claude-code | codex | custom", "claude-code").option(
|
|
10863
10924
|
"--command <template>",
|
|
10864
10925
|
"custom harness command template ({prompt_file}, {workspace}, {model})"
|
|
10865
|
-
).option(
|
|
10926
|
+
).option(
|
|
10927
|
+
"--max-concurrent <n>",
|
|
10928
|
+
"local maximum simultaneous runs (remote-adjustment ceiling when enabled)",
|
|
10929
|
+
(v) => Number(v),
|
|
10930
|
+
1
|
|
10931
|
+
).option(
|
|
10932
|
+
"--allow-remote-concurrency",
|
|
10933
|
+
"allow signed-in operators to request concurrency up to --max-concurrent"
|
|
10934
|
+
).option("--poll-interval <seconds>", "seconds between polls", (v) => Number.parseInt(v, 10), 15).option(
|
|
10866
10935
|
"--keep-workspaces <mode>",
|
|
10867
10936
|
"keep settled runs' workspaces for debugging: never | failed | always",
|
|
10868
10937
|
"never"
|
|
@@ -10910,6 +10979,7 @@ function parseDaemonFlags(opts) {
|
|
|
10910
10979
|
harness,
|
|
10911
10980
|
...opts.command !== void 0 ? { command: opts.command } : {},
|
|
10912
10981
|
maxConcurrent: opts.maxConcurrent,
|
|
10982
|
+
allowRemoteConcurrency: opts.allowRemoteConcurrency === true,
|
|
10913
10983
|
pollIntervalSeconds: opts.pollInterval,
|
|
10914
10984
|
keepWorkspaces,
|
|
10915
10985
|
keepWorkspacesForHours: opts.keepWorkspacesFor,
|
|
@@ -10961,6 +11031,7 @@ function daemonArgs(input2) {
|
|
|
10961
11031
|
];
|
|
10962
11032
|
if (input2.command !== void 0) args.push("--command", input2.command);
|
|
10963
11033
|
if (input2.maxConcurrent !== 1) args.push("--max-concurrent", String(input2.maxConcurrent));
|
|
11034
|
+
if (input2.allowRemoteConcurrency) args.push("--allow-remote-concurrency");
|
|
10964
11035
|
if (input2.pollIntervalSeconds !== void 0 && input2.pollIntervalSeconds !== 15)
|
|
10965
11036
|
args.push("--poll-interval", String(input2.pollIntervalSeconds));
|
|
10966
11037
|
if (input2.keepWorkspaces !== void 0 && input2.keepWorkspaces !== "never")
|
|
@@ -11228,6 +11299,7 @@ stop it first \u2014 \`tines runners pause ${settings.name}\`, wait for 0 active
|
|
|
11228
11299
|
harness: settings.harness,
|
|
11229
11300
|
...settings.command !== void 0 ? { command: settings.command } : {},
|
|
11230
11301
|
maxConcurrent: settings.maxConcurrent,
|
|
11302
|
+
allowRemoteConcurrency: settings.allowRemoteConcurrency,
|
|
11231
11303
|
...resolveApiKey(opts) !== void 0 ? { apiKey: resolveApiKey(opts) } : {},
|
|
11232
11304
|
log: log2
|
|
11233
11305
|
});
|
|
@@ -11258,6 +11330,7 @@ stop it first \u2014 \`tines runners pause ${settings.name}\`, wait for 0 active
|
|
|
11258
11330
|
harness: harnessFlag(settings.harness),
|
|
11259
11331
|
...settings.command !== void 0 ? { command: settings.command } : {},
|
|
11260
11332
|
maxConcurrent: settings.maxConcurrent,
|
|
11333
|
+
allowRemoteConcurrency: settings.allowRemoteConcurrency,
|
|
11261
11334
|
pollIntervalSeconds: settings.pollIntervalSeconds,
|
|
11262
11335
|
keepWorkspaces: settings.keepWorkspaces,
|
|
11263
11336
|
keepWorkspacesForHours: settings.keepWorkspacesForHours,
|
|
@@ -11429,12 +11502,13 @@ function register8(program3) {
|
|
|
11429
11502
|
if (opts.json) return printJson(res);
|
|
11430
11503
|
if (res.items.length === 0) return console.log("no runners");
|
|
11431
11504
|
table([
|
|
11432
|
-
["NAME", "TYPE", "STATUS", "RUNS", "TIER", "LAST SEEN"],
|
|
11505
|
+
["NAME", "TYPE", "STATUS", "RUNS", "CONCURRENCY", "TIER", "LAST SEEN"],
|
|
11433
11506
|
...res.items.map((r) => [
|
|
11434
11507
|
r.name,
|
|
11435
11508
|
r.type,
|
|
11436
11509
|
runnerStatusLabel(r),
|
|
11437
11510
|
`${r.active_runs}/${r.max_concurrent}`,
|
|
11511
|
+
runnerConcurrencyLabel(r),
|
|
11438
11512
|
r.default_tier,
|
|
11439
11513
|
r.last_seen_at ? timestamp(r.last_seen_at) : "\u2014"
|
|
11440
11514
|
])
|
|
@@ -11449,6 +11523,7 @@ function register8(program3) {
|
|
|
11449
11523
|
console.log(
|
|
11450
11524
|
`active runs: ${runner.active_runs}/${runner.max_concurrent} timeout: ${runner.max_run_minutes}m default tier: ${runner.default_tier}`
|
|
11451
11525
|
);
|
|
11526
|
+
console.log(`concurrency: ${runnerConcurrencyLabel(runner)}`);
|
|
11452
11527
|
if (runner.last_seen_at) console.log(`last seen: ${timestamp(runner.last_seen_at)}`);
|
|
11453
11528
|
if (runner.backoff_reason === "rate_limit" && runner.backoff_until) {
|
|
11454
11529
|
console.log(
|
|
@@ -11691,6 +11766,7 @@ function register8(program3) {
|
|
|
11691
11766
|
harness: settings.harness,
|
|
11692
11767
|
command: settings.command,
|
|
11693
11768
|
maxConcurrent: settings.maxConcurrent,
|
|
11769
|
+
allowRemoteConcurrency: settings.allowRemoteConcurrency,
|
|
11694
11770
|
pollIntervalMs: settings.pollIntervalSeconds * 1e3,
|
|
11695
11771
|
configDir: defaultConfigDir(),
|
|
11696
11772
|
cliRefresh: opts.cliRefresh,
|
|
@@ -12178,7 +12254,7 @@ function queueFix(block) {
|
|
|
12178
12254
|
const runner = block.runnerName ?? "the routed runner";
|
|
12179
12255
|
switch (block.verdict) {
|
|
12180
12256
|
case "at_capacity":
|
|
12181
|
-
return `
|
|
12257
|
+
return `edit ${runner} on the Agents page; if web adjustment is off, relaunch locally with tines runner daemon --allow-remote-concurrency --max-concurrent N`;
|
|
12182
12258
|
case "quota_exhausted":
|
|
12183
12259
|
return block.binding?.kind === "state_roster" ? "raise the roster limit \u2014 tines supervisor quota roster --default <n> --state <workflow>/<state>=<n> (this replaces the whole roster, so restate every override you keep)" : "raise the cap \u2014 tines supervisor quota global <n> \u2014 or switch to a per-state roster with tines supervisor quota roster";
|
|
12184
12260
|
case "offline":
|
|
@@ -12283,7 +12359,8 @@ function register10(program3) {
|
|
|
12283
12359
|
` ${r.name}`,
|
|
12284
12360
|
r.type,
|
|
12285
12361
|
runnerStatusLabel(r),
|
|
12286
|
-
`${r.active_runs}/${r.max_concurrent}
|
|
12362
|
+
`${r.active_runs}/${r.max_concurrent}`,
|
|
12363
|
+
runnerConcurrencyLabel(r)
|
|
12287
12364
|
])
|
|
12288
12365
|
);
|
|
12289
12366
|
}
|