@cldmv/gh-broker 0.10.0 → 0.10.1
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 +5 -6
- package/dist/index.mjs +42 -9
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,19 +19,18 @@ It runs as a dedicated `gh-broker` system user, holds a read-scoped and a write-
|
|
|
19
19
|
|
|
20
20
|
## ✨ What's New
|
|
21
21
|
|
|
22
|
-
### Latest: v0.10.
|
|
22
|
+
### Latest: v0.10.1 (November 2026)
|
|
23
23
|
|
|
24
|
-
- **OTP gate
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- [View full v0.10.0 Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.10.0.md)
|
|
24
|
+
- **The OTP gate says why it refused a code.** A wrong code now reads "not accepted", with the bad-code count and the lockout length, and a reused code says it's spent, so the agent can tell you what happened instead of just asking again.
|
|
25
|
+
- **`report_dependents` lists dev branches on large orgs.** The GraphQL listing pages at 25 repos and backs off when GitHub times out, instead of failing with a raw 502.
|
|
26
|
+
- [View full v0.10.1 Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.10.1.md)
|
|
28
27
|
|
|
29
28
|
### Recent Releases
|
|
30
29
|
|
|
30
|
+
- **v0.10.0** (November 2026) — OTP gate for risky write tools, one socket per agent with the `gh-broker` command, eleven new write tools and a built-in deny list ([Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.10.0.md))
|
|
31
31
|
- **v0.9.0** (November 2026) — `report_alert_status`, `report_release_changes` and `report_dependents` for the release and fleet skills, `ciVerbosity` on `list_pull_requests`, and install from npm ([Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.9.0.md))
|
|
32
32
|
- **v0.8.0** (November 2026) — `npm_scaffold` creates an npm package and its trusted publisher, `ghcr_push` pushes images without an agent-held token, and optional credentials hide their tools ([Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.8.0.md))
|
|
33
33
|
- **v0.7.0** (October 2026) — `issue_labels` adds, removes or swaps single labels without replacing the set, and `issue_write` warns that its `labels` does ([Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.7.0.md))
|
|
34
|
-
- **v0.6.1** (October 2026) — MCP SDK 1.32.0 (same-origin redirects only) and a refreshed vitest toolchain; no broker source changes ([Changelog](https://github.com/CLDMV/gh-broker/blob/master/docs/changelog/v0/v0.6.1.md))
|
|
35
34
|
|
|
36
35
|
📚 For the complete version history, see the [docs/changelog/](https://github.com/CLDMV/gh-broker/tree/master/docs/changelog/) folder.
|
|
37
36
|
|
package/dist/index.mjs
CHANGED
|
@@ -21468,14 +21468,17 @@ var SEARCH_PAGE_SIZE = 100;
|
|
|
21468
21468
|
var SEARCH_MAX_PAGES = 10;
|
|
21469
21469
|
var LOCKFILES = ["package-lock.json", "pnpm-lock.yaml"];
|
|
21470
21470
|
var NAME_PATTERN = /^(@[a-z0-9~-][a-z0-9._~-]*\/)?[a-z0-9~-][a-z0-9._~-]*$/;
|
|
21471
|
-
var
|
|
21471
|
+
var DEV_LIST_PAGE_SIZE = 25;
|
|
21472
|
+
var DEV_LIST_MIN_PAGE_SIZE = 5;
|
|
21473
|
+
var DEV_LIST_MAX_PAGES = 80;
|
|
21474
|
+
var TIMEOUT_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
21472
21475
|
var DEV_BRANCH = "dev";
|
|
21473
21476
|
var SEARCH_NOTE = "Candidates come from GitHub code search, which lags recent commits and only indexes default branches; each candidate was verified against its default-branch package.json. A repo that changed very recently can be missing.";
|
|
21474
21477
|
var DEV_NOTE = " Every `dev` branch that differs from its default branch was also read in full, so dev-trunk repos are included; a `dev` whose root package.json version is below the default branch's is treated as abandoned and skipped (see `abandonedDev`).";
|
|
21475
21478
|
var NO_DEV_NOTE = " Dev branches were not read, so a dev-trunk repo whose real manifests live on `dev` is missing.";
|
|
21476
|
-
var DEV_BRANCHES_QUERY = `query($owner: String!, $after: String) {
|
|
21479
|
+
var DEV_BRANCHES_QUERY = `query($owner: String!, $after: String, $first: Int!) {
|
|
21477
21480
|
repositoryOwner(login: $owner) {
|
|
21478
|
-
repositories(first:
|
|
21481
|
+
repositories(first: $first, after: $after, orderBy: { field: NAME, direction: ASC }) {
|
|
21479
21482
|
pageInfo { hasNextPage endCursor }
|
|
21480
21483
|
nodes {
|
|
21481
21484
|
name
|
|
@@ -21620,8 +21623,17 @@ async function listDevBranches(octokit, budget, owner2) {
|
|
|
21620
21623
|
const repos = [];
|
|
21621
21624
|
const abandoned = [];
|
|
21622
21625
|
let after = null;
|
|
21626
|
+
let first = DEV_LIST_PAGE_SIZE;
|
|
21623
21627
|
for (let page = 1; page <= DEV_LIST_MAX_PAGES; page++) {
|
|
21624
|
-
|
|
21628
|
+
let res;
|
|
21629
|
+
try {
|
|
21630
|
+
res = await reqOne(octokit, budget, "POST /graphql", { query: DEV_BRANCHES_QUERY, variables: { owner: owner2, after, first } });
|
|
21631
|
+
} catch (err) {
|
|
21632
|
+
if (!TIMEOUT_STATUSES.has(err.status)) throw err;
|
|
21633
|
+
if (first === DEV_LIST_MIN_PAGE_SIZE) return { repos, abandoned, complete: false, timedOut: true };
|
|
21634
|
+
first = Math.max(DEV_LIST_MIN_PAGE_SIZE, Math.ceil(first / 2));
|
|
21635
|
+
continue;
|
|
21636
|
+
}
|
|
21625
21637
|
const conn = res.data?.repositoryOwner?.repositories;
|
|
21626
21638
|
if (!conn) throw new Error(`report_dependents: listing dev branches failed: ${res.errors?.map((e) => e.message).join("; ") ?? `no owner "${owner2}"`}`);
|
|
21627
21639
|
for (const r of conn.nodes) {
|
|
@@ -21725,8 +21737,9 @@ async function reportDependents(octokit, input, deps = {}) {
|
|
|
21725
21737
|
dependents.push(entry);
|
|
21726
21738
|
}
|
|
21727
21739
|
if (devBranches) {
|
|
21728
|
-
const { repos, abandoned, complete } = await listDevBranches(octokit, budget, owner2);
|
|
21740
|
+
const { repos, abandoned, complete, timedOut } = await listDevBranches(octokit, budget, owner2);
|
|
21729
21741
|
abandonedDev.push(...abandoned);
|
|
21742
|
+
if (timedOut) truncatedReason ??= "github-timeout";
|
|
21730
21743
|
if (!complete) truncatedReason ??= "max-repos";
|
|
21731
21744
|
const room = Math.max(0, maxRepos - Math.min(repoNames.length, maxRepos));
|
|
21732
21745
|
if (repos.length > room) truncatedReason ??= "max-repos";
|
|
@@ -25030,11 +25043,30 @@ var OtpGate = class {
|
|
|
25030
25043
|
this.windows.push({ user, code, expiresAt, id });
|
|
25031
25044
|
return { otp: "opened", window: id, until: new Date(expiresAt).toISOString() };
|
|
25032
25045
|
}
|
|
25033
|
-
if (step !== void 0)
|
|
25034
|
-
|
|
25035
|
-
|
|
25046
|
+
if (step !== void 0) {
|
|
25047
|
+
throw new OtpRefusal(
|
|
25048
|
+
`OTP code for ${tool} is spent (it, or a newer code, was already used): ask the user for the next code from their authenticator app and pass it as "${OTP_ARG}". Nothing was run.`,
|
|
25049
|
+
{ otp: "refused" }
|
|
25050
|
+
);
|
|
25051
|
+
}
|
|
25052
|
+
throw this.rejection(tool, user, this.recordFailure(tool, state, mine, user, now));
|
|
25053
|
+
}
|
|
25054
|
+
/** The refusal for a wrong code, saying how close `user` now is to a lockout. */
|
|
25055
|
+
rejection(tool, user, after) {
|
|
25056
|
+
const lead = `OTP code for ${tool} was not accepted`;
|
|
25057
|
+
if (after.hardLock) {
|
|
25058
|
+
return new OtpRefusal(`${lead}, and that was the last lockout allowed: gh-broker is now locked for ${user}. Ask the user to run "${unlockCommand(user)}" on ${this.host}. Nothing was run.`, {
|
|
25059
|
+
otp: "refused"
|
|
25060
|
+
});
|
|
25061
|
+
}
|
|
25062
|
+
const lockout = windowText(this.settings.lockoutSeconds);
|
|
25063
|
+
if (after.failures === 0) return new OtpRefusal(`${lead}, and that was too many bad codes: OTP is locked for ${lockout}. Nothing was run.`, { otp: "refused" });
|
|
25064
|
+
return new OtpRefusal(
|
|
25065
|
+
`${lead} (${after.failures} of ${this.settings.maxFailures} bad codes before a lockout of ${lockout}): ask the user for a fresh code from their authenticator app and pass it as "${OTP_ARG}". Nothing was run.`,
|
|
25066
|
+
{ otp: "refused" }
|
|
25067
|
+
);
|
|
25036
25068
|
}
|
|
25037
|
-
/** Count one bad code for `user`, starting a short lockout (and maybe the hard lock) at the limit. */
|
|
25069
|
+
/** Count one bad code for `user`, starting a short lockout (and maybe the hard lock) at the limit. Returns the user's new counters. */
|
|
25038
25070
|
recordFailure(tool, state, mine, user, now) {
|
|
25039
25071
|
const next = { ...mine, lockouts: mine.lockouts.filter((t) => Date.parse(t) > now - DAY_MS) };
|
|
25040
25072
|
next.failures += 1;
|
|
@@ -25054,6 +25086,7 @@ var OtpGate = class {
|
|
|
25054
25086
|
this.unsaved.add(user);
|
|
25055
25087
|
console.error(`gh-broker: ${err.message}`);
|
|
25056
25088
|
}
|
|
25089
|
+
return next;
|
|
25057
25090
|
}
|
|
25058
25091
|
/**
|
|
25059
25092
|
* `state` tidied for a write: every user's lockouts pruned to the last 24 h, expired short
|