tines 0.0.211 → 0.0.213
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/README.md +7 -1
- package/dist/index.js +134 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,12 +46,18 @@ tines issues comment <project>/<number> - <<'EOF' # body from stdin; @file al
|
|
|
46
46
|
EOF
|
|
47
47
|
tines events list --issue <project>/<number>
|
|
48
48
|
tines events list --since 2026-09-01T00:00:00Z --until 2026-09-08T00:00:00Z --state Engineering/Review
|
|
49
|
+
tines events list --project Tines --all-pages --max-items 20000 --json
|
|
49
50
|
tines supervisor stats --window 7d --project Tines
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
Issues are addressed as `<project>/<number>`; schedules as `<project>/<name>`; workflow
|
|
53
54
|
states as `<workflow>/<state>`. Every `list` command returns one page — add `--all-pages`
|
|
54
|
-
for the whole list — and every command takes `--json` for machine-readable output.
|
|
55
|
+
for the whole list — and every command takes `--json` for machine-readable output. Complete
|
|
56
|
+
list walks have a default 10,000-item safety ceiling. Use `--max-items <n>` with
|
|
57
|
+
`--all-pages` to choose a different positive finite bound; exceeding it fails without
|
|
58
|
+
printing a partial result. `--limit` remains the per-request page size. A larger bound keeps
|
|
59
|
+
more output in memory and makes more requests, so increase it deliberately or narrow the
|
|
60
|
+
list's filters.
|
|
55
61
|
`tines <noun> --help` lists the rest: `workflows`, `context`, `journal`, `schedules`,
|
|
56
62
|
`runners`, `runs`, `routing`, `supervisor`.
|
|
57
63
|
|
package/dist/index.js
CHANGED
|
@@ -4160,6 +4160,7 @@ function createApiClient(options) {
|
|
|
4160
4160
|
),
|
|
4161
4161
|
getUsage: (filters = {}) => get(`/api/v1/usage${query(filters)}`),
|
|
4162
4162
|
getIssueUsage: (issue) => get(`/api/v1/usage${query({ mode: "issue", issue })}`),
|
|
4163
|
+
getCohortUsage: (filters) => get(`/api/v1/usage${query({ mode: "cohort", ...filters })}`),
|
|
4163
4164
|
getUsageScope: (scope) => get(`/api/v1/usage${query({ scope })}`),
|
|
4164
4165
|
getUsageEvidence: (filters) => get(`/api/v1/usage/evidence${query(filters)}`),
|
|
4165
4166
|
getRun: (id2) => get(`/api/v1/runs/${id2}`),
|
|
@@ -5904,23 +5905,39 @@ function withCommon(cmd) {
|
|
|
5904
5905
|
).option("--api-key <key>", "API key (or set TINES_API_KEY, or run `tines login`)").option("--json", "output the raw JSON response");
|
|
5905
5906
|
}
|
|
5906
5907
|
function withList(cmd) {
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5908
|
+
const listCommand = cmd.option(
|
|
5909
|
+
"--limit <n>",
|
|
5910
|
+
"maximum items to return (page size under --all-pages)",
|
|
5911
|
+
parsePositiveInteger
|
|
5912
|
+
).option("--cursor <cursor>", "resume from the next_cursor of a previous page").addOption(
|
|
5913
|
+
new Option(
|
|
5914
|
+
"--all-pages",
|
|
5915
|
+
"fetch every page, not just the first (default safety ceiling: 10000 items)"
|
|
5916
|
+
).conflicts("cursor")
|
|
5917
|
+
).option(
|
|
5918
|
+
"--max-items <n>",
|
|
5919
|
+
"safety ceiling for --all-pages (default 10000; exceeding it errors)",
|
|
5920
|
+
parseMaxItems
|
|
5921
|
+
).hook("preAction", (_thisCommand, actionCommand) => {
|
|
5922
|
+
const opts = actionCommand.opts();
|
|
5923
|
+
if (opts.maxItems !== void 0 && !opts.allPages) {
|
|
5924
|
+
actionCommand.error("--max-items requires --all-pages");
|
|
5925
|
+
}
|
|
5926
|
+
});
|
|
5927
|
+
return withCommon(listCommand);
|
|
5919
5928
|
}
|
|
5920
5929
|
function parsePositiveInteger(value) {
|
|
5921
|
-
|
|
5930
|
+
return parsePositiveIntegerOption(value, "limit");
|
|
5931
|
+
}
|
|
5932
|
+
function parseMaxItems(value) {
|
|
5933
|
+
return parsePositiveIntegerOption(value, "max-items");
|
|
5934
|
+
}
|
|
5935
|
+
function parsePositiveIntegerOption(value, optionName) {
|
|
5936
|
+
if (!/^[1-9]\d*$/.test(value)) throw new Error(`${optionName} must be a positive integer`);
|
|
5922
5937
|
const result = Number(value);
|
|
5923
|
-
if (!Number.isSafeInteger(result))
|
|
5938
|
+
if (!Number.isSafeInteger(result)) {
|
|
5939
|
+
throw new Error(`${optionName} must be a positive safe integer`);
|
|
5940
|
+
}
|
|
5924
5941
|
return result;
|
|
5925
5942
|
}
|
|
5926
5943
|
function isUsageIdentity(value, prefix) {
|
|
@@ -5999,7 +6016,10 @@ more results: rerun with --all-pages, or resume with --cursor ${res.next_cursor}
|
|
|
5999
6016
|
}
|
|
6000
6017
|
async function fetchList(opts, fetchPage) {
|
|
6001
6018
|
if (opts.allPages) {
|
|
6002
|
-
return {
|
|
6019
|
+
return {
|
|
6020
|
+
items: await listAll(fetchPage, { pageSize: opts.limit, maxItems: opts.maxItems }),
|
|
6021
|
+
next_cursor: null
|
|
6022
|
+
};
|
|
6003
6023
|
}
|
|
6004
6024
|
const res = await fetchPage({ limit: opts.limit, cursor: opts.cursor });
|
|
6005
6025
|
if (res.next_cursor && opts.json && !opts.cursor) {
|
|
@@ -12872,6 +12892,7 @@ function normalizeUrl(value) {
|
|
|
12872
12892
|
}
|
|
12873
12893
|
|
|
12874
12894
|
// src/commands/usage.ts
|
|
12895
|
+
var collect3 = (value, previous = []) => [...previous, value];
|
|
12875
12896
|
function printEvidence(page) {
|
|
12876
12897
|
console.log(
|
|
12877
12898
|
`Usage evidence \xB7 ${page.kind} \xB7 ${page.population} \xB7 ${page.total_count} total \xB7 ${page.sort} ${page.direction}`
|
|
@@ -12886,6 +12907,17 @@ function printEvidence(page) {
|
|
|
12886
12907
|
String(item.attempt_count)
|
|
12887
12908
|
])
|
|
12888
12909
|
]);
|
|
12910
|
+
else if (page.kind === "entries")
|
|
12911
|
+
table([
|
|
12912
|
+
["EVENT", "ISSUE", "STATE", "AT", "EVIDENCE"],
|
|
12913
|
+
...page.items.map((item) => [
|
|
12914
|
+
item.event_id,
|
|
12915
|
+
item.issue_id ?? "Unavailable issue",
|
|
12916
|
+
item.state_name ?? item.state_id ?? "Unclassifiable",
|
|
12917
|
+
new Date(item.created_at).toISOString(),
|
|
12918
|
+
item.chosen ? "Chosen completion" : item.reopening_relevant ? "Reopening witness" : item.qualifies ? "Qualifying entry" : item.unavailable_reason ?? "Excluded"
|
|
12919
|
+
])
|
|
12920
|
+
]);
|
|
12889
12921
|
else
|
|
12890
12922
|
table([
|
|
12891
12923
|
["RUN", "ISSUE", "SPEND", "AT"],
|
|
@@ -12912,6 +12944,18 @@ function printEvidence(page) {
|
|
|
12912
12944
|
console.log(
|
|
12913
12945
|
`Matching total: ${money(page.matching_total.cost_usd)} \xB7 ${page.attempt_count} attempts \xB7 ${page.pending_count} pending`
|
|
12914
12946
|
);
|
|
12947
|
+
if (page.counters && page.history && page.from !== void 0 && page.to !== void 0 && page.observed_through !== void 0) {
|
|
12948
|
+
console.log(
|
|
12949
|
+
`Cohort selection: ${page.counters.distinct_issue_count} completed \xB7 ${page.counters.attempt_count} attempts \xB7 ${page.counters.pending_count} pending`
|
|
12950
|
+
);
|
|
12951
|
+
console.log(
|
|
12952
|
+
`Entries [${new Date(page.from).toISOString()}, ${new Date(page.to).toISOString()}) \xB7 history ${page.history.status} \xB7 observed through ${new Date(page.observed_through).toISOString()}`
|
|
12953
|
+
);
|
|
12954
|
+
if (page.parent_counters)
|
|
12955
|
+
console.log(
|
|
12956
|
+
`Parent cohort: ${page.parent_counters.distinct_issue_count} completed \xB7 ${money(page.parent_matching_total?.cost_usd ?? null)}`
|
|
12957
|
+
);
|
|
12958
|
+
}
|
|
12915
12959
|
if (page.next_cursor) console.log(`Next cursor: ${page.next_cursor}`);
|
|
12916
12960
|
}
|
|
12917
12961
|
function printIssueReport(report) {
|
|
@@ -12928,6 +12972,35 @@ function printIssueReport(report) {
|
|
|
12928
12972
|
for (const line of usageAggregateLines("Lifetime", issue.aggregate)) console.log(line);
|
|
12929
12973
|
}
|
|
12930
12974
|
}
|
|
12975
|
+
function printCohortReport(report) {
|
|
12976
|
+
console.log(`Completed issues \xB7 ${report.workflow.name}`);
|
|
12977
|
+
console.log(
|
|
12978
|
+
`${new Date(report.from).toISOString()} \u2014 ${new Date(report.to).toISOString()} \xB7 costs through exclusive cutoff`
|
|
12979
|
+
);
|
|
12980
|
+
console.log(
|
|
12981
|
+
`Terminal states: ${report.selected_states.map((state) => state.name).join(", ") || "None available"}`
|
|
12982
|
+
);
|
|
12983
|
+
console.log("Completed-issue costs are not additive to period spend.");
|
|
12984
|
+
console.log(
|
|
12985
|
+
`${report.counters.distinct_issue_count} issues \xB7 ${report.counters.attempt_count}/${report.counters.distinct_issue_count} attempts/all issues \xB7 ${money(report.counters.known_cost_per_issue.value_usd)} known USD/all issues`
|
|
12986
|
+
);
|
|
12987
|
+
console.log(
|
|
12988
|
+
`${report.counters.priced_run_coverage.numerator}/${report.counters.priced_run_coverage.denominator} priced finalized runs \xB7 ${report.counters.fully_priced_issue_count}/${report.counters.distinct_issue_count} fully priced issues \xB7 ${report.counters.pending_count} pending \xB7 ${report.counters.zero_run_issue_count} no-run`
|
|
12989
|
+
);
|
|
12990
|
+
console.log(
|
|
12991
|
+
`${report.counters.reopened_issue_count} reopened \xB7 ${report.counters.reopening_history_unavailable_issue_count} reopening unknown \xB7 observed through ${new Date(report.observed_through).toISOString()}`
|
|
12992
|
+
);
|
|
12993
|
+
console.log(
|
|
12994
|
+
`History: ${report.history.status} \xB7 ${report.history.qualifying_fact_count} qualifying entries`
|
|
12995
|
+
);
|
|
12996
|
+
for (const state of report.terminal_states)
|
|
12997
|
+
console.log(
|
|
12998
|
+
`${state.state.name}: ${state.counters.distinct_issue_count} completed \xB7 ${money(state.aggregate.cost_usd)} known`
|
|
12999
|
+
);
|
|
13000
|
+
if (report.counters.distinct_issue_count === 0)
|
|
13001
|
+
console.log("No completed issues in available history");
|
|
13002
|
+
if (report.scope) console.log(`Scope: ${report.scope}`);
|
|
13003
|
+
}
|
|
12931
13004
|
var money = (value) => usageCostLabel(value);
|
|
12932
13005
|
function printReport(report) {
|
|
12933
13006
|
console.log(`Usage \xB7 ${report.filters.project ?? "All projects"} \xB7 ${report.by}`);
|
|
@@ -12986,8 +13059,15 @@ function register12(program3) {
|
|
|
12986
13059
|
withCommon(
|
|
12987
13060
|
program3.command("usage").description("Compare finalized run spend for a period").addOption(
|
|
12988
13061
|
new Option("--window <window>", "today, 7d, or 30d").choices(["today", "7d", "30d"])
|
|
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("--
|
|
12990
|
-
|
|
13062
|
+
).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("--cohort", "report issues completed in the selected workflow and window").option(
|
|
13063
|
+
"--done-state <id>",
|
|
13064
|
+
"terminal state id (repeatable; defaults to all done states)",
|
|
13065
|
+
collect3
|
|
13066
|
+
).option("--scope <token>", "replay a frozen usage scope").addOption(
|
|
13067
|
+
new Option("--evidence <kind>", "list evidence").choices(["issues", "runs", "entries"])
|
|
13068
|
+
).option("--member <issue-id>", "narrow run evidence to one contributing issue").addOption(
|
|
13069
|
+
new Option("--population <population>", "all, finalized, or pending").choices([
|
|
13070
|
+
"all",
|
|
12991
13071
|
"finalized",
|
|
12992
13072
|
"pending"
|
|
12993
13073
|
])
|
|
@@ -13025,6 +13105,8 @@ function register12(program3) {
|
|
|
13025
13105
|
if (opts.scope) {
|
|
13026
13106
|
const reportFlags = [
|
|
13027
13107
|
opts.issue,
|
|
13108
|
+
opts.cohort,
|
|
13109
|
+
opts.doneState,
|
|
13028
13110
|
opts.window,
|
|
13029
13111
|
opts.from,
|
|
13030
13112
|
opts.to,
|
|
@@ -13044,6 +13126,7 @@ function register12(program3) {
|
|
|
13044
13126
|
const report2 = await api2.getUsageScope(opts.scope);
|
|
13045
13127
|
if (opts.json) return printJson(report2);
|
|
13046
13128
|
if (report2.mode === "issue") printIssueReport(report2);
|
|
13129
|
+
else if (report2.mode === "cohort") printCohortReport(report2);
|
|
13047
13130
|
else printReport(report2);
|
|
13048
13131
|
return;
|
|
13049
13132
|
}
|
|
@@ -13066,7 +13149,7 @@ function register12(program3) {
|
|
|
13066
13149
|
cursor
|
|
13067
13150
|
});
|
|
13068
13151
|
for (const item of page.items) {
|
|
13069
|
-
const id2 = "id" in item ? item.id : item.issue_id ?? "unknown";
|
|
13152
|
+
const id2 = "event_id" in item ? item.event_id : "id" in item ? item.id : item.issue_id ?? "unknown";
|
|
13070
13153
|
if (seenIds.has(id2)) throw new Error(`Evidence pagination repeated ${id2}`);
|
|
13071
13154
|
seenIds.add(id2);
|
|
13072
13155
|
}
|
|
@@ -13080,6 +13163,8 @@ function register12(program3) {
|
|
|
13080
13163
|
}
|
|
13081
13164
|
if (opts.issue) {
|
|
13082
13165
|
const contradictions = [
|
|
13166
|
+
opts.cohort,
|
|
13167
|
+
opts.doneState,
|
|
13083
13168
|
opts.window,
|
|
13084
13169
|
opts.from,
|
|
13085
13170
|
opts.to,
|
|
@@ -13101,6 +13186,37 @@ function register12(program3) {
|
|
|
13101
13186
|
printIssueReport(report2);
|
|
13102
13187
|
return;
|
|
13103
13188
|
}
|
|
13189
|
+
if (opts.doneState?.length && !opts.cohort) throw new Error("--done-state requires --cohort");
|
|
13190
|
+
if (opts.cohort) {
|
|
13191
|
+
if (!opts.workflow) throw new Error("--cohort requires --workflow");
|
|
13192
|
+
if ([
|
|
13193
|
+
opts.issue,
|
|
13194
|
+
opts.scope,
|
|
13195
|
+
opts.state,
|
|
13196
|
+
opts.runner,
|
|
13197
|
+
opts.tier,
|
|
13198
|
+
opts.outcome,
|
|
13199
|
+
opts.accountingStatus
|
|
13200
|
+
].some((value) => value !== void 0) || command.getOptionValueSource("by") === "cli")
|
|
13201
|
+
throw new Error("--cohort cannot be combined with issue, scope, grouping, or run filters");
|
|
13202
|
+
if (opts.from === void 0 !== (opts.to === void 0))
|
|
13203
|
+
throw new Error("--from and --to are required together");
|
|
13204
|
+
if (opts.window && opts.from) throw new Error("--window cannot be combined with --from/--to");
|
|
13205
|
+
const api2 = client(opts);
|
|
13206
|
+
const project2 = opts.project && !isUsageIdentity(opts.project, "prj") ? (await resolveProject(api2, opts.project)).id : opts.project;
|
|
13207
|
+
const workflow3 = !isUsageIdentity(opts.workflow, "wf") ? (await resolveWorkflow(api2, opts.workflow)).id : opts.workflow;
|
|
13208
|
+
const report2 = await api2.getCohortUsage({
|
|
13209
|
+
workflow: workflow3,
|
|
13210
|
+
project: project2,
|
|
13211
|
+
window: opts.window,
|
|
13212
|
+
from: opts.from,
|
|
13213
|
+
to: opts.to,
|
|
13214
|
+
done_state: opts.doneState
|
|
13215
|
+
});
|
|
13216
|
+
if (opts.json) return printJson(report2);
|
|
13217
|
+
printCohortReport(report2);
|
|
13218
|
+
return;
|
|
13219
|
+
}
|
|
13104
13220
|
if (opts.from === void 0 !== (opts.to === void 0))
|
|
13105
13221
|
throw new Error("--from and --to are required together");
|
|
13106
13222
|
if (opts.window && opts.from) throw new Error("--window cannot be combined with --from/--to");
|