@mutmutco/cli 2.9.0 → 2.10.0
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/index.cjs +51 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5566,9 +5566,10 @@ async function buildTrainApplyContext(deps) {
|
|
|
5566
5566
|
const [owner, name] = repo.split("/");
|
|
5567
5567
|
if (!owner || !name) throw new Error(`repo must be owner/name, got ${repo}`);
|
|
5568
5568
|
const login = requireValue(clean(await deps.run("gh", ["api", "user", "--jq", ".login"])), "GitHub login");
|
|
5569
|
-
const
|
|
5570
|
-
if (
|
|
5571
|
-
|
|
5569
|
+
const verdict = await deps.trainAuthority(repo);
|
|
5570
|
+
if (!verdict.ok) throw new Error(`${commandAuthorityLabel(owner)}: train authority could not be verified (${verdict.error})`);
|
|
5571
|
+
if (!verdict.train) {
|
|
5572
|
+
throw new Error(`${commandAuthorityLabel(owner)}: @${login} is ${verdict.role} \u2014 no train authority on ${repo}`);
|
|
5572
5573
|
}
|
|
5573
5574
|
return {
|
|
5574
5575
|
repo,
|
|
@@ -6391,6 +6392,25 @@ var PROJECTS_ENVELOPE_KEY = "projects";
|
|
|
6391
6392
|
|
|
6392
6393
|
// src/registry-client.ts
|
|
6393
6394
|
var DEFAULT_TIMEOUT_MS = 8e3;
|
|
6395
|
+
async function fetchTrainAuthority(repo, deps) {
|
|
6396
|
+
if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured" };
|
|
6397
|
+
const token = await deps.token();
|
|
6398
|
+
if (!token) return { ok: false, error: "no GitHub token (gh auth login)" };
|
|
6399
|
+
const doFetch = deps.fetch ?? fetch;
|
|
6400
|
+
try {
|
|
6401
|
+
const res = await doFetch(`${deps.baseUrl.replace(/\/$/, "")}/train-authority?repo=${encodeURIComponent(repo)}`, {
|
|
6402
|
+
method: "GET",
|
|
6403
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
6404
|
+
signal: AbortSignal.timeout(deps.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
6405
|
+
});
|
|
6406
|
+
if (!res.ok) return { ok: false, error: `train-authority HTTP ${res.status}` };
|
|
6407
|
+
const body = await res.json();
|
|
6408
|
+
if (typeof body?.train !== "boolean" || !body.role) return { ok: false, error: "malformed train-authority response" };
|
|
6409
|
+
return { ok: true, authority: body };
|
|
6410
|
+
} catch (e) {
|
|
6411
|
+
return { ok: false, error: e.message };
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6394
6414
|
async function fetchProjectsList(deps) {
|
|
6395
6415
|
if (!deps.baseUrl) return null;
|
|
6396
6416
|
const token = await deps.token();
|
|
@@ -8845,7 +8865,11 @@ for (const commandName of ["rcand", "release", "hotfix"]) {
|
|
|
8845
8865
|
try {
|
|
8846
8866
|
const result = await runTrainApply(commandName, {
|
|
8847
8867
|
run: async (file, args) => (await execFileP3(file, args, { timeout: file === "gh" ? 3e4 : GIT_TIMEOUT_MS })).stdout,
|
|
8848
|
-
runSelf: async (args) => (await execFileP3(process.execPath, [process.argv[1], ...args], { timeout: 3e4 })).stdout
|
|
8868
|
+
runSelf: async (args) => (await execFileP3(process.execPath, [process.argv[1], ...args], { timeout: 3e4 })).stdout,
|
|
8869
|
+
trainAuthority: async (repo) => {
|
|
8870
|
+
const verdict = await fetchTrainAuthority(repo, registryClientDeps(await loadConfig()));
|
|
8871
|
+
return verdict.ok ? { ok: true, role: verdict.authority.role, train: verdict.authority.train } : verdict;
|
|
8872
|
+
}
|
|
8849
8873
|
});
|
|
8850
8874
|
const message = `mmi-cli ${commandName}: applied ${result.repo} ${result.stage} train at ${result.tag} [${result.deployModel}]; ${result.dispatch}`;
|
|
8851
8875
|
return printLine(o.json ? JSON.stringify(result, null, 2) : message);
|
|
@@ -9011,6 +9035,29 @@ LIVE apply to ${repo}:
|
|
|
9011
9035
|
}
|
|
9012
9036
|
});
|
|
9013
9037
|
var access = program2.command("access").description("org access audit (read-only)");
|
|
9038
|
+
access.command("role [repo]").description("D14 train authority for a repo (server-side Hub check): master | project-admin | developer").option("--json", "machine-readable output").action(async (repoArg) => {
|
|
9039
|
+
const o = { json: rawFlag("--json") };
|
|
9040
|
+
const repo = repoArg ?? await resolveRepo();
|
|
9041
|
+
if (!repo) return fail("access role: pass <owner/repo> or run inside a repo checkout");
|
|
9042
|
+
const cfg = await loadConfig();
|
|
9043
|
+
const verdict = await fetchTrainAuthority(repo, registryClientDeps(cfg));
|
|
9044
|
+
if (!verdict.ok) {
|
|
9045
|
+
if (o.json) {
|
|
9046
|
+
console.log(JSON.stringify({ repo, train: false, error: verdict.error }));
|
|
9047
|
+
process.exitCode = 1;
|
|
9048
|
+
return;
|
|
9049
|
+
}
|
|
9050
|
+
return fail(`access role: ${verdict.error}`);
|
|
9051
|
+
}
|
|
9052
|
+
const a = verdict.authority;
|
|
9053
|
+
if (o.json) {
|
|
9054
|
+
console.log(JSON.stringify(a));
|
|
9055
|
+
if (!a.train) process.exitCode = 1;
|
|
9056
|
+
return;
|
|
9057
|
+
}
|
|
9058
|
+
console.log(`${a.repo}: @${a.login} is ${a.role} \u2014 train ${a.train ? "AUTHORIZED" : "not authorized"}${a.hubTrainMasterOnly ? " (Hub train is master-only)" : ""}`);
|
|
9059
|
+
if (!a.train) process.exitCode = 1;
|
|
9060
|
+
});
|
|
9014
9061
|
access.command("audit").description("audit collaborator roles + train-branch push allowlists vs the locked state; read-only, emits gh remediation, never applies").option("--json", "machine-readable output").option("--repo <owner/repo>", "audit a single repo instead of the whole org").option("--class <class>", "repo class for --repo (deployable | content)", "deployable").action(async () => {
|
|
9015
9062
|
const o = { json: rawFlag("--json"), repo: rawValue("--repo", ""), class: rawValue("--class", "deployable") };
|
|
9016
9063
|
const deps = { gh: async (args) => execFileP3("gh", args, { timeout: 2e4 }) };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|