tines 0.0.208 → 0.0.210

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.js +312 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -45,6 +45,8 @@ tines issues comment <project>/<number> - <<'EOF' # body from stdin; @file al
45
45
  …
46
46
  EOF
47
47
  tines events list --issue <project>/<number>
48
+ tines events list --since 2026-09-01T00:00:00Z --until 2026-09-08T00:00:00Z --state Engineering/Review
49
+ tines supervisor stats --window 7d --project Tines
48
50
  ```
49
51
 
50
52
  Issues are addressed as `<project>/<number>`; schedules as `<project>/<name>`; workflow
package/dist/index.js CHANGED
@@ -3482,6 +3482,7 @@ function parsePrSpec(spec) {
3482
3482
  return repoUrl2 ? { repo_url: repoUrl2, number: Number.parseInt(url[2], 10) } : null;
3483
3483
  }
3484
3484
  var MODEL_TIERS = ["smartest", "balanced", "cheapest"];
3485
+ var RUN_END_OUTCOMES = ["advanced", "stalled", "interrupted"];
3485
3486
  var RUNNER_ONLINE_WINDOW_MS = 2 * 60 * 1e3;
3486
3487
  var DEFAULT_RESUME_WINDOW_HOURS = 48;
3487
3488
  var DEFAULT_RESUME_MAX_TURNS = 25;
@@ -3575,6 +3576,11 @@ function utilizationLabel(quota, activeRuns, stateName = (id2) => id2) {
3575
3576
  ([stateId, { name: name2, n }]) => `${name2} ${n}/${quota.overrides[stateId] ?? quota.default_limit}`
3576
3577
  ).join(" \xB7 ");
3577
3578
  }
3579
+ var RUN_OUTCOME_BUCKETS = [
3580
+ ...RUN_END_OUTCOMES,
3581
+ "failed",
3582
+ "unrecorded"
3583
+ ];
3578
3584
  var LIBRARY_MAX_BYTES = 5 * 1024 * 1024;
3579
3585
 
3580
3586
  // ../shared/src/events.ts
@@ -4153,6 +4159,9 @@ function createApiClient(options) {
4153
4159
  `/api/v1/runs${query(filters)}`
4154
4160
  ),
4155
4161
  getUsage: (filters = {}) => get(`/api/v1/usage${query(filters)}`),
4162
+ getIssueUsage: (issue) => get(`/api/v1/usage${query({ mode: "issue", issue })}`),
4163
+ getUsageScope: (scope) => get(`/api/v1/usage${query({ scope })}`),
4164
+ getUsageEvidence: (filters) => get(`/api/v1/usage/evidence${query(filters)}`),
4156
4165
  getRun: (id2) => get(`/api/v1/runs/${id2}`),
4157
4166
  /**
4158
4167
  * The run's complete log (not the 256 KB tail `getRun` returns) as a
@@ -4180,7 +4189,9 @@ function createApiClient(options) {
4180
4189
  getPreferences: () => get("/api/v1/preferences"),
4181
4190
  updatePreferences: (body) => request("PATCH", "/api/v1/preferences", body),
4182
4191
  getSupervisorSettings: () => get("/api/v1/supervisor/settings"),
4183
- getSupervisorQueue: () => get("/api/v1/supervisor/queue"),
4192
+ getSupervisorQueue: (q = {}) => get(`/api/v1/supervisor/queue${query(q)}`),
4193
+ getSupervisorStats: (q = {}) => get(`/api/v1/supervisor/stats${query(q)}`),
4194
+ getSupervisorSentBack: (q) => get(`/api/v1/supervisor/stats/sent-back${query(q)}`),
4184
4195
  updateSupervisorSettings: (body) => request("PUT", "/api/v1/supervisor/settings", body),
4185
4196
  // API keys (create/revoke require a browser session, not a key)
4186
4197
  listApiKeys: (filters = {}) => get(`/api/v1/api-keys${query(filters)}`),
@@ -4423,6 +4434,38 @@ async function listAll(fetchPage, opts = {}) {
4423
4434
  // ../shared/src/routing.ts
4424
4435
  var INHERIT_RUNNER_ID = "*";
4425
4436
 
4437
+ // ../shared/src/stats-format.ts
4438
+ var MIN = 6e4;
4439
+ var HOUR = 60 * MIN;
4440
+ var DAY = 24 * HOUR;
4441
+ function durationLabel(ms) {
4442
+ if (ms === null || ms === void 0) return "\u2014";
4443
+ if (ms < MIN) return `${Math.round(ms / 1e3)} s`;
4444
+ if (ms < HOUR) return `${Math.round(ms / MIN)} min`;
4445
+ if (ms < DAY) return `${(ms / HOUR).toFixed(1)} h`;
4446
+ return `${(ms / DAY).toFixed(1)} d`;
4447
+ }
4448
+ function shareLabel(share) {
4449
+ if (share === null || share === void 0) return "\u2014";
4450
+ return `${Math.round(share * 100)}%`;
4451
+ }
4452
+ function deltaLabel(delta, kind) {
4453
+ if (delta === null || delta === void 0) return "";
4454
+ if (delta === 0) return "=";
4455
+ const arrow = delta > 0 ? "\u25B2" : "\u25BC";
4456
+ const size = Math.abs(delta);
4457
+ switch (kind) {
4458
+ case "ms":
4459
+ return `${arrow} ${durationLabel(size)}`;
4460
+ case "share":
4461
+ return `${arrow} ${Math.round(size * 100)} pp`;
4462
+ case "ratio":
4463
+ return `${arrow} ${size.toFixed(1)}`;
4464
+ default:
4465
+ return `${arrow} ${size}`;
4466
+ }
4467
+ }
4468
+
4426
4469
  // ../shared/src/issue-transfer.ts
4427
4470
  var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
4428
4471
  function conflictsByKey(context2) {
@@ -8168,27 +8211,33 @@ function registerTime(program3) {
8168
8211
  function registerEvents(program3) {
8169
8212
  const events = program3.command("events").description("Read the activity log");
8170
8213
  withList(
8171
- events.command("list").description("List activity events, newest first").option("-i, --issue <ref>", "filter to one issue (<project>/<number>)").option("-p, --project <name>", "filter by project name or id").option("-t, --type <type>", "filter by event type (e.g. issue.transitioned)")
8172
- ).action(async (opts) => {
8173
- const api = client(opts);
8174
- const issueId = opts.issue ? (await resolveIssue(api, opts.issue)).id : void 0;
8175
- const res = await fetchList(
8176
- opts,
8177
- (page) => api.listEvents({
8178
- issue: issueId,
8179
- project: opts.project,
8180
- type: opts.type,
8181
- ...page
8182
- })
8183
- );
8184
- printList(res, opts, (items) => {
8185
- if (items.length === 0) return console.log("no events");
8186
- table([
8187
- ["WHEN", "ACTOR", "EVENT"],
8188
- ...items.map((ev) => [timestamp(ev.created_at), displayActor(ev), eventSummary(ev)])
8189
- ]);
8190
- });
8191
- });
8214
+ events.command("list").description("List activity events, newest first").option("-i, --issue <ref>", "filter to one issue (<project>/<number>)").option("-p, --project <name>", "filter by project name or id").option("-t, --type <type>", "filter by event type (e.g. issue.transitioned)").option("--since <time>", "inclusive lower bound (ISO 8601 or epoch ms)").option("--until <time>", "exclusive upper bound (ISO 8601 or epoch ms)").option("--state <workflow/state>", "events referencing a workflow state")
8215
+ ).action(
8216
+ async (opts) => {
8217
+ const api = client(opts);
8218
+ const issueId = opts.issue ? (await resolveIssue(api, opts.issue)).id : void 0;
8219
+ const stateId = opts.state ? (await resolveStateFlag(api, opts.state)).state.id : void 0;
8220
+ const res = await fetchList(
8221
+ opts,
8222
+ (page) => api.listEvents({
8223
+ issue: issueId,
8224
+ project: opts.project,
8225
+ type: opts.type,
8226
+ since: opts.since,
8227
+ until: opts.until,
8228
+ state: stateId,
8229
+ ...page
8230
+ })
8231
+ );
8232
+ printList(res, opts, (items) => {
8233
+ if (items.length === 0) return console.log("no events");
8234
+ table([
8235
+ ["WHEN", "ACTOR", "EVENT"],
8236
+ ...items.map((ev) => [timestamp(ev.created_at), displayActor(ev), eventSummary(ev)])
8237
+ ]);
8238
+ });
8239
+ }
8240
+ );
8192
8241
  }
8193
8242
 
8194
8243
  // src/commands/projects.ts
@@ -11009,9 +11058,11 @@ function usageAggregateLines(label, aggregate) {
11009
11058
  function usageEvidenceLines(id2, dimensions, accounting) {
11010
11059
  const dims = ["project", "workflow", "state", "outcome", "runner", "tier"].map((name2) => `${name2}=${dimensions[name2].name} [${dimensions[name2].id ?? "unknown"}]`).join(" \xB7 ");
11011
11060
  const diagnostics = Object.entries(accounting.diagnostics).filter(([, count]) => count > 0).map(([name2, count]) => `${name2}=${count}`).join(" \xB7 ");
11061
+ const tokens = ["input_tokens", "output_tokens", "cache_read_tokens", "cache_write_tokens"].map((name2) => `${name2}=${present(accounting.tokens[name2])}`).join(" \xB7 ");
11012
11062
  return [
11013
11063
  `Evidence ${id2}: ${dims}`,
11014
11064
  `Accounting ${id2}: ${accounting.status} \xB7 source ${accounting.source ?? "unavailable"} \xB7 exact cost ${accounting.cost_exact ?? "unavailable"}${accounting.pricing_reason ? ` \xB7 reason ${accounting.pricing_reason}` : ""}${diagnostics ? ` \xB7 diagnostics ${diagnostics}` : ""}`,
11065
+ `Tokens ${id2}: ${tokens} \xB7 invalid_tokens=${accounting.invalid_tokens.length ? accounting.invalid_tokens.join(",") : "none"}`,
11015
11066
  ...accounting.source === "calculated" ? [`Rate ${id2}: ${basisLine(accounting.basis)}`] : []
11016
11067
  ];
11017
11068
  }
@@ -11829,10 +11880,26 @@ function queueFix(block) {
11829
11880
  return null;
11830
11881
  }
11831
11882
  }
11883
+ function outcomeLabel(stats) {
11884
+ const o = stats.current.runs.outcomes;
11885
+ const parts = [`adv ${o.advanced}`, `stalled ${o.stalled}`, `failed ${o.failed}`];
11886
+ if (o.interrupted > 0) parts.push(`intr ${o.interrupted}`);
11887
+ if (o.unrecorded > 0) parts.push(`unrecorded ${o.unrecorded}`);
11888
+ if (stats.current.runs.active > 0) parts.push(`active ${stats.current.runs.active}`);
11889
+ return parts.join(" \xB7 ");
11890
+ }
11891
+ function statsLevers() {
11892
+ return [
11893
+ "levers:",
11894
+ " queue wait tines supervisor quota roster --state <wf>/<state>=<n>",
11895
+ " runs tines runs list --state <wf>/<state>",
11896
+ " sent back tines context show <wf>/<state> instructions"
11897
+ ];
11898
+ }
11832
11899
  function register10(program3) {
11833
11900
  const supervisor = program3.command("supervisor").description("The automation kill switch, quota policy, and attempt limit");
11834
11901
  withCommon(
11835
- supervisor.command("status").description("One-screen overview: kill switch, quota, utilization, runners")
11902
+ supervisor.command("status").description("One-screen overview: kill switch, quota, utilization, runners").option("--project <name-or-id>", "filter waiting work to one project")
11836
11903
  ).action(async (opts) => {
11837
11904
  const api = client(opts);
11838
11905
  const [settings, runnersRes, workflows, activeRunItems, queue] = await Promise.all([
@@ -11840,7 +11907,7 @@ function register10(program3) {
11840
11907
  api.listRunners(),
11841
11908
  api.listWorkflows(),
11842
11909
  listAll((page) => api.listRuns({ active: true, ...page })),
11843
- api.getSupervisorQueue()
11910
+ api.getSupervisorQueue({ project: opts.project })
11844
11911
  ]);
11845
11912
  if (opts.json) {
11846
11913
  return printJson({
@@ -11897,6 +11964,71 @@ function register10(program3) {
11897
11964
  );
11898
11965
  }
11899
11966
  });
11967
+ withCommon(
11968
+ supervisor.command("stats").description("Per-stage flow this week: queue wait, work time, runs per visit, sent back").option("--window <window>", "Rolling window, e.g. 24h or 7d", "7d").option("--project <ref>", "Narrow to one project (id or name)").option("--sent-back <workflow/state>", "Show the issues behind one sent-back figure")
11969
+ ).action(async (opts) => {
11970
+ const api = client(opts);
11971
+ const project = opts.project ? await resolveProject(api, opts.project) : null;
11972
+ if (opts.sentBack) {
11973
+ const { state } = await resolveStateFlag(api, opts.sentBack);
11974
+ const detail = await api.getSupervisorSentBack({
11975
+ state: state.id,
11976
+ window: opts.window,
11977
+ project: project?.id ?? void 0
11978
+ });
11979
+ if (opts.json) return printJson(detail);
11980
+ console.log(`sent back from ${detail.state.workflow_name}/${detail.state.name}`);
11981
+ if (detail.prompt)
11982
+ console.log(`prompt: ${detail.prompt.name} (current v${detail.prompt.current_version})`);
11983
+ if (detail.items.length === 0) return console.log("no issues sent back");
11984
+ for (const item of detail.items) {
11985
+ console.log(
11986
+ `${item.issue.project_name}/${item.issue.number} \u2192 ${item.to_state_name} \xB7 prompt ${item.prompt_version ? `v${item.prompt_version}` : "unknown"} \xB7 ${item.actor.user_name}`
11987
+ );
11988
+ console.log(` ${item.comment?.excerpt.split("\n")[0] ?? "no comment"}`);
11989
+ }
11990
+ return;
11991
+ }
11992
+ const report = await api.getSupervisorStats({
11993
+ window: opts.window,
11994
+ project: project?.id ?? void 0
11995
+ });
11996
+ if (opts.json) return printJson(report);
11997
+ if (report.states.length === 0) {
11998
+ console.log("No agent stage saw work in the last window.");
11999
+ return;
12000
+ }
12001
+ const windowLabel = opts.window ?? "7d";
12002
+ console.log(
12003
+ `this week (${windowLabel}${report.project ? `, project ${report.project.name}` : ""})${report.previous ? ` \u2014 vs the ${windowLabel} before` : ""}`
12004
+ );
12005
+ table([
12006
+ [" stage", "visits", "queue p50", "p90", "work p50", "runs/visit", "ended", "sent back"],
12007
+ ...report.states.map((s) => [
12008
+ ` ${s.workflow_name}/${s.state_name}`,
12009
+ `${s.current.visits}\xB7${s.current.exits} ${deltaLabel(s.delta.visits, "count")}`.trim(),
12010
+ `${durationLabel(s.current.queue_wait?.p50 ?? null)} ${deltaLabel(s.delta.queue_wait_p50, "ms")}`.trim(),
12011
+ durationLabel(s.current.queue_wait?.p90 ?? null),
12012
+ `${durationLabel(s.current.work?.p50 ?? null)} ${deltaLabel(s.delta.work_p50, "ms")}`.trim(),
12013
+ s.current.runs.per_visit === null ? "\u2014" : `${s.current.runs.per_visit.toFixed(1)} ${deltaLabel(s.delta.runs_per_visit, "ratio")}`.trim(),
12014
+ outcomeLabel(s),
12015
+ `${s.current.sent_back.count} of ${s.current.exits} (${shareLabel(s.current.sent_back.share)}) \xB7 agents ${s.current.sent_back.agent} ${deltaLabel(s.delta.sent_back_share, "share")}`.trim()
12016
+ ])
12017
+ ]);
12018
+ if (report.markers.length > 0) {
12019
+ console.log("changes:");
12020
+ for (const marker of report.markers) {
12021
+ console.log(` ${marker.label}`);
12022
+ for (const effect of marker.effects) {
12023
+ const state = report.states.find((row) => row.state_id === effect.state_id);
12024
+ console.log(
12025
+ ` ${state?.state_name ?? effect.state_id}: since ${effect.after?.exits ?? 0} exits, ${shareLabel(effect.after?.sent_back_share)} sent back, queue ${durationLabel(effect.after?.queue_wait_p50)} \xB7 before ${effect.before?.exits ?? 0} exits, ${shareLabel(effect.before?.sent_back_share)} sent back, queue ${durationLabel(effect.before?.queue_wait_p50)}`
12026
+ );
12027
+ }
12028
+ }
12029
+ }
12030
+ for (const line of statsLevers()) console.log(line);
12031
+ });
11900
12032
  withCommon(
11901
12033
  supervisor.command("enable").description("Resume automation (the kill switch on)")
11902
12034
  ).action(async (opts) => {
@@ -12740,6 +12872,62 @@ function normalizeUrl(value) {
12740
12872
  }
12741
12873
 
12742
12874
  // src/commands/usage.ts
12875
+ function printEvidence(page) {
12876
+ console.log(
12877
+ `Usage evidence \xB7 ${page.kind} \xB7 ${page.population} \xB7 ${page.total_count} total \xB7 ${page.sort} ${page.direction}`
12878
+ );
12879
+ if (page.kind === "issues")
12880
+ table([
12881
+ ["ISSUE", "SPEND", "COVERAGE", "RUNS"],
12882
+ ...page.items.map((item) => [
12883
+ item.issue_ref ? `${item.issue_ref.project_name}/${item.issue_ref.number} ${item.issue_ref.title}` : item.issue_id ? `Unavailable issue (${item.issue_id})` : "Unknown issue",
12884
+ money(item.aggregate.cost_usd),
12885
+ item.aggregate.coverage,
12886
+ String(item.attempt_count)
12887
+ ])
12888
+ ]);
12889
+ else
12890
+ table([
12891
+ ["RUN", "ISSUE", "SPEND", "AT"],
12892
+ ...page.items.map((item) => {
12893
+ const run2 = item;
12894
+ return [
12895
+ run2.id,
12896
+ run2.issue_id,
12897
+ page.population === "pending" ? "Pending" : money(run2.usage_accounting?.cost ?? null),
12898
+ new Date(run2.ended_at ?? run2.created_at).toISOString()
12899
+ ];
12900
+ })
12901
+ ]);
12902
+ if (page.kind === "runs" && page.population === "finalized")
12903
+ for (const item of page.items) {
12904
+ if ("id" in item && "usage_accounting" in item && item.usage_dimensions)
12905
+ for (const line of usageEvidenceLines(
12906
+ item.id,
12907
+ item.usage_dimensions,
12908
+ item.usage_accounting
12909
+ ))
12910
+ console.log(line);
12911
+ }
12912
+ console.log(
12913
+ `Matching total: ${money(page.matching_total.cost_usd)} \xB7 ${page.attempt_count} attempts \xB7 ${page.pending_count} pending`
12914
+ );
12915
+ if (page.next_cursor) console.log(`Next cursor: ${page.next_cursor}`);
12916
+ }
12917
+ function printIssueReport(report) {
12918
+ const { issue } = report;
12919
+ console.log(
12920
+ `Usage \xB7 ${issue.issue_ref ? `${issue.issue_ref.project_name}/${issue.issue_ref.number}` : issue.issue_id} \xB7 Lifetime through now`
12921
+ );
12922
+ console.log(`as of ${new Date(report.cutoff).toISOString()} \xB7 direct retained attempts`);
12923
+ if (issue.attempt_count === 0) console.log("No agent runs");
12924
+ else {
12925
+ console.log(
12926
+ `${money(issue.aggregate.cost_usd)} \xB7 ${issue.aggregate.coverage} \xB7 ${issue.aggregate.finalized_run_count} finalized \xB7 ${issue.pending_count} pending at cutoff`
12927
+ );
12928
+ for (const line of usageAggregateLines("Lifetime", issue.aggregate)) console.log(line);
12929
+ }
12930
+ }
12743
12931
  var money = (value) => usageCostLabel(value);
12744
12932
  function printReport(report) {
12745
12933
  console.log(`Usage \xB7 ${report.filters.project ?? "All projects"} \xB7 ${report.by}`);
@@ -12798,7 +12986,12 @@ function register12(program3) {
12798
12986
  withCommon(
12799
12987
  program3.command("usage").description("Compare finalized run spend for a period").addOption(
12800
12988
  new Option("--window <window>", "today, 7d, or 30d").choices(["today", "7d", "30d"])
12801
- ).option("--from <bound>", "custom inclusive start (date or offset timestamp)").option("--to <bound>", "custom exclusive end (date or offset timestamp)").option("--project <name-or-id>", "project scope (including archived), or unknown").option("--workflow <name-or-id>", "workflow filter, or unknown").option("--state <id>", "starting state id (requires --workflow)").option("--runner <name-or-id>", "runner filter, or unknown").option("--tier <tier>", "tier filter").addOption(
12989
+ ).option("--from <bound>", "custom inclusive start (date or offset timestamp)").option("--to <bound>", "custom exclusive end (date or offset timestamp)").option("--issue <ref>", "direct issue lifetime through now").option("--scope <token>", "replay a frozen usage scope").addOption(new Option("--evidence <kind>", "list evidence").choices(["issues", "runs"])).option("--member <issue-id>", "narrow run evidence to one contributing issue").addOption(
12990
+ new Option("--population <population>", "finalized or pending").choices([
12991
+ "finalized",
12992
+ "pending"
12993
+ ])
12994
+ ).addOption(new Option("--sort <sort>", "evidence sort").choices(["cost", "time"])).addOption(new Option("--direction <direction>", "sort direction").choices(["asc", "desc"])).option("--cursor <token>", "evidence page cursor").option("--limit <count>", "evidence page size (1-100)").option("--all-pages", "fetch every evidence page").option("--project <name-or-id>", "project scope (including archived), or unknown").option("--workflow <name-or-id>", "workflow filter, or unknown").option("--state <id>", "starting state id (requires --workflow)").option("--runner <name-or-id>", "runner filter, or unknown").option("--tier <tier>", "tier filter").addOption(
12802
12995
  new Option("--outcome <outcome>", "recorded outcome").choices([
12803
12996
  "advanced",
12804
12997
  "stalled",
@@ -12814,7 +13007,100 @@ function register12(program3) {
12814
13007
  ).addOption(
12815
13008
  new Option("--by <dimension>", "group dimension").choices(["project", "workflow", "state", "outcome", "runner", "tier"]).default("workflow")
12816
13009
  )
12817
- ).action(async (opts) => {
13010
+ ).action(async (opts, command) => {
13011
+ const evidenceOnly = [
13012
+ opts.member,
13013
+ opts.population,
13014
+ opts.sort,
13015
+ opts.direction,
13016
+ opts.cursor,
13017
+ opts.limit,
13018
+ opts.allPages
13019
+ ];
13020
+ if (evidenceOnly.some((value) => value !== void 0) && !opts.evidence)
13021
+ throw new Error(
13022
+ "--member/--population/--sort/--direction/--cursor/--limit/--all-pages require --evidence"
13023
+ );
13024
+ if (opts.evidence && !opts.scope) throw new Error("--evidence requires --scope");
13025
+ if (opts.scope) {
13026
+ const reportFlags = [
13027
+ opts.issue,
13028
+ opts.window,
13029
+ opts.from,
13030
+ opts.to,
13031
+ opts.project,
13032
+ opts.workflow,
13033
+ opts.state,
13034
+ opts.runner,
13035
+ opts.tier,
13036
+ opts.outcome,
13037
+ opts.accountingStatus
13038
+ ];
13039
+ if (command.getOptionValueSource("by") === "cli") reportFlags.push(opts.by);
13040
+ if (reportFlags.some((value) => value !== void 0))
13041
+ throw new Error("--scope cannot be combined with report options");
13042
+ const api2 = client(opts);
13043
+ if (!opts.evidence) {
13044
+ const report2 = await api2.getUsageScope(opts.scope);
13045
+ if (opts.json) return printJson(report2);
13046
+ if (report2.mode === "issue") printIssueReport(report2);
13047
+ else printReport(report2);
13048
+ return;
13049
+ }
13050
+ let cursor = opts.cursor;
13051
+ const seenCursors = /* @__PURE__ */ new Set();
13052
+ const seenIds = /* @__PURE__ */ new Set();
13053
+ let combined = null;
13054
+ do {
13055
+ if (cursor && seenCursors.has(cursor))
13056
+ throw new Error("Evidence pagination repeated a cursor");
13057
+ if (cursor) seenCursors.add(cursor);
13058
+ const page = await api2.getUsageEvidence({
13059
+ scope: opts.scope,
13060
+ kind: opts.evidence,
13061
+ population: opts.population,
13062
+ member: opts.member,
13063
+ sort: opts.sort,
13064
+ direction: opts.direction,
13065
+ limit: opts.limit ? Number(opts.limit) : void 0,
13066
+ cursor
13067
+ });
13068
+ for (const item of page.items) {
13069
+ const id2 = "id" in item ? item.id : item.issue_id ?? "unknown";
13070
+ if (seenIds.has(id2)) throw new Error(`Evidence pagination repeated ${id2}`);
13071
+ seenIds.add(id2);
13072
+ }
13073
+ combined = combined ? { ...page, items: [...combined.items, ...page.items], previous_cursor: null } : page;
13074
+ cursor = page.next_cursor ?? void 0;
13075
+ } while (opts.allPages && cursor);
13076
+ if (!combined) throw new Error("Evidence response was empty");
13077
+ if (opts.json) return printJson(combined);
13078
+ printEvidence(combined);
13079
+ return;
13080
+ }
13081
+ if (opts.issue) {
13082
+ const contradictions = [
13083
+ opts.window,
13084
+ opts.from,
13085
+ opts.to,
13086
+ opts.project,
13087
+ opts.workflow,
13088
+ opts.state,
13089
+ opts.runner,
13090
+ opts.tier,
13091
+ opts.outcome,
13092
+ opts.accountingStatus
13093
+ ];
13094
+ if (command.getOptionValueSource("by") === "cli") contradictions.push(opts.by);
13095
+ if (contradictions.some((value) => value !== void 0))
13096
+ throw new Error("--issue cannot be combined with period or filter options");
13097
+ const api2 = client(opts);
13098
+ const issueId = isUsageIdentity(opts.issue, "iss") ? opts.issue : (await resolveIssue(api2, opts.issue)).id;
13099
+ const report2 = await api2.getIssueUsage(issueId);
13100
+ if (opts.json) return printJson(report2);
13101
+ printIssueReport(report2);
13102
+ return;
13103
+ }
12818
13104
  if (opts.from === void 0 !== (opts.to === void 0))
12819
13105
  throw new Error("--from and --to are required together");
12820
13106
  if (opts.window && opts.from) throw new Error("--window cannot be combined with --from/--to");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.208",
3
+ "version": "0.0.210",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",