tines 0.0.142 → 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.
Files changed (2) hide show
  1. package/dist/index.js +118 -3
  2. 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,
@@ -8258,20 +8264,108 @@ recent instances:`);
8258
8264
  }
8259
8265
 
8260
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
+ }
8261
8349
  function register10(program3) {
8262
8350
  const supervisor = program3.command("supervisor").description("The automation kill switch, quota policy, and attempt limit");
8263
8351
  withCommon(
8264
8352
  supervisor.command("status").description("One-screen overview: kill switch, quota, utilization, runners")
8265
8353
  ).action(async (opts) => {
8266
8354
  const api = client(opts);
8267
- const [settings, runnersRes, workflows, activeRunItems] = await Promise.all([
8355
+ const [settings, runnersRes, workflows, activeRunItems, queue] = await Promise.all([
8268
8356
  api.getSupervisorSettings(),
8269
8357
  api.listRunners(),
8270
8358
  api.listWorkflows(),
8271
- listAll((page) => api.listRuns({ active: true, ...page }))
8359
+ listAll((page) => api.listRuns({ active: true, ...page })),
8360
+ api.getSupervisorQueue()
8272
8361
  ]);
8273
8362
  if (opts.json) {
8274
- return printJson({ settings, runners: runnersRes.items, active_runs: activeRunItems });
8363
+ return printJson({
8364
+ settings,
8365
+ runners: runnersRes.items,
8366
+ active_runs: activeRunItems,
8367
+ queue
8368
+ });
8275
8369
  }
8276
8370
  const stateNames = /* @__PURE__ */ new Map();
8277
8371
  for (const wf of workflows.items) {
@@ -8285,6 +8379,27 @@ function register10(program3) {
8285
8379
  `utilization: ${utilizationLabel(settings.quota, activeRunItems, (id) => stateNames.get(id) ?? id)}`
8286
8380
  );
8287
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
+ }
8288
8403
  if (runnersRes.items.length === 0) {
8289
8404
  console.log("runners: none");
8290
8405
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.142",
3
+ "version": "0.0.143",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",