@mutmutco/cli 3.139.1 → 3.139.2
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/main.cjs +119 -12
- package/dist/repo-index-v4.cjs +26 -14
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -7078,9 +7078,15 @@ function gateSeedVars(cls, releaseTrack, runtime = "node") {
|
|
|
7078
7078
|
GATE_BUDGET_SHA: BLESSED_RUN_WITH_BUDGET_SHA
|
|
7079
7079
|
};
|
|
7080
7080
|
const track = releaseTrack ?? (cls === "content" ? "trunk" : "full");
|
|
7081
|
+
const windowsCompat = {
|
|
7082
|
+
// #5113: opt-in informational windows-latest proof. Default OFF — the CLI fills the rendered job
|
|
7083
|
+
// YAML (or '') at the final layering step; never hand-passed.
|
|
7084
|
+
GATE_WINDOWS_COMPAT_JOB_YAML: ""
|
|
7085
|
+
};
|
|
7081
7086
|
if (track === "trunk") {
|
|
7082
7087
|
return {
|
|
7083
7088
|
...runtimeVars,
|
|
7089
|
+
...windowsCompat,
|
|
7084
7090
|
GATE_PUSH_BRANCHES_YAML: "[main]",
|
|
7085
7091
|
GATE_FULL_RUN_BRANCH: "main",
|
|
7086
7092
|
GATE_RULESET_BRANCH_REFS_JSON: '["refs/heads/main"]'
|
|
@@ -7089,6 +7095,7 @@ function gateSeedVars(cls, releaseTrack, runtime = "node") {
|
|
|
7089
7095
|
if (track === "direct") {
|
|
7090
7096
|
return {
|
|
7091
7097
|
...runtimeVars,
|
|
7098
|
+
...windowsCompat,
|
|
7092
7099
|
GATE_PUSH_BRANCHES_YAML: "[development, main]",
|
|
7093
7100
|
GATE_FULL_RUN_BRANCH: "development",
|
|
7094
7101
|
GATE_RULESET_BRANCH_REFS_JSON: '["refs/heads/development", "refs/heads/main"]'
|
|
@@ -7096,6 +7103,7 @@ function gateSeedVars(cls, releaseTrack, runtime = "node") {
|
|
|
7096
7103
|
}
|
|
7097
7104
|
return {
|
|
7098
7105
|
...runtimeVars,
|
|
7106
|
+
...windowsCompat,
|
|
7099
7107
|
GATE_PUSH_BRANCHES_YAML: "[development, rc, main]",
|
|
7100
7108
|
GATE_FULL_RUN_BRANCH: "development",
|
|
7101
7109
|
GATE_RULESET_BRANCH_REFS_JSON: '["refs/heads/development", "refs/heads/rc", "refs/heads/main"]'
|
|
@@ -7112,8 +7120,38 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack) {
|
|
|
7112
7120
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime))) {
|
|
7113
7121
|
out[key] ??= value;
|
|
7114
7122
|
}
|
|
7123
|
+
if (out.GATE_WINDOWS_COMPAT === "true" && !out.GATE_WINDOWS_COMPAT_JOB_YAML) {
|
|
7124
|
+
out.GATE_WINDOWS_COMPAT_JOB_YAML = windowsCompatJobYaml(out);
|
|
7125
|
+
}
|
|
7115
7126
|
return out;
|
|
7116
7127
|
}
|
|
7128
|
+
function windowsCompatJobYaml(vars) {
|
|
7129
|
+
const workdir = vars.GATE_WORKDIR ?? ".";
|
|
7130
|
+
const cmd = vars.GATE_CMD ?? DEFAULT_GATE_CMD;
|
|
7131
|
+
const install = vars.GATE_INSTALL_CMD ?? "npm ci";
|
|
7132
|
+
const fullRunBranch = vars.GATE_FULL_RUN_BRANCH ?? "development";
|
|
7133
|
+
const runtime = vars.GATE_RUNTIME === "python" ? "python" : "node";
|
|
7134
|
+
const setup = runtime === "python" ? ` - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
7135
|
+
with: { python-version: '${vars.GATE_PY_VERSION ?? DEFAULT_GATE_PY_VERSION}' }` : ` - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
7136
|
+
with: { node-version: 24, cache: npm, cache-dependency-path: ${vars.GATE_CACHE_DEP_PATH ?? "package-lock.json"} }`;
|
|
7137
|
+
return ` # MMI-Hub#5113: opt-in Windows compatibility proof \u2014 informational only. Do NOT add this job to a
|
|
7138
|
+
# required-contexts ruleset without a deliberate repo decision: the required gate stays the Linux lane
|
|
7139
|
+
# (faster, cheaper, where autonomous agents run). GitHub-hosted Windows minutes cost more than Linux,
|
|
7140
|
+
# hence opt-in. defaults.run.shell=bash is Git for Windows bash on windows-latest, so the check syntax
|
|
7141
|
+
# the Linux gate runs keeps working here.
|
|
7142
|
+
windows-compat:
|
|
7143
|
+
if: \${{ github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref_name == '${fullRunBranch}' || github.ref_name == 'main')) }}
|
|
7144
|
+
runs-on: windows-latest
|
|
7145
|
+
defaults:
|
|
7146
|
+
run: { working-directory: ${workdir}, shell: bash }
|
|
7147
|
+
steps:
|
|
7148
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
7149
|
+
${setup}
|
|
7150
|
+
- run: ${install}
|
|
7151
|
+
# Fast proof only \u2014 the full suite stays on the Linux gate.
|
|
7152
|
+
- run: ${cmd}
|
|
7153
|
+
`;
|
|
7154
|
+
}
|
|
7117
7155
|
function gateConfigToVars(gate) {
|
|
7118
7156
|
const out = {};
|
|
7119
7157
|
if (!gate || typeof gate !== "object") return out;
|
|
@@ -7124,6 +7162,7 @@ function gateConfigToVars(gate) {
|
|
|
7124
7162
|
if (typeof gate.pyVersion === "string" && gate.pyVersion.trim()) out.GATE_PY_VERSION = gate.pyVersion;
|
|
7125
7163
|
const seconds = typeof gate.maxSeconds === "number" ? String(gate.maxSeconds) : gate.maxSeconds;
|
|
7126
7164
|
if (typeof seconds === "string" && /^\d+$/.test(seconds.trim()) && Number(seconds) > 0) out.GATE_MAX_SECONDS = seconds.trim();
|
|
7165
|
+
if (gate.windowsCompat === true) out.GATE_WINDOWS_COMPAT = "true";
|
|
7127
7166
|
return out;
|
|
7128
7167
|
}
|
|
7129
7168
|
function seedMatchesDeployModel(seed, deployModel) {
|
|
@@ -10707,10 +10746,10 @@ var rollout_plan_default = {
|
|
|
10707
10746
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
10708
10747
|
},
|
|
10709
10748
|
baseline: {
|
|
10710
|
-
version: "3.139.
|
|
10711
|
-
tag: "v3.139.
|
|
10712
|
-
commit: "
|
|
10713
|
-
npm: "@mutmutco/cli@3.139.
|
|
10749
|
+
version: "3.139.2",
|
|
10750
|
+
tag: "v3.139.2",
|
|
10751
|
+
commit: "1e69c6815490",
|
|
10752
|
+
npm: "@mutmutco/cli@3.139.2"
|
|
10714
10753
|
},
|
|
10715
10754
|
exitCriterion: "fleet-n-of-n",
|
|
10716
10755
|
hubOnlyShortcut: "forbidden",
|
|
@@ -10727,14 +10766,14 @@ var rollout_plan_default = {
|
|
|
10727
10766
|
repo: "mutmutco/mmi-hub",
|
|
10728
10767
|
role: "canary",
|
|
10729
10768
|
schedule: "train",
|
|
10730
|
-
v3Target: "v3.139.
|
|
10769
|
+
v3Target: "v3.139.2"
|
|
10731
10770
|
}
|
|
10732
10771
|
],
|
|
10733
10772
|
rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
|
|
10734
10773
|
rollback: {
|
|
10735
10774
|
independent: true,
|
|
10736
|
-
mechanism: "npm dist-tag latest -> 3.139.
|
|
10737
|
-
v3Target: "v3.139.
|
|
10775
|
+
mechanism: "npm dist-tag latest -> 3.139.2 and redeploy the Hub Lambda from tag v3.139.2 (1e69c6815490); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
10776
|
+
v3Target: "v3.139.2 (@mutmutco/cli@3.139.2, tag commit 1e69c6815490 \u2014 the preserved latest-v3 distribution, D6b)"
|
|
10738
10777
|
}
|
|
10739
10778
|
},
|
|
10740
10779
|
{
|
|
@@ -22696,8 +22735,9 @@ var import_node_crypto5 = require("node:crypto");
|
|
|
22696
22735
|
var import_node_child_process12 = require("node:child_process");
|
|
22697
22736
|
var import_node_fs23 = require("node:fs");
|
|
22698
22737
|
var import_node_path21 = require("node:path");
|
|
22699
|
-
|
|
22700
|
-
|
|
22738
|
+
|
|
22739
|
+
// ../infra/repo-index-path-policy.mjs
|
|
22740
|
+
var HARD_DENY = Object.freeze([
|
|
22701
22741
|
/(^|\/)\.env(\.|$)/i,
|
|
22702
22742
|
/(^|\/)\.env\./i,
|
|
22703
22743
|
/credentials/i,
|
|
@@ -22708,7 +22748,20 @@ var HARD_DENY = [
|
|
|
22708
22748
|
/(^|\/)id_rsa/i,
|
|
22709
22749
|
/(^|\/)id_ed25519/i,
|
|
22710
22750
|
/\.keystore$/i
|
|
22711
|
-
];
|
|
22751
|
+
]);
|
|
22752
|
+
function isHardDeniedRepoIndexPath(value) {
|
|
22753
|
+
return typeof value !== "string" || HARD_DENY.some((pattern) => pattern.test(value));
|
|
22754
|
+
}
|
|
22755
|
+
function isSafeRepoIndexPath(value) {
|
|
22756
|
+
if (typeof value !== "string" || !value || value.length > 1024) return false;
|
|
22757
|
+
if (value.startsWith("/") || value.includes("\\") || /[\u0000-\u001f\u007f]/u.test(value)) return false;
|
|
22758
|
+
const segments = value.split("/");
|
|
22759
|
+
if (segments.some((segment) => !segment || segment === "." || segment === "..")) return false;
|
|
22760
|
+
return !isHardDeniedRepoIndexPath(value);
|
|
22761
|
+
}
|
|
22762
|
+
|
|
22763
|
+
// src/repo-index.ts
|
|
22764
|
+
var REPO_INDEX_SCHEMA = 1;
|
|
22712
22765
|
var INDEXABLE_EXT = /* @__PURE__ */ new Set([
|
|
22713
22766
|
".ts",
|
|
22714
22767
|
".tsx",
|
|
@@ -22742,10 +22795,10 @@ function repoIndexStorePath(cwd) {
|
|
|
22742
22795
|
return repoRuntimeStatePath(cwd, "repo-index", "index.json");
|
|
22743
22796
|
}
|
|
22744
22797
|
function isHardDeniedPath(relPosix) {
|
|
22745
|
-
return
|
|
22798
|
+
return isHardDeniedRepoIndexPath(relPosix);
|
|
22746
22799
|
}
|
|
22747
22800
|
function isIndexablePath(relPosix) {
|
|
22748
|
-
if (
|
|
22801
|
+
if (!isSafeRepoIndexPath(relPosix)) return false;
|
|
22749
22802
|
if (relPosix.startsWith(".git/")) return false;
|
|
22750
22803
|
if (relPosix.includes("node_modules/")) return false;
|
|
22751
22804
|
if (relPosix.includes("dist/")) return false;
|
|
@@ -34335,6 +34388,39 @@ function planGitignore(current) {
|
|
|
34335
34388
|
const { content, changed } = upsertManagedGitignoreBlock(current);
|
|
34336
34389
|
return changed ? { ok: false, content } : { ok: true };
|
|
34337
34390
|
}
|
|
34391
|
+
function checkLineEndings(probe) {
|
|
34392
|
+
if (probe.error) {
|
|
34393
|
+
return {
|
|
34394
|
+
ok: true,
|
|
34395
|
+
verified: false,
|
|
34396
|
+
id: "line-endings",
|
|
34397
|
+
label: "line endings",
|
|
34398
|
+
detail: `could not inspect tracked shell scripts: ${probe.error}`,
|
|
34399
|
+
fix: "run `git ls-files --eol -- '*.sh'` from the repository root, then re-run `mmi-cli doctor`"
|
|
34400
|
+
};
|
|
34401
|
+
}
|
|
34402
|
+
const problems = [
|
|
34403
|
+
...probe.attributesPresent ? [] : [".gitattributes is missing"],
|
|
34404
|
+
...probe.crlfShellScripts.map((path2) => `${path2} is CRLF in the index`)
|
|
34405
|
+
];
|
|
34406
|
+
if (!problems.length) {
|
|
34407
|
+
return {
|
|
34408
|
+
ok: true,
|
|
34409
|
+
id: "line-endings",
|
|
34410
|
+
label: "line endings",
|
|
34411
|
+
detail: ".gitattributes present; tracked *.sh index entries are LF"
|
|
34412
|
+
};
|
|
34413
|
+
}
|
|
34414
|
+
return {
|
|
34415
|
+
ok: false,
|
|
34416
|
+
id: "line-endings",
|
|
34417
|
+
label: "line endings",
|
|
34418
|
+
detail: problems.join("; "),
|
|
34419
|
+
fix: "add the canonical .gitattributes if missing, then run `git add --renormalize .`",
|
|
34420
|
+
command: "git add --renormalize .",
|
|
34421
|
+
verbose: probe.crlfShellScripts.map((path2) => `i/crlf: ${path2}`)
|
|
34422
|
+
};
|
|
34423
|
+
}
|
|
34338
34424
|
async function runDoctorClean(opts, io, deps) {
|
|
34339
34425
|
const full = !opts.fast && !opts.banner && !opts.preflight;
|
|
34340
34426
|
const applyEnv = full;
|
|
@@ -34468,6 +34554,11 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
34468
34554
|
const aws = checkAwsIdentity({ isOrgRepo, probed: probeAws, callerArn });
|
|
34469
34555
|
if (aws) emitNow(aws);
|
|
34470
34556
|
}
|
|
34557
|
+
async function runLineEndingsRow() {
|
|
34558
|
+
const root = await deps.repoRoot();
|
|
34559
|
+
const state = deps.lineEndingState?.(root);
|
|
34560
|
+
if (state) emitNow(checkLineEndings(state));
|
|
34561
|
+
}
|
|
34471
34562
|
async function runGitignoreRow() {
|
|
34472
34563
|
const current = deps.readGitignore();
|
|
34473
34564
|
const gi = planGitignore(current);
|
|
@@ -34851,6 +34942,7 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
34851
34942
|
{ id: "aws-identity", when: true, run: runAwsRow },
|
|
34852
34943
|
{ id: "sessionstart-payload", when: true, run: runSessionPayloadRow },
|
|
34853
34944
|
{ id: "claude-binary", when: true, run: runClaudeBinaryRow },
|
|
34945
|
+
{ id: "line-endings", when: isOrgRepo, run: runLineEndingsRow },
|
|
34854
34946
|
{ id: "gitignore-block", when: isOrgRepo, run: runGitignoreRow }
|
|
34855
34947
|
];
|
|
34856
34948
|
const maxPasses = 3;
|
|
@@ -35143,6 +35235,20 @@ function writeGitignore(content) {
|
|
|
35143
35235
|
return false;
|
|
35144
35236
|
}
|
|
35145
35237
|
}
|
|
35238
|
+
function lineEndingState(root) {
|
|
35239
|
+
const attributesPresent = (0, import_node_fs41.existsSync)((0, import_node_path38.join)(root, ".gitattributes"));
|
|
35240
|
+
try {
|
|
35241
|
+
const output = (0, import_node_child_process18.execFileSync)("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
35242
|
+
windowsHide: true,
|
|
35243
|
+
encoding: "utf8",
|
|
35244
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
35245
|
+
});
|
|
35246
|
+
const crlfShellScripts = output.split(/\r?\n/).filter((line) => line.startsWith("i/crlf ")).map((line) => line.slice(line.indexOf(" ") + 1)).filter(Boolean);
|
|
35247
|
+
return { attributesPresent, crlfShellScripts };
|
|
35248
|
+
} catch (error) {
|
|
35249
|
+
return { attributesPresent, crlfShellScripts: [], error: error.message || "git ls-files --eol failed" };
|
|
35250
|
+
}
|
|
35251
|
+
}
|
|
35146
35252
|
async function ghInstalled() {
|
|
35147
35253
|
try {
|
|
35148
35254
|
await execFileP6("gh", ["--version"]);
|
|
@@ -35352,6 +35458,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
35352
35458
|
currentCliVersion: resolveClientVersion,
|
|
35353
35459
|
readGitignore,
|
|
35354
35460
|
writeGitignore,
|
|
35461
|
+
lineEndingState,
|
|
35355
35462
|
repoRoot,
|
|
35356
35463
|
executeScratchGc: (root, o) => executeScratchGc(root, { apply: o.apply }),
|
|
35357
35464
|
// #2759: same fetch+ff-only sync SessionStart runs, wired here too so an on-demand `mmi-cli doctor`
|
package/dist/repo-index-v4.cjs
CHANGED
|
@@ -85,6 +85,30 @@ function defaultIsIgnored(root, relPaths, exec = import_node_child_process.execF
|
|
|
85
85
|
var import_node_child_process2 = require("node:child_process");
|
|
86
86
|
var import_node_path2 = require("node:path");
|
|
87
87
|
|
|
88
|
+
// ../infra/repo-index-path-policy.mjs
|
|
89
|
+
var HARD_DENY = Object.freeze([
|
|
90
|
+
/(^|\/)\.env(\.|$)/i,
|
|
91
|
+
/(^|\/)\.env\./i,
|
|
92
|
+
/credentials/i,
|
|
93
|
+
/secrets?\.json$/i,
|
|
94
|
+
/\.pem$/i,
|
|
95
|
+
/\.p12$/i,
|
|
96
|
+
/\.key$/i,
|
|
97
|
+
/(^|\/)id_rsa/i,
|
|
98
|
+
/(^|\/)id_ed25519/i,
|
|
99
|
+
/\.keystore$/i
|
|
100
|
+
]);
|
|
101
|
+
function isHardDeniedRepoIndexPath(value) {
|
|
102
|
+
return typeof value !== "string" || HARD_DENY.some((pattern) => pattern.test(value));
|
|
103
|
+
}
|
|
104
|
+
function isSafeRepoIndexPath(value) {
|
|
105
|
+
if (typeof value !== "string" || !value || value.length > 1024) return false;
|
|
106
|
+
if (value.startsWith("/") || value.includes("\\") || /[\u0000-\u001f\u007f]/u.test(value)) return false;
|
|
107
|
+
const segments = value.split("/");
|
|
108
|
+
if (segments.some((segment) => !segment || segment === "." || segment === "..")) return false;
|
|
109
|
+
return !isHardDeniedRepoIndexPath(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
88
112
|
// src/repo-runtime-state.ts
|
|
89
113
|
var import_node_crypto = require("node:crypto");
|
|
90
114
|
var import_node_fs = require("node:fs");
|
|
@@ -120,18 +144,6 @@ function repoRuntimeStatePath(cwd, ...parts) {
|
|
|
120
144
|
}
|
|
121
145
|
|
|
122
146
|
// src/repo-index.ts
|
|
123
|
-
var HARD_DENY = [
|
|
124
|
-
/(^|\/)\.env(\.|$)/i,
|
|
125
|
-
/(^|\/)\.env\./i,
|
|
126
|
-
/credentials/i,
|
|
127
|
-
/secrets?\.json$/i,
|
|
128
|
-
/\.pem$/i,
|
|
129
|
-
/\.p12$/i,
|
|
130
|
-
/\.key$/i,
|
|
131
|
-
/(^|\/)id_rsa/i,
|
|
132
|
-
/(^|\/)id_ed25519/i,
|
|
133
|
-
/\.keystore$/i
|
|
134
|
-
];
|
|
135
147
|
var INDEXABLE_EXT = /* @__PURE__ */ new Set([
|
|
136
148
|
".ts",
|
|
137
149
|
".tsx",
|
|
@@ -155,10 +167,10 @@ var INDEXABLE_EXT = /* @__PURE__ */ new Set([
|
|
|
155
167
|
".html"
|
|
156
168
|
]);
|
|
157
169
|
function isHardDeniedPath(relPosix) {
|
|
158
|
-
return
|
|
170
|
+
return isHardDeniedRepoIndexPath(relPosix);
|
|
159
171
|
}
|
|
160
172
|
function isIndexablePath(relPosix) {
|
|
161
|
-
if (
|
|
173
|
+
if (!isSafeRepoIndexPath(relPosix)) return false;
|
|
162
174
|
if (relPosix.startsWith(".git/")) return false;
|
|
163
175
|
if (relPosix.includes("node_modules/")) return false;
|
|
164
176
|
if (relPosix.includes("dist/")) return false;
|
package/package.json
CHANGED