tines 0.0.141 → 0.0.143
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 +135 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4029,6 +4029,7 @@ function createApiClient(options) {
|
|
|
4029
4029
|
deleteRoutingRule: (id) => request("DELETE", `/api/v1/routing-rules/${id}`),
|
|
4030
4030
|
// Supervisor settings
|
|
4031
4031
|
getSupervisorSettings: () => get("/api/v1/supervisor/settings"),
|
|
4032
|
+
getSupervisorQueue: () => get("/api/v1/supervisor/queue"),
|
|
4032
4033
|
updateSupervisorSettings: (body) => request("PUT", "/api/v1/supervisor/settings", body),
|
|
4033
4034
|
// API keys (create/revoke require a browser session, not a key)
|
|
4034
4035
|
listApiKeys: (filters = {}) => get(`/api/v1/api-keys${query(filters)}`),
|
|
@@ -4266,6 +4267,11 @@ function ageLabel(isoTimestamp, now = Date.now()) {
|
|
|
4266
4267
|
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
|
|
4267
4268
|
return `${Math.floor(seconds / 86400)}d`;
|
|
4268
4269
|
}
|
|
4270
|
+
function hoursLabel(ms, now = Date.now()) {
|
|
4271
|
+
const hours = Math.max(0, now - ms) / 36e5;
|
|
4272
|
+
if (hours < 48) return `${hours < 10 ? hours.toFixed(1) : Math.round(hours)}h`;
|
|
4273
|
+
return `${Math.round(hours / 24)}d`;
|
|
4274
|
+
}
|
|
4269
4275
|
function keptWorkspaceRow(kept, sizeBytes, now = Date.now()) {
|
|
4270
4276
|
return [
|
|
4271
4277
|
kept.run_id,
|
|
@@ -5314,7 +5320,16 @@ function printExplainer(issue, ex) {
|
|
|
5314
5320
|
console.log(`
|
|
5315
5321
|
${ex.verdict}
|
|
5316
5322
|
`);
|
|
5317
|
-
table(
|
|
5323
|
+
table(
|
|
5324
|
+
ex.checks.map((c) => [
|
|
5325
|
+
` ${c.ok ? "ok" : "FAIL"}`,
|
|
5326
|
+
c.name.replaceAll("_", " "),
|
|
5327
|
+
c.detail,
|
|
5328
|
+
// The remedy, when the check has one and it is something to run
|
|
5329
|
+
// here: a link is useless without an origin.
|
|
5330
|
+
c.action?.cli ? `fix: ${c.action.cli}` : ""
|
|
5331
|
+
])
|
|
5332
|
+
);
|
|
5318
5333
|
if (ex.pin) {
|
|
5319
5334
|
console.log(
|
|
5320
5335
|
`
|
|
@@ -7277,6 +7292,9 @@ async function runDaemon(opts) {
|
|
|
7277
7292
|
creds = { runner_id: registered.runner.id, token: registered.runner_token };
|
|
7278
7293
|
saveRunnerCredentials(opts.configDir, opts.url, opts.name, creds);
|
|
7279
7294
|
log(`registered runner "${opts.name}" (${creds.runner_id}); token stored in ${opts.configDir}`);
|
|
7295
|
+
log(
|
|
7296
|
+
`next: route work to "${opts.name}" and turn automation on \u2014 ${opts.url}/agents (or: tines routing set ${opts.name} && tines supervisor enable)`
|
|
7297
|
+
);
|
|
7280
7298
|
}
|
|
7281
7299
|
const client2 = createApiClient({ baseUrl: opts.url, apiKey: creds.token });
|
|
7282
7300
|
const statePath = daemonStatePath(opts.configDir, creds.runner_id);
|
|
@@ -7865,7 +7883,10 @@ function register8(program3) {
|
|
|
7865
7883
|
withCommon(
|
|
7866
7884
|
runnerCmd.command("daemon").description(
|
|
7867
7885
|
"Run the local runner daemon: register/reconnect, poll for assigned runs, execute them"
|
|
7868
|
-
).option(
|
|
7886
|
+
).option(
|
|
7887
|
+
"--name <name>",
|
|
7888
|
+
"runner name, unique per user; name it machine-plus-harness, e.g. macbook-claude (default: this hostname)"
|
|
7889
|
+
).option("--harness <harness>", "claude-code | codex | custom", "claude-code").option(
|
|
7869
7890
|
"--command <template>",
|
|
7870
7891
|
"custom harness command template ({prompt_file}, {workspace}, {model})"
|
|
7871
7892
|
).option("--max-concurrent <n>", "maximum simultaneous runs", (v) => Number.parseInt(v, 10), 1).option(
|
|
@@ -8243,20 +8264,108 @@ recent instances:`);
|
|
|
8243
8264
|
}
|
|
8244
8265
|
|
|
8245
8266
|
// src/commands/supervisor.ts
|
|
8267
|
+
function queueBlocks(groups) {
|
|
8268
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
8269
|
+
for (const g of groups) {
|
|
8270
|
+
const key = `${g.verdict}|${g.runner_id ?? ""}`;
|
|
8271
|
+
const seen = byKey.get(key);
|
|
8272
|
+
if (seen) {
|
|
8273
|
+
seen.count += g.count;
|
|
8274
|
+
seen.groups.push(g);
|
|
8275
|
+
seen.binding ??= g.binding;
|
|
8276
|
+
} else {
|
|
8277
|
+
byKey.set(key, {
|
|
8278
|
+
verdict: g.verdict,
|
|
8279
|
+
runnerName: g.runner_name,
|
|
8280
|
+
binding: g.binding,
|
|
8281
|
+
count: g.count,
|
|
8282
|
+
groups: [g]
|
|
8283
|
+
});
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
return [...byKey.values()].sort((a, b) => b.count - a.count);
|
|
8287
|
+
}
|
|
8288
|
+
function queueHeadline(block) {
|
|
8289
|
+
const runner = block.runnerName ?? "the routed runner";
|
|
8290
|
+
switch (block.verdict) {
|
|
8291
|
+
case "ok":
|
|
8292
|
+
return "dispatching next pass";
|
|
8293
|
+
case "at_capacity":
|
|
8294
|
+
return block.binding?.kind === "max_concurrent" ? `at capacity on ${runner} (${block.binding.current}/${block.binding.limit})` : `at capacity on ${runner}`;
|
|
8295
|
+
case "quota_exhausted":
|
|
8296
|
+
if (block.binding?.kind === "global_cap")
|
|
8297
|
+
return `global cap reached (${block.binding.current}/${block.binding.limit})`;
|
|
8298
|
+
if (block.binding?.kind === "state_roster")
|
|
8299
|
+
return `roster limit reached (${block.binding.current}/${block.binding.limit})`;
|
|
8300
|
+
return "quota exhausted";
|
|
8301
|
+
case "offline":
|
|
8302
|
+
return `${runner} offline`;
|
|
8303
|
+
case "paused":
|
|
8304
|
+
return `${runner} paused`;
|
|
8305
|
+
case "draining":
|
|
8306
|
+
return `${runner} draining`;
|
|
8307
|
+
case "backing_off":
|
|
8308
|
+
return `${runner} backing off`;
|
|
8309
|
+
case "no_rule":
|
|
8310
|
+
return "no matching routing rule";
|
|
8311
|
+
case "no_targets":
|
|
8312
|
+
return "the matching rule has no targets";
|
|
8313
|
+
case "ambiguous_rule":
|
|
8314
|
+
return "two routing rules tie";
|
|
8315
|
+
case "pin_missing":
|
|
8316
|
+
return "pinned to a runner that is gone";
|
|
8317
|
+
case "automation_off":
|
|
8318
|
+
return "automation is off";
|
|
8319
|
+
case "parked":
|
|
8320
|
+
return "parked";
|
|
8321
|
+
}
|
|
8322
|
+
}
|
|
8323
|
+
function queueFix(block) {
|
|
8324
|
+
const runner = block.runnerName ?? "the routed runner";
|
|
8325
|
+
switch (block.verdict) {
|
|
8326
|
+
case "at_capacity":
|
|
8327
|
+
return `raise the cap on ${runner} \u2014 restart its daemon with tines runner daemon --max-concurrent N, or edit the runner on the Agents page`;
|
|
8328
|
+
case "quota_exhausted":
|
|
8329
|
+
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";
|
|
8330
|
+
case "offline":
|
|
8331
|
+
return `start the daemon on that machine \u2014 tines runner daemon`;
|
|
8332
|
+
case "paused":
|
|
8333
|
+
return `tines runners resume ${runner}`;
|
|
8334
|
+
case "no_rule":
|
|
8335
|
+
return "add a routing rule for that state \u2014 tines routing set <runner> --state <workflow>/<state>";
|
|
8336
|
+
case "no_targets":
|
|
8337
|
+
case "ambiguous_rule":
|
|
8338
|
+
return "tines routing list, then edit the rule";
|
|
8339
|
+
case "pin_missing":
|
|
8340
|
+
return "clear the pin on those issues";
|
|
8341
|
+
case "automation_off":
|
|
8342
|
+
return "tines supervisor enable";
|
|
8343
|
+
case "backing_off":
|
|
8344
|
+
return "retries automatically; check the daemon log if it keeps failing";
|
|
8345
|
+
default:
|
|
8346
|
+
return null;
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8246
8349
|
function register10(program3) {
|
|
8247
8350
|
const supervisor = program3.command("supervisor").description("The automation kill switch, quota policy, and attempt limit");
|
|
8248
8351
|
withCommon(
|
|
8249
8352
|
supervisor.command("status").description("One-screen overview: kill switch, quota, utilization, runners")
|
|
8250
8353
|
).action(async (opts) => {
|
|
8251
8354
|
const api = client(opts);
|
|
8252
|
-
const [settings, runnersRes, workflows, activeRunItems] = await Promise.all([
|
|
8355
|
+
const [settings, runnersRes, workflows, activeRunItems, queue] = await Promise.all([
|
|
8253
8356
|
api.getSupervisorSettings(),
|
|
8254
8357
|
api.listRunners(),
|
|
8255
8358
|
api.listWorkflows(),
|
|
8256
|
-
listAll((page) => api.listRuns({ active: true, ...page }))
|
|
8359
|
+
listAll((page) => api.listRuns({ active: true, ...page })),
|
|
8360
|
+
api.getSupervisorQueue()
|
|
8257
8361
|
]);
|
|
8258
8362
|
if (opts.json) {
|
|
8259
|
-
return printJson({
|
|
8363
|
+
return printJson({
|
|
8364
|
+
settings,
|
|
8365
|
+
runners: runnersRes.items,
|
|
8366
|
+
active_runs: activeRunItems,
|
|
8367
|
+
queue
|
|
8368
|
+
});
|
|
8260
8369
|
}
|
|
8261
8370
|
const stateNames = /* @__PURE__ */ new Map();
|
|
8262
8371
|
for (const wf of workflows.items) {
|
|
@@ -8270,6 +8379,27 @@ function register10(program3) {
|
|
|
8270
8379
|
`utilization: ${utilizationLabel(settings.quota, activeRunItems, (id) => stateNames.get(id) ?? id)}`
|
|
8271
8380
|
);
|
|
8272
8381
|
console.log(`attempt limit: ${settings.attempt_limit} strikes, then the issue parks`);
|
|
8382
|
+
if (queue.waiting === 0) {
|
|
8383
|
+
console.log("waiting: nothing");
|
|
8384
|
+
} else {
|
|
8385
|
+
console.log(`waiting: ${queue.waiting} ${queue.waiting === 1 ? "issue" : "issues"}`);
|
|
8386
|
+
for (const block of queueBlocks(queue.groups)) {
|
|
8387
|
+
const states = block.groups.map(
|
|
8388
|
+
(g) => `${stateNames.get(g.state_id) ?? g.state_name} ${g.count} (oldest ${hoursLabel(g.oldest_entered_at)})`
|
|
8389
|
+
).join(" \xB7 ");
|
|
8390
|
+
console.log(` ${queueHeadline(block)}: ${states}`);
|
|
8391
|
+
const fix = queueFix(block);
|
|
8392
|
+
if (fix) console.log(` fix: ${fix}`);
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
console.log(
|
|
8396
|
+
queue.parked.count === 0 ? "parked: none" : `parked: ${queue.parked.count}, oldest ${hoursLabel(queue.parked.oldest_entered_at ?? Date.now())}`
|
|
8397
|
+
);
|
|
8398
|
+
if (queue.awaiting_human.count > 0) {
|
|
8399
|
+
console.log(
|
|
8400
|
+
`awaiting you: ${queue.awaiting_human.count} issues, oldest ${hoursLabel(queue.awaiting_human.oldest_entered_at ?? Date.now())}`
|
|
8401
|
+
);
|
|
8402
|
+
}
|
|
8273
8403
|
if (runnersRes.items.length === 0) {
|
|
8274
8404
|
console.log("runners: none");
|
|
8275
8405
|
} else {
|