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.
Files changed (2) hide show
  1. package/dist/index.js +135 -5
  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,
@@ -5314,7 +5320,16 @@ function printExplainer(issue, ex) {
5314
5320
  console.log(`
5315
5321
  ${ex.verdict}
5316
5322
  `);
5317
- table(ex.checks.map((c) => [` ${c.ok ? "ok" : "FAIL"}`, c.name.replaceAll("_", " "), c.detail]));
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("--name <name>", "runner name, unique per user (default: this hostname)").option("--harness <harness>", "claude-code | codex | custom", "claude-code").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({ settings, runners: runnersRes.items, active_runs: activeRunItems });
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.141",
3
+ "version": "0.0.143",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",