@bli-cockpit/mcp 0.1.84 → 0.1.85
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/usage-tools.d.ts +35 -0
- package/dist/usage-tools.js +39 -2
- package/package.json +1 -1
package/dist/usage-tools.d.ts
CHANGED
|
@@ -1,4 +1,39 @@
|
|
|
1
1
|
import { type ToolDeps } from "./tool-result.js";
|
|
2
|
+
/**
|
|
3
|
+
* BLI-12561: the sentences about the GitHub counts, worded exactly as the /usage
|
|
4
|
+
* page and `cockpit usage people` word them (`apps/dashboard/src/lib/usage/
|
|
5
|
+
* usage-github-words.ts` is the original; every copy has the same cases). This
|
|
6
|
+
* package does not import the dashboard, so the words are carried here.
|
|
7
|
+
*/
|
|
8
|
+
interface GithubCounts {
|
|
9
|
+
match: string;
|
|
10
|
+
pull_requests_opened: number | null;
|
|
11
|
+
pull_requests_merged: number | null;
|
|
12
|
+
commits: number | null;
|
|
13
|
+
}
|
|
14
|
+
interface GithubBlock {
|
|
15
|
+
status: "ok" | "unavailable";
|
|
16
|
+
reason?: string;
|
|
17
|
+
detail?: string;
|
|
18
|
+
counts_label?: string;
|
|
19
|
+
repositories?: unknown[];
|
|
20
|
+
oldest_pull_request_at?: string | null;
|
|
21
|
+
newest_pull_request_at?: string | null;
|
|
22
|
+
unattributed?: {
|
|
23
|
+
pull_requests_opened: number;
|
|
24
|
+
pull_requests_merged: number;
|
|
25
|
+
commits: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare function githubWords(github: GithubBlock, people: readonly {
|
|
29
|
+
github?: GithubCounts;
|
|
30
|
+
}[]): {
|
|
31
|
+
lines: string[];
|
|
32
|
+
label: string;
|
|
33
|
+
};
|
|
34
|
+
/** The sentence appended to the text half, or the line that says a dashboard sent no GitHub block. */
|
|
35
|
+
export declare function githubSentence(body: Record<string, unknown>): string;
|
|
2
36
|
export declare function registerUsageTools(server: {
|
|
3
37
|
registerTool: (...args: never[]) => unknown;
|
|
4
38
|
}, deps: ToolDeps): void;
|
|
39
|
+
export {};
|
package/dist/usage-tools.js
CHANGED
|
@@ -6,10 +6,43 @@ import { dataResult, doorFailureText, errorResult, registrarFor, withSession } f
|
|
|
6
6
|
// half alone) could say how many sessions were counted and never whose they
|
|
7
7
|
// were or what they cost (BLI-10432).
|
|
8
8
|
const WINDOW_BOUND = "<n>d (1 to 365), <n>h (1 to 8760), or an ISO timestamp";
|
|
9
|
+
const noun = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
10
|
+
const PLACED = new Set(["github_login", "counted_on_other_account"]);
|
|
11
|
+
export function githubWords(github, people) {
|
|
12
|
+
const label = github.counts_label ?? "";
|
|
13
|
+
if (github.status === "unavailable") {
|
|
14
|
+
return { lines: [`GitHub counts are unavailable: ${github.reason ?? "unknown_reason"}${github.detail ? ` (${github.detail})` : ""}.`], label };
|
|
15
|
+
}
|
|
16
|
+
const sum = (field) => people.reduce((total, person) => total + (person.github?.[field] ?? 0), 0);
|
|
17
|
+
const range = github.oldest_pull_request_at && github.newest_pull_request_at
|
|
18
|
+
? `; mirrored pull requests run from ${github.oldest_pull_request_at} to ${github.newest_pull_request_at}`
|
|
19
|
+
: "; the mirror holds no pull requests";
|
|
20
|
+
const lines = [
|
|
21
|
+
`GitHub: ${noun(sum("pull_requests_opened"), "pull request")} opened, ${sum("pull_requests_merged")} merged, ${noun(sum("commits"), "commit")}, ` +
|
|
22
|
+
`from ${noun((github.repositories ?? []).length, "mirrored repository", "mirrored repositories")}${range}.`,
|
|
23
|
+
];
|
|
24
|
+
const unmatched = github.unattributed;
|
|
25
|
+
if (unmatched && unmatched.pull_requests_opened + unmatched.pull_requests_merged + unmatched.commits > 0) {
|
|
26
|
+
lines.push(`Not matched to a person: ${noun(unmatched.pull_requests_opened, "pull request")} opened, ${unmatched.pull_requests_merged} merged, ${noun(unmatched.commits, "commit")}.`);
|
|
27
|
+
}
|
|
28
|
+
const unplaced = people.filter((person) => person.github && !PLACED.has(person.github.match)).length;
|
|
29
|
+
if (unplaced > 0)
|
|
30
|
+
lines.push(`${noun(unplaced, "person cannot", "people cannot")} be matched to a GitHub login, so their GitHub counts are unknown, not zero.`);
|
|
31
|
+
return { lines, label };
|
|
32
|
+
}
|
|
33
|
+
/** The sentence appended to the text half, or the line that says a dashboard sent no GitHub block. */
|
|
34
|
+
export function githubSentence(body) {
|
|
35
|
+
const github = body.github;
|
|
36
|
+
if (github === undefined)
|
|
37
|
+
return " GitHub counts: this dashboard sent none (it predates them).";
|
|
38
|
+
const people = Array.isArray(body.people) ? body.people : [];
|
|
39
|
+
const words = githubWords(github, people);
|
|
40
|
+
return ` ${[...words.lines, words.label].filter(Boolean).join(" ")}`;
|
|
41
|
+
}
|
|
9
42
|
export function registerUsageTools(server, deps) {
|
|
10
43
|
registrarFor(server)("usage_people", {
|
|
11
44
|
title: "Read usage by person",
|
|
12
|
-
description: "Per-person Claude Code and Codex token usage, list-price equivalent (not actual spend), and extraction coverage, over a window (default the last 30 days). groupBy adds a per-person breakdown; groupBy topic or ticket lists the labels usage_sessions takes as subject or task.",
|
|
45
|
+
description: "Per-person Claude Code and Codex token usage, list-price equivalent (not actual spend), and extraction coverage, over a window (default the last 30 days). Each person also carries GitHub pull requests opened, pull requests merged and commits from the pull requests Tower mirrors from GitHub (null means Tower cannot match that person to a GitHub login: unknown, not zero); the github block says which repositories the mirror holds and what matched nobody. groupBy adds a per-person breakdown; groupBy topic or ticket lists the labels usage_sessions takes as subject or task.",
|
|
13
46
|
inputSchema: {
|
|
14
47
|
minConfidence: z.enum(["high", "medium", "low"]).optional().describe("With groupBy task_type or topic: only labels at or above this confidence."),
|
|
15
48
|
groupBy: z.enum(["repo", "task_type", "topic", "ticket"]).optional().describe("Break each person's usage down by repo, task type, topic or ticket."),
|
|
@@ -43,7 +76,11 @@ export function registerUsageTools(server, deps) {
|
|
|
43
76
|
const priceLabel = typeof response.body.api_list_price_equivalent_label === "string"
|
|
44
77
|
? ` ${response.body.api_list_price_equivalent_label}.`
|
|
45
78
|
: "";
|
|
46
|
-
|
|
79
|
+
// BLI-12561: the same GitHub sentences the page and the CLI print, so an
|
|
80
|
+
// agent reading only the text half cannot quote a pull-request count without
|
|
81
|
+
// what it covers and who it left out.
|
|
82
|
+
const github = githubSentence(response.body);
|
|
83
|
+
return dataResult(`${coverage?.sessions_extracted ?? 0} of ${coverage?.sessions_observed ?? 0} sessions extracted.${why} ${coverage?.sessions_counted_at_upload ?? 0} of ${coverage?.sessions_observed ?? 0} sessions counted at upload, ${coverage?.sessions_counted_by_server ?? 0} by server backfill, ${coverage?.sessions_counted_by_ladder ?? 0} by the ladder.${args.groupBy === "task_type" ? ` ${coverage?.sessions_labelled ?? 0} of ${coverage?.sessions_observed ?? 0} sessions labelled by topic.` : ""}${priceLabel}${github}`, response.body);
|
|
47
84
|
}));
|
|
48
85
|
registrarFor(server)("usage_sessions", {
|
|
49
86
|
title: "Read task or subject sessions",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bli-cockpit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "bli-tower: an MCP server over BLI Cockpit's agent doors: JARVIS (jarvis_*), documents (docs_*), channels (msg_*), issues (work_*), the daily page (brief_*), meeting notes (notes_*), the ops board (ops_status/slack_*), settings/team/model, Scout and the workbook, plus the legacy event-stream tools (emit_event, get_ticket_timeline, get_active_tickets).",
|
|
6
6
|
"type": "module",
|