@mutmutco/codex-plugin 4.6.24 → 4.6.26
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/.codex-plugin/plugin.json +1 -1
- package/cli/dist/main.cjs +963 -568
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/skills/bootstrap/seeds/suite-guide.template.md +5 -1
- package/skills/org-guide/SKILL.md +1 -1
- package/skills/org-guide/references/design.md +13 -2
- package/skills/org-guide/references/repository-rules.md +4 -0
package/cli/dist/main.cjs
CHANGED
|
@@ -157,19 +157,19 @@ function hardExit(code) {
|
|
|
157
157
|
process.exit(code);
|
|
158
158
|
}
|
|
159
159
|
function flushStream(stream) {
|
|
160
|
-
return new Promise((
|
|
160
|
+
return new Promise((resolve8) => {
|
|
161
161
|
try {
|
|
162
|
-
stream.write("", () =>
|
|
162
|
+
stream.write("", () => resolve8());
|
|
163
163
|
} catch {
|
|
164
|
-
|
|
164
|
+
resolve8();
|
|
165
165
|
}
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
async function flushStdio(timeoutMs = STDIO_FLUSH_TIMEOUT_MS) {
|
|
169
169
|
await Promise.race([
|
|
170
170
|
Promise.all([flushStream(process.stdout), flushStream(process.stderr)]),
|
|
171
|
-
new Promise((
|
|
172
|
-
setTimeout(
|
|
171
|
+
new Promise((resolve8) => {
|
|
172
|
+
setTimeout(resolve8, timeoutMs).unref?.();
|
|
173
173
|
})
|
|
174
174
|
]);
|
|
175
175
|
}
|
|
@@ -177,7 +177,7 @@ async function cleanExit(code) {
|
|
|
177
177
|
process.exitCode = code;
|
|
178
178
|
await closeHttpPool();
|
|
179
179
|
await flushStdio();
|
|
180
|
-
await new Promise((
|
|
180
|
+
await new Promise((resolve8) => setImmediate(resolve8));
|
|
181
181
|
return void 0;
|
|
182
182
|
}
|
|
183
183
|
async function finishCliRun(watchdogMs = CLI_EXIT_WATCHDOG_MS) {
|
|
@@ -454,9 +454,9 @@ var init_gh_read_route = __esm({
|
|
|
454
454
|
function commandPath() {
|
|
455
455
|
if (cachedCommand !== void 0) return cachedCommand;
|
|
456
456
|
const words = [];
|
|
457
|
-
for (const
|
|
458
|
-
if (
|
|
459
|
-
words.push(
|
|
457
|
+
for (const arg2 of process.argv.slice(2)) {
|
|
458
|
+
if (arg2.startsWith("-") || words.length === 3) break;
|
|
459
|
+
words.push(arg2);
|
|
460
460
|
}
|
|
461
461
|
return cachedCommand = words.join(" ") || "(none)";
|
|
462
462
|
}
|
|
@@ -851,7 +851,7 @@ function rateLimitedReceipt(opts) {
|
|
|
851
851
|
};
|
|
852
852
|
}
|
|
853
853
|
async function runWithRateLimitBackoff(operation, opts) {
|
|
854
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
854
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
855
855
|
const now = opts.now ?? Date.now;
|
|
856
856
|
const log = opts.log ?? ((message2) => console.warn(message2));
|
|
857
857
|
const capMs = opts.capMs ?? RATE_LIMIT_WAIT_CAP_MS;
|
|
@@ -941,7 +941,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
941
941
|
const attempts = opts.attempts ?? 3;
|
|
942
942
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
943
943
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
944
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
944
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
945
945
|
let lastErr;
|
|
946
946
|
for (let i = 0; i < attempts; i++) {
|
|
947
947
|
const isLast = i === attempts - 1;
|
|
@@ -1346,8 +1346,8 @@ async function readStdin(opts = {}) {
|
|
|
1346
1346
|
})().catch(() => {
|
|
1347
1347
|
});
|
|
1348
1348
|
let timer;
|
|
1349
|
-
const timeout = new Promise((
|
|
1350
|
-
timer = setTimeout(
|
|
1349
|
+
const timeout = new Promise((resolve8) => {
|
|
1350
|
+
timer = setTimeout(resolve8, timeoutMs);
|
|
1351
1351
|
});
|
|
1352
1352
|
try {
|
|
1353
1353
|
await Promise.race([drain, timeout]);
|
|
@@ -1444,12 +1444,12 @@ function killProcessTree(pid) {
|
|
|
1444
1444
|
function execFileHard(file, args, options) {
|
|
1445
1445
|
const { timeout, step, ...rest } = options;
|
|
1446
1446
|
const started = Date.now();
|
|
1447
|
-
return new Promise((
|
|
1447
|
+
return new Promise((resolve8, reject2) => {
|
|
1448
1448
|
const child2 = (0, import_node_child_process.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
|
|
1449
1449
|
clearTimeout(timer);
|
|
1450
1450
|
if (expired) return;
|
|
1451
1451
|
if (error) reject2(error);
|
|
1452
|
-
else
|
|
1452
|
+
else resolve8({ stdout: String(stdout), stderr: String(stderr) });
|
|
1453
1453
|
});
|
|
1454
1454
|
let expired = false;
|
|
1455
1455
|
const timer = setTimeout(() => {
|
|
@@ -1585,12 +1585,12 @@ function installBrokenPipeGuard(stream) {
|
|
|
1585
1585
|
});
|
|
1586
1586
|
}
|
|
1587
1587
|
function rawFlag(flag) {
|
|
1588
|
-
return process.argv.some((
|
|
1588
|
+
return process.argv.some((arg2) => arg2 === flag || arg2.startsWith(`${flag}=`));
|
|
1589
1589
|
}
|
|
1590
1590
|
function rawValue(flag, fallback) {
|
|
1591
1591
|
const index = process.argv.indexOf(flag);
|
|
1592
1592
|
if (index >= 0 && process.argv[index + 1]) return process.argv[index + 1];
|
|
1593
|
-
const inline = process.argv.find((
|
|
1593
|
+
const inline = process.argv.find((arg2) => arg2.startsWith(`${flag}=`));
|
|
1594
1594
|
return inline ? inline.slice(flag.length + 1) : fallback;
|
|
1595
1595
|
}
|
|
1596
1596
|
function printLine(value) {
|
|
@@ -2276,19 +2276,19 @@ var require_toJS = __commonJS({
|
|
|
2276
2276
|
"node_modules/yaml/dist/nodes/toJS.js"(exports2) {
|
|
2277
2277
|
"use strict";
|
|
2278
2278
|
var identity = require_identity();
|
|
2279
|
-
function toJS(value,
|
|
2279
|
+
function toJS(value, arg2, ctx) {
|
|
2280
2280
|
if (Array.isArray(value))
|
|
2281
2281
|
return value.map((v, i) => toJS(v, String(i), ctx));
|
|
2282
2282
|
if (value && typeof value.toJSON === "function") {
|
|
2283
2283
|
if (!ctx || !identity.hasAnchor(value))
|
|
2284
|
-
return value.toJSON(
|
|
2284
|
+
return value.toJSON(arg2, ctx);
|
|
2285
2285
|
const data = { aliasCount: 0, count: 1, res: void 0 };
|
|
2286
2286
|
ctx.anchors.set(value, data);
|
|
2287
2287
|
ctx.onCreate = (res2) => {
|
|
2288
2288
|
data.res = res2;
|
|
2289
2289
|
delete ctx.onCreate;
|
|
2290
2290
|
};
|
|
2291
|
-
const res = value.toJSON(
|
|
2291
|
+
const res = value.toJSON(arg2, ctx);
|
|
2292
2292
|
if (ctx.onCreate)
|
|
2293
2293
|
ctx.onCreate(res);
|
|
2294
2294
|
return res;
|
|
@@ -2471,8 +2471,8 @@ var require_Scalar = __commonJS({
|
|
|
2471
2471
|
super(identity.SCALAR);
|
|
2472
2472
|
this.value = value;
|
|
2473
2473
|
}
|
|
2474
|
-
toJSON(
|
|
2475
|
-
return ctx?.keep ? this.value : toJS.toJS(this.value,
|
|
2474
|
+
toJSON(arg2, ctx) {
|
|
2475
|
+
return ctx?.keep ? this.value : toJS.toJS(this.value, arg2, ctx);
|
|
2476
2476
|
}
|
|
2477
2477
|
toString() {
|
|
2478
2478
|
return String(this.value);
|
|
@@ -10271,16 +10271,16 @@ var Argument = class {
|
|
|
10271
10271
|
*/
|
|
10272
10272
|
choices(values) {
|
|
10273
10273
|
this.argChoices = values.slice();
|
|
10274
|
-
this.parseArg = (
|
|
10275
|
-
if (!this.argChoices.includes(
|
|
10274
|
+
this.parseArg = (arg2, previous) => {
|
|
10275
|
+
if (!this.argChoices.includes(arg2)) {
|
|
10276
10276
|
throw new InvalidArgumentError(
|
|
10277
10277
|
`Allowed choices are ${this.argChoices.join(", ")}.`
|
|
10278
10278
|
);
|
|
10279
10279
|
}
|
|
10280
10280
|
if (this.variadic) {
|
|
10281
|
-
return this._collectValue(
|
|
10281
|
+
return this._collectValue(arg2, previous);
|
|
10282
10282
|
}
|
|
10283
|
-
return
|
|
10283
|
+
return arg2;
|
|
10284
10284
|
};
|
|
10285
10285
|
return this;
|
|
10286
10286
|
}
|
|
@@ -10303,9 +10303,9 @@ var Argument = class {
|
|
|
10303
10303
|
return this;
|
|
10304
10304
|
}
|
|
10305
10305
|
};
|
|
10306
|
-
function humanReadableArgName(
|
|
10307
|
-
const nameOutput =
|
|
10308
|
-
return
|
|
10306
|
+
function humanReadableArgName(arg2) {
|
|
10307
|
+
const nameOutput = arg2.name() + (arg2.variadic === true ? "..." : "");
|
|
10308
|
+
return arg2.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
|
|
10309
10309
|
}
|
|
10310
10310
|
|
|
10311
10311
|
// node_modules/commander/lib/command.js
|
|
@@ -10442,7 +10442,7 @@ var Help = class {
|
|
|
10442
10442
|
* @returns {string}
|
|
10443
10443
|
*/
|
|
10444
10444
|
subcommandTerm(cmd) {
|
|
10445
|
-
const args = cmd.registeredArguments.map((
|
|
10445
|
+
const args = cmd.registeredArguments.map((arg2) => humanReadableArgName(arg2)).join(" ");
|
|
10446
10446
|
return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + // simplistic check for non-help option
|
|
10447
10447
|
(args ? " " + args : "");
|
|
10448
10448
|
}
|
|
@@ -10964,8 +10964,8 @@ var Option = class {
|
|
|
10964
10964
|
* @param {*} arg
|
|
10965
10965
|
* @return {Option}
|
|
10966
10966
|
*/
|
|
10967
|
-
preset(
|
|
10968
|
-
this.presetArg =
|
|
10967
|
+
preset(arg2) {
|
|
10968
|
+
this.presetArg = arg2;
|
|
10969
10969
|
return this;
|
|
10970
10970
|
}
|
|
10971
10971
|
/**
|
|
@@ -11065,16 +11065,16 @@ var Option = class {
|
|
|
11065
11065
|
*/
|
|
11066
11066
|
choices(values) {
|
|
11067
11067
|
this.argChoices = values.slice();
|
|
11068
|
-
this.parseArg = (
|
|
11069
|
-
if (!this.argChoices.includes(
|
|
11068
|
+
this.parseArg = (arg2, previous) => {
|
|
11069
|
+
if (!this.argChoices.includes(arg2)) {
|
|
11070
11070
|
throw new InvalidArgumentError(
|
|
11071
11071
|
`Allowed choices are ${this.argChoices.join(", ")}.`
|
|
11072
11072
|
);
|
|
11073
11073
|
}
|
|
11074
11074
|
if (this.variadic) {
|
|
11075
|
-
return this._collectValue(
|
|
11075
|
+
return this._collectValue(arg2, previous);
|
|
11076
11076
|
}
|
|
11077
|
-
return
|
|
11077
|
+
return arg2;
|
|
11078
11078
|
};
|
|
11079
11079
|
return this;
|
|
11080
11080
|
}
|
|
@@ -11118,8 +11118,8 @@ var Option = class {
|
|
|
11118
11118
|
* @return {boolean}
|
|
11119
11119
|
* @package
|
|
11120
11120
|
*/
|
|
11121
|
-
is(
|
|
11122
|
-
return this.short ===
|
|
11121
|
+
is(arg2) {
|
|
11122
|
+
return this.short === arg2 || this.long === arg2;
|
|
11123
11123
|
}
|
|
11124
11124
|
/**
|
|
11125
11125
|
* Return whether a boolean option.
|
|
@@ -12455,9 +12455,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
12455
12455
|
* @private
|
|
12456
12456
|
*/
|
|
12457
12457
|
_checkNumberOfArguments() {
|
|
12458
|
-
this.registeredArguments.forEach((
|
|
12459
|
-
if (
|
|
12460
|
-
this.missingArgument(
|
|
12458
|
+
this.registeredArguments.forEach((arg2, i) => {
|
|
12459
|
+
if (arg2.required && this.args[i] == null) {
|
|
12460
|
+
this.missingArgument(arg2.name());
|
|
12461
12461
|
}
|
|
12462
12462
|
});
|
|
12463
12463
|
if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
|
|
@@ -12668,8 +12668,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
12668
12668
|
* @return {Option}
|
|
12669
12669
|
* @package
|
|
12670
12670
|
*/
|
|
12671
|
-
_findOption(
|
|
12672
|
-
return this.options.find((option) => option.is(
|
|
12671
|
+
_findOption(arg2) {
|
|
12672
|
+
return this.options.find((option) => option.is(arg2));
|
|
12673
12673
|
}
|
|
12674
12674
|
/**
|
|
12675
12675
|
* Display an error message if a mandatory option does not have a value.
|
|
@@ -12743,11 +12743,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
12743
12743
|
const operands = [];
|
|
12744
12744
|
const unknown = [];
|
|
12745
12745
|
let dest = operands;
|
|
12746
|
-
function maybeOption(
|
|
12747
|
-
return
|
|
12746
|
+
function maybeOption(arg2) {
|
|
12747
|
+
return arg2.length > 1 && arg2[0] === "-";
|
|
12748
12748
|
}
|
|
12749
|
-
const negativeNumberArg = (
|
|
12750
|
-
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(
|
|
12749
|
+
const negativeNumberArg = (arg2) => {
|
|
12750
|
+
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg2)) return false;
|
|
12751
12751
|
return !this._getCommandAndAncestors().some(
|
|
12752
12752
|
(cmd) => cmd.options.map((opt) => opt.short).some((short3) => /^-\d$/.test(short3))
|
|
12753
12753
|
);
|
|
@@ -12756,20 +12756,20 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
12756
12756
|
let activeGroup = null;
|
|
12757
12757
|
let i = 0;
|
|
12758
12758
|
while (i < args.length || activeGroup) {
|
|
12759
|
-
const
|
|
12759
|
+
const arg2 = activeGroup ?? args[i++];
|
|
12760
12760
|
activeGroup = null;
|
|
12761
|
-
if (
|
|
12762
|
-
if (dest === unknown) dest.push(
|
|
12761
|
+
if (arg2 === "--") {
|
|
12762
|
+
if (dest === unknown) dest.push(arg2);
|
|
12763
12763
|
dest.push(...args.slice(i));
|
|
12764
12764
|
break;
|
|
12765
12765
|
}
|
|
12766
|
-
if (activeVariadicOption && (!maybeOption(
|
|
12767
|
-
this.emit(`option:${activeVariadicOption.name()}`,
|
|
12766
|
+
if (activeVariadicOption && (!maybeOption(arg2) || negativeNumberArg(arg2))) {
|
|
12767
|
+
this.emit(`option:${activeVariadicOption.name()}`, arg2);
|
|
12768
12768
|
continue;
|
|
12769
12769
|
}
|
|
12770
12770
|
activeVariadicOption = null;
|
|
12771
|
-
if (maybeOption(
|
|
12772
|
-
const option = this._findOption(
|
|
12771
|
+
if (maybeOption(arg2)) {
|
|
12772
|
+
const option = this._findOption(arg2);
|
|
12773
12773
|
if (option) {
|
|
12774
12774
|
if (option.required) {
|
|
12775
12775
|
const value = args[i++];
|
|
@@ -12788,47 +12788,47 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
12788
12788
|
continue;
|
|
12789
12789
|
}
|
|
12790
12790
|
}
|
|
12791
|
-
if (
|
|
12792
|
-
const option = this._findOption(`-${
|
|
12791
|
+
if (arg2.length > 2 && arg2[0] === "-" && arg2[1] !== "-") {
|
|
12792
|
+
const option = this._findOption(`-${arg2[1]}`);
|
|
12793
12793
|
if (option) {
|
|
12794
12794
|
if (option.required || option.optional && this._combineFlagAndOptionalValue) {
|
|
12795
|
-
this.emit(`option:${option.name()}`,
|
|
12795
|
+
this.emit(`option:${option.name()}`, arg2.slice(2));
|
|
12796
12796
|
} else {
|
|
12797
12797
|
this.emit(`option:${option.name()}`);
|
|
12798
|
-
activeGroup = `-${
|
|
12798
|
+
activeGroup = `-${arg2.slice(2)}`;
|
|
12799
12799
|
}
|
|
12800
12800
|
continue;
|
|
12801
12801
|
}
|
|
12802
12802
|
}
|
|
12803
|
-
if (/^--[^=]+=/.test(
|
|
12804
|
-
const index =
|
|
12805
|
-
const option = this._findOption(
|
|
12803
|
+
if (/^--[^=]+=/.test(arg2)) {
|
|
12804
|
+
const index = arg2.indexOf("=");
|
|
12805
|
+
const option = this._findOption(arg2.slice(0, index));
|
|
12806
12806
|
if (option && (option.required || option.optional)) {
|
|
12807
|
-
this.emit(`option:${option.name()}`,
|
|
12807
|
+
this.emit(`option:${option.name()}`, arg2.slice(index + 1));
|
|
12808
12808
|
continue;
|
|
12809
12809
|
}
|
|
12810
12810
|
}
|
|
12811
|
-
if (dest === operands && maybeOption(
|
|
12811
|
+
if (dest === operands && maybeOption(arg2) && !(this.commands.length === 0 && negativeNumberArg(arg2))) {
|
|
12812
12812
|
dest = unknown;
|
|
12813
12813
|
}
|
|
12814
12814
|
if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
|
|
12815
|
-
if (this._findCommand(
|
|
12816
|
-
operands.push(
|
|
12815
|
+
if (this._findCommand(arg2)) {
|
|
12816
|
+
operands.push(arg2);
|
|
12817
12817
|
unknown.push(...args.slice(i));
|
|
12818
12818
|
break;
|
|
12819
|
-
} else if (this._getHelpCommand() &&
|
|
12820
|
-
operands.push(
|
|
12819
|
+
} else if (this._getHelpCommand() && arg2 === this._getHelpCommand().name()) {
|
|
12820
|
+
operands.push(arg2, ...args.slice(i));
|
|
12821
12821
|
break;
|
|
12822
12822
|
} else if (this._defaultCommandName) {
|
|
12823
|
-
unknown.push(
|
|
12823
|
+
unknown.push(arg2, ...args.slice(i));
|
|
12824
12824
|
break;
|
|
12825
12825
|
}
|
|
12826
12826
|
}
|
|
12827
12827
|
if (this._passThroughOptions) {
|
|
12828
|
-
dest.push(
|
|
12828
|
+
dest.push(arg2, ...args.slice(i));
|
|
12829
12829
|
break;
|
|
12830
12830
|
}
|
|
12831
|
-
dest.push(
|
|
12831
|
+
dest.push(arg2);
|
|
12832
12832
|
}
|
|
12833
12833
|
return { operands, unknown };
|
|
12834
12834
|
}
|
|
@@ -13156,8 +13156,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
13156
13156
|
usage(str2) {
|
|
13157
13157
|
if (str2 === void 0) {
|
|
13158
13158
|
if (this._usage) return this._usage;
|
|
13159
|
-
const args = this.registeredArguments.map((
|
|
13160
|
-
return humanReadableArgName(
|
|
13159
|
+
const args = this.registeredArguments.map((arg2) => {
|
|
13160
|
+
return humanReadableArgName(arg2);
|
|
13161
13161
|
});
|
|
13162
13162
|
return [].concat(
|
|
13163
13163
|
this.options.length || this._helpOption !== null ? "[options]" : [],
|
|
@@ -13479,7 +13479,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
13479
13479
|
*/
|
|
13480
13480
|
_outputHelpIfRequested(args) {
|
|
13481
13481
|
const helpOption = this._getHelpOption();
|
|
13482
|
-
const helpRequested = helpOption && args.find((
|
|
13482
|
+
const helpRequested = helpOption && args.find((arg2) => helpOption.is(arg2));
|
|
13483
13483
|
if (helpRequested) {
|
|
13484
13484
|
this.outputHelp();
|
|
13485
13485
|
this._exit(0, "commander.helpDisplayed", "(outputHelp)");
|
|
@@ -13487,24 +13487,24 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
13487
13487
|
}
|
|
13488
13488
|
};
|
|
13489
13489
|
function incrementNodeInspectorPort(args) {
|
|
13490
|
-
return args.map((
|
|
13491
|
-
if (!
|
|
13492
|
-
return
|
|
13490
|
+
return args.map((arg2) => {
|
|
13491
|
+
if (!arg2.startsWith("--inspect")) {
|
|
13492
|
+
return arg2;
|
|
13493
13493
|
}
|
|
13494
13494
|
let debugOption;
|
|
13495
13495
|
let debugHost = "127.0.0.1";
|
|
13496
13496
|
let debugPort = "9229";
|
|
13497
13497
|
let match;
|
|
13498
|
-
if ((match =
|
|
13498
|
+
if ((match = arg2.match(/^(--inspect(-brk)?)$/)) !== null) {
|
|
13499
13499
|
debugOption = match[1];
|
|
13500
|
-
} else if ((match =
|
|
13500
|
+
} else if ((match = arg2.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
|
|
13501
13501
|
debugOption = match[1];
|
|
13502
13502
|
if (/^\d+$/.test(match[3])) {
|
|
13503
13503
|
debugPort = match[3];
|
|
13504
13504
|
} else {
|
|
13505
13505
|
debugHost = match[3];
|
|
13506
13506
|
}
|
|
13507
|
-
} else if ((match =
|
|
13507
|
+
} else if ((match = arg2.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
|
|
13508
13508
|
debugOption = match[1];
|
|
13509
13509
|
debugHost = match[3];
|
|
13510
13510
|
debugPort = match[4];
|
|
@@ -13512,7 +13512,7 @@ function incrementNodeInspectorPort(args) {
|
|
|
13512
13512
|
if (debugOption && debugPort !== "0") {
|
|
13513
13513
|
return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
|
|
13514
13514
|
}
|
|
13515
|
-
return
|
|
13515
|
+
return arg2;
|
|
13516
13516
|
});
|
|
13517
13517
|
}
|
|
13518
13518
|
function useColor() {
|
|
@@ -13527,7 +13527,7 @@ function useColor() {
|
|
|
13527
13527
|
var program = new Command();
|
|
13528
13528
|
|
|
13529
13529
|
// src/command-composition.ts
|
|
13530
|
-
var
|
|
13530
|
+
var import_node_fs67 = require("node:fs");
|
|
13531
13531
|
init_clean_exit();
|
|
13532
13532
|
init_cli_shared();
|
|
13533
13533
|
|
|
@@ -15195,7 +15195,7 @@ function resolveVerbAliasShim(argv) {
|
|
|
15195
15195
|
|
|
15196
15196
|
// src/command-composition.ts
|
|
15197
15197
|
var import_node_os28 = require("node:os");
|
|
15198
|
-
var
|
|
15198
|
+
var import_node_path64 = require("node:path");
|
|
15199
15199
|
|
|
15200
15200
|
// src/board-read.ts
|
|
15201
15201
|
var import_node_fs18 = require("node:fs");
|
|
@@ -15495,7 +15495,7 @@ function setStatusWriteTokenProvider(provider) {
|
|
|
15495
15495
|
statusWriteTokenProvider = provider;
|
|
15496
15496
|
}
|
|
15497
15497
|
async function fleetStatusWriteEnv(args) {
|
|
15498
|
-
if (args[0] !== "api" || args[1] !== "-X" || args[2] !== "POST" || !/^repos\/[^/]+\/[^/]+\/statuses\/[^/]+$/.test(args[3] ?? "") || !args.some((
|
|
15498
|
+
if (args[0] !== "api" || args[1] !== "-X" || args[2] !== "POST" || !/^repos\/[^/]+\/[^/]+\/statuses\/[^/]+$/.test(args[3] ?? "") || !args.some((arg2, i) => arg2 === "context=fleet-lockstep" && args[i - 1] === "-f")) return void 0;
|
|
15499
15499
|
if (!statusWriteTokenProvider) throw new AppActorError("app actor: status-write provider not installed");
|
|
15500
15500
|
try {
|
|
15501
15501
|
const { token } = await statusWriteTokenProvider();
|
|
@@ -16482,7 +16482,7 @@ async function readRestPrSnapshotWithRetry(prNumber, repo, gh2 = defaultGhApi, o
|
|
|
16482
16482
|
const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
|
|
16483
16483
|
const untilMs = options?.retryTransientUntilMs;
|
|
16484
16484
|
const delayMs = options?.delayMs ?? (untilMs !== void 0 ? PR_CHECKS_POLL_MS : PR_SNAPSHOT_READ_DELAY_MS);
|
|
16485
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
16485
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
16486
16486
|
const now = options?.now ?? (() => Date.now());
|
|
16487
16487
|
let lastError = "no attempt completed";
|
|
16488
16488
|
let attempt = 0;
|
|
@@ -16512,7 +16512,7 @@ var PR_LAND_PINNED_HEAD_READ_BUDGET_MS = 6e4;
|
|
|
16512
16512
|
var PR_LAND_PINNED_HEAD_READ_404_ATTEMPTS = 3;
|
|
16513
16513
|
async function readPrLandPinnedHeadSha(prNumber, repo, gh2 = defaultGhApi, options) {
|
|
16514
16514
|
const delayMs = options?.delayMs ?? PR_SNAPSHOT_READ_DELAY_MS;
|
|
16515
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
16515
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
16516
16516
|
let notFound = 0;
|
|
16517
16517
|
for (; ; ) {
|
|
16518
16518
|
const read = await readRestPrSnapshotWithRetry(prNumber, repo, gh2, {
|
|
@@ -18327,9 +18327,9 @@ function extractWorkflowCrons(yamlText) {
|
|
|
18327
18327
|
function workflowEntry(repo, workflowPath, yamlText) {
|
|
18328
18328
|
const crons = extractWorkflowCrons(yamlText);
|
|
18329
18329
|
if (!crons.length) return null;
|
|
18330
|
-
const
|
|
18330
|
+
const basename6 = workflowPath.split("/").pop() ?? workflowPath;
|
|
18331
18331
|
return {
|
|
18332
|
-
name: `${repo}/${
|
|
18332
|
+
name: `${repo}/${basename6.replace(/\.ya?ml$/, "")}`,
|
|
18333
18333
|
cadence: crons.join(" + "),
|
|
18334
18334
|
executor: "github-actions",
|
|
18335
18335
|
llm: llmFromHeader(yamlText),
|
|
@@ -18663,8 +18663,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
18663
18663
|
const crons = extractWorkflowCrons(yamlText);
|
|
18664
18664
|
const header = parseScheduleHeader(yamlText);
|
|
18665
18665
|
if (!crons.length && !header.schedule) return null;
|
|
18666
|
-
const
|
|
18667
|
-
const expectedId = `${repo}/${
|
|
18666
|
+
const basename6 = workflowPath.split("/").pop() ?? workflowPath;
|
|
18667
|
+
const expectedId = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
|
|
18668
18668
|
if (isHarbourUnmanaged(yamlText)) return null;
|
|
18669
18669
|
const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
|
|
18670
18670
|
if (missing.length) {
|
|
@@ -20851,7 +20851,7 @@ async function createPrViaRestFallback(args, swappedArgs, deps, knownPools) {
|
|
|
20851
20851
|
}
|
|
20852
20852
|
async function ghCreate(args, deps = {}) {
|
|
20853
20853
|
const exec3 = deps.exec ?? execFileP;
|
|
20854
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
20854
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
20855
20855
|
const now = deps.now ?? Date.now;
|
|
20856
20856
|
const read = deps.readFile ?? import_promises.readFile;
|
|
20857
20857
|
const restCreatePr = deps.restCreatePr ?? ((input) => defaultRestCreatePr(exec3, input));
|
|
@@ -21660,8 +21660,8 @@ function resolveUseEnvNames(keys, name) {
|
|
|
21660
21660
|
}
|
|
21661
21661
|
return { ok: true, entries };
|
|
21662
21662
|
}
|
|
21663
|
-
function referencesDollarEnv(
|
|
21664
|
-
return
|
|
21663
|
+
function referencesDollarEnv(arg2, name) {
|
|
21664
|
+
return arg2.includes(`\${${name}}`) || new RegExp(`\\$${name}(?![A-Za-z0-9_])`).test(arg2);
|
|
21665
21665
|
}
|
|
21666
21666
|
function isShellScriptCommand(command) {
|
|
21667
21667
|
const exe = (command[0] ?? "").split(/[/\\]/).pop()?.toLowerCase().replace(/\.exe$/, "") ?? "";
|
|
@@ -21669,9 +21669,9 @@ function isShellScriptCommand(command) {
|
|
|
21669
21669
|
}
|
|
21670
21670
|
function secretsUseDollarHint(command, envNames) {
|
|
21671
21671
|
if (isShellScriptCommand(command)) return null;
|
|
21672
|
-
for (const
|
|
21672
|
+
for (const arg2 of command) {
|
|
21673
21673
|
for (const name of envNames) {
|
|
21674
|
-
if (!referencesDollarEnv(
|
|
21674
|
+
if (!referencesDollarEnv(arg2, name)) continue;
|
|
21675
21675
|
return `secrets use: ${command[0]} receives the literal text $${name} \u2014 the value is injected into the CHILD's env, and ${command[0]} does not expand variables in its arguments. Wrap the consumer in a shell so expansion happens inside: bash -c 'your-command "$${name}"'`;
|
|
21676
21676
|
}
|
|
21677
21677
|
}
|
|
@@ -21681,8 +21681,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
|
21681
21681
|
async function drainHttpPoolBeforeSpawn() {
|
|
21682
21682
|
const drained = await Promise.race([
|
|
21683
21683
|
closeHttpPool().then(() => true),
|
|
21684
|
-
new Promise((
|
|
21685
|
-
setTimeout(() =>
|
|
21684
|
+
new Promise((resolve8) => {
|
|
21685
|
+
setTimeout(() => resolve8(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
|
|
21686
21686
|
})
|
|
21687
21687
|
]);
|
|
21688
21688
|
if (!drained) destroyHttpPool();
|
|
@@ -24653,7 +24653,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
|
|
|
24653
24653
|
await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
|
|
24654
24654
|
return cliPriorityToFieldName(priority);
|
|
24655
24655
|
}
|
|
24656
|
-
var defaultRetrySleep = (ms) => new Promise((
|
|
24656
|
+
var defaultRetrySleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
24657
24657
|
async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
|
|
24658
24658
|
const attempts = Math.max(1, opts.attempts ?? 5);
|
|
24659
24659
|
const delayMs = opts.delayMs ?? 300;
|
|
@@ -25243,6 +25243,9 @@ var CMD_RE = /`mmi-cli ((?:[a-z][a-z-]*)(?: [a-z][a-z-]*){0,3})/g;
|
|
|
25243
25243
|
var RETIRED_RE = /\b(retired|historical|removed|deleted|gone|no longer|superseded|legacy)\b/i;
|
|
25244
25244
|
var ROOT_DOCS = ["README.md", "architecture.md"];
|
|
25245
25245
|
var SKIP_WALK = ["docs/decisions/"];
|
|
25246
|
+
function formatDocRefFinding(finding) {
|
|
25247
|
+
return `${finding.doc}:${finding.line} [${finding.kind}] ${finding.detail}${finding.suggest?.length ? ` (did you mean ${finding.suggest[0]}?)` : ""}`;
|
|
25248
|
+
}
|
|
25246
25249
|
function refFirstSegment(ref) {
|
|
25247
25250
|
return ref.replace(/^(\.\/)+/, "").split("/")[0];
|
|
25248
25251
|
}
|
|
@@ -25663,21 +25666,14 @@ function defaultIsIgnored(root, relPaths, exec3 = import_node_child_process6.exe
|
|
|
25663
25666
|
throw error;
|
|
25664
25667
|
}
|
|
25665
25668
|
}
|
|
25666
|
-
function
|
|
25667
|
-
if (firstSegments.length === 0) return /* @__PURE__ */ new Set();
|
|
25669
|
+
function defaultTrackedFiles(root, firstSegments, exec3 = import_node_child_process6.execFileSync) {
|
|
25668
25670
|
try {
|
|
25669
25671
|
const out = exec3("git", ["ls-files", "-z", "--", ...firstSegments.map((s) => `:(literal)${s}`)], {
|
|
25670
25672
|
cwd: root,
|
|
25671
25673
|
encoding: "utf8",
|
|
25672
25674
|
maxBuffer: CHECK_IGNORE_MAX_BUFFER
|
|
25673
25675
|
});
|
|
25674
|
-
|
|
25675
|
-
for (const path2 of out.split("\0")) {
|
|
25676
|
-
if (path2) for (const segment of firstSegments) {
|
|
25677
|
-
if (path2 === segment || path2.startsWith(`${segment}/`)) tracked.add(segment);
|
|
25678
|
-
}
|
|
25679
|
-
}
|
|
25680
|
-
return tracked;
|
|
25676
|
+
return out.split("\0").filter(Boolean);
|
|
25681
25677
|
} catch (error) {
|
|
25682
25678
|
if (error?.status === 128) return null;
|
|
25683
25679
|
if (error?.code === "ENOENT") return null;
|
|
@@ -25721,7 +25717,12 @@ function runDocRefs(root, deps = {}) {
|
|
|
25721
25717
|
const exists = deps.exists ?? import_node_fs19.existsSync;
|
|
25722
25718
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
25723
25719
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
25724
|
-
|
|
25720
|
+
let inventory;
|
|
25721
|
+
const trackedFiles = () => inventory === void 0 ? inventory = deps.trackedFiles ? deps.trackedFiles() : deps.trackedFirstSegments ? null : defaultTrackedFiles(root, []) : inventory;
|
|
25722
|
+
const trackedFirstSegments = deps.trackedFirstSegments ?? ((segs) => {
|
|
25723
|
+
const files = trackedFiles();
|
|
25724
|
+
return files == null ? null : new Set(segs.filter((seg) => files.some((path2) => path2 === seg || path2.startsWith(`${seg}/`))));
|
|
25725
|
+
});
|
|
25725
25726
|
const commandPaths = deps.commandPaths ?? null;
|
|
25726
25727
|
const walked = listDocs(root);
|
|
25727
25728
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
@@ -25761,6 +25762,18 @@ ${sourceMessages}`;
|
|
|
25761
25762
|
(override ? warnings : findings).push(...stale);
|
|
25762
25763
|
if (!/^Seed-Runbook-Affected:[ \t]*\S.*$/m.test(allMessages)) findings.push(...seedStale);
|
|
25763
25764
|
}
|
|
25765
|
+
for (const finding of findings) {
|
|
25766
|
+
if (finding.kind !== "missing-path") continue;
|
|
25767
|
+
const directory = import_node_path16.posix.dirname(import_node_path16.posix.normalize(finding.detail)).split("/");
|
|
25768
|
+
const distance = (path2) => {
|
|
25769
|
+
const parts = import_node_path16.posix.dirname(path2).split("/");
|
|
25770
|
+
let common = 0;
|
|
25771
|
+
while (common < directory.length && common < parts.length && directory[common] === parts[common]) common += 1;
|
|
25772
|
+
return directory.length + parts.length - 2 * common;
|
|
25773
|
+
};
|
|
25774
|
+
const suggest = [...new Set(trackedFiles() ?? [])].filter((path2) => import_node_path16.posix.basename(path2) === import_node_path16.posix.basename(finding.detail) && exists((0, import_node_path16.join)(root, path2))).sort((a, b) => distance(a) - distance(b) || a.localeCompare(b)).slice(0, 3);
|
|
25775
|
+
if (suggest.length) finding.suggest = suggest;
|
|
25776
|
+
}
|
|
25764
25777
|
return {
|
|
25765
25778
|
ok: findings.length === 0,
|
|
25766
25779
|
findings,
|
|
@@ -28166,10 +28179,10 @@ var rollout_plan_default = {
|
|
|
28166
28179
|
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)."
|
|
28167
28180
|
},
|
|
28168
28181
|
baseline: {
|
|
28169
|
-
version: "4.6.
|
|
28170
|
-
tag: "v4.6.
|
|
28171
|
-
commit: "
|
|
28172
|
-
npm: "@mutmutco/cli@4.6.
|
|
28182
|
+
version: "4.6.26",
|
|
28183
|
+
tag: "v4.6.26",
|
|
28184
|
+
commit: "bab0b3157d41",
|
|
28185
|
+
npm: "@mutmutco/cli@4.6.26"
|
|
28173
28186
|
},
|
|
28174
28187
|
exitCriterion: "fleet-n-of-n",
|
|
28175
28188
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28186,14 +28199,14 @@ var rollout_plan_default = {
|
|
|
28186
28199
|
repo: "mutmutco/mmi-hub",
|
|
28187
28200
|
role: "canary",
|
|
28188
28201
|
schedule: "train",
|
|
28189
|
-
v3Target: "v4.6.
|
|
28202
|
+
v3Target: "v4.6.26"
|
|
28190
28203
|
}
|
|
28191
28204
|
],
|
|
28192
28205
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
28193
28206
|
rollback: {
|
|
28194
28207
|
independent: true,
|
|
28195
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28196
|
-
v3Target: "v4.6.
|
|
28208
|
+
mechanism: "npm dist-tag latest -> 4.6.26 and redeploy the Hub Lambda from tag v4.6.26 (bab0b3157d41); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28209
|
+
v3Target: "v4.6.26 (@mutmutco/cli@4.6.26, tag commit bab0b3157d41 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28197
28210
|
}
|
|
28198
28211
|
},
|
|
28199
28212
|
{
|
|
@@ -30288,7 +30301,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
30288
30301
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
30289
30302
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
30290
30303
|
}
|
|
30291
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
30304
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve8, extraTolerated = []) {
|
|
30292
30305
|
try {
|
|
30293
30306
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
30294
30307
|
return;
|
|
@@ -30302,7 +30315,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, ex
|
|
|
30302
30315
|
unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
|
|
30303
30316
|
);
|
|
30304
30317
|
}
|
|
30305
|
-
await deps.run("git", ["checkout", `--${
|
|
30318
|
+
await deps.run("git", ["checkout", `--${resolve8}`, "--", ...unmerged]);
|
|
30306
30319
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
30307
30320
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
30308
30321
|
}
|
|
@@ -30500,7 +30513,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
|
30500
30513
|
var CORRELATE_PAGE_LIMIT = 50;
|
|
30501
30514
|
var RUN_CONFIRM_ATTEMPTS = 3;
|
|
30502
30515
|
var RUN_CONFIRM_DELAY_MS = 1e3;
|
|
30503
|
-
var defaultSleep = (ms) => new Promise((
|
|
30516
|
+
var defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
30504
30517
|
function resolveSleep(deps) {
|
|
30505
30518
|
return deps.sleep ?? defaultSleep;
|
|
30506
30519
|
}
|
|
@@ -32391,7 +32404,7 @@ async function discoverShaWorkflowRuns(deps, repo, headSha, seenRunIds) {
|
|
|
32391
32404
|
}
|
|
32392
32405
|
return extra;
|
|
32393
32406
|
}
|
|
32394
|
-
async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince, autoRunHeadSha, dispatchFailure = "throw", publishDir) {
|
|
32407
|
+
async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince, autoRunHeadSha, dispatchFailure = "throw", publishDir, rollback = false) {
|
|
32395
32408
|
if (validSuiteCutContext(ctx.repo, deps.suiteCut) && stage !== "dev") {
|
|
32396
32409
|
return { note: "suite bundle-only: live backend not deployed", deployStatus: "success" };
|
|
32397
32410
|
}
|
|
@@ -32399,7 +32412,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
32399
32412
|
deps.warn?.("Release: Starting deployment");
|
|
32400
32413
|
const since = (deps.now ?? Date.now)();
|
|
32401
32414
|
try {
|
|
32402
|
-
await dispatchTenantDeployWithRetry(deps, { repo: ctx.repo, slug: ctx.slug, ref, stage });
|
|
32415
|
+
await dispatchTenantDeployWithRetry(deps, { repo: ctx.repo, slug: ctx.slug, ref, stage, ...rollback ? { rollback: true } : {} });
|
|
32403
32416
|
} catch (e) {
|
|
32404
32417
|
if (dispatchFailure === "throw") throw e;
|
|
32405
32418
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -32484,19 +32497,19 @@ async function buildEnvironments(deps, ctx, model, deployStatus, retirement) {
|
|
|
32484
32497
|
const mainDomains = domains?.main;
|
|
32485
32498
|
const rcDomains = domains?.rc;
|
|
32486
32499
|
const healthStatus = deployStatus === "success" ? "success" : deployStatus === "failure" ? "failure" : "pending";
|
|
32487
|
-
const
|
|
32500
|
+
const main2 = { healthStatus };
|
|
32488
32501
|
if (mainDomains?.length) {
|
|
32489
|
-
|
|
32490
|
-
|
|
32502
|
+
main2.domains = mainDomains;
|
|
32503
|
+
main2.healthUrl = `https://${mainDomains[0]}/`;
|
|
32491
32504
|
}
|
|
32492
32505
|
const rc = { retirement: retirement.status };
|
|
32493
32506
|
if (retirement.category) rc.retirementCategory = retirement.category;
|
|
32494
32507
|
if (rcDomains?.length) rc.domains = rcDomains;
|
|
32495
32508
|
if (domainsRead === "failed") {
|
|
32496
|
-
|
|
32509
|
+
main2.domainsRead = "failed";
|
|
32497
32510
|
rc.domainsRead = "failed";
|
|
32498
32511
|
}
|
|
32499
|
-
return { main, rc };
|
|
32512
|
+
return { main: main2, rc };
|
|
32500
32513
|
}
|
|
32501
32514
|
var RETIRE_MAX_ATTEMPTS = 3;
|
|
32502
32515
|
var RETIRE_BACKOFF_MS = 1500;
|
|
@@ -32577,14 +32590,27 @@ async function runTenantRedeploy(deps, options) {
|
|
|
32577
32590
|
const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), repo);
|
|
32578
32591
|
const deployModel = requireDeployModel(meta, repo);
|
|
32579
32592
|
const singleTrackMain = stage === "main" && meta.releaseTrack === "single";
|
|
32580
|
-
if (
|
|
32581
|
-
|
|
32593
|
+
if (options.rollback && !options.ref) throw new Error("--rollback requires --ref");
|
|
32594
|
+
const deployed = await deps.readDeployedSha?.(ctx.slug, stage).catch(() => void 0);
|
|
32595
|
+
let ref = options.ref ?? deployed ?? (stage === "dev" || singleTrackMain ? "development" : stage);
|
|
32596
|
+
if (!deployed) {
|
|
32597
|
+
deps.warn?.(`Serving DEPLOY#${stage} stamp unreadable or absent; falling back to ${ref}.`);
|
|
32598
|
+
} else if (options.ref) {
|
|
32599
|
+
if (!deps.compareDeployRef) throw new Error("Deploy ancestry comparison unavailable");
|
|
32600
|
+
const relation = await deps.compareDeployRef(repo, deployed, options.ref);
|
|
32601
|
+
if (!["ahead", "identical", "behind", "diverged"].includes(relation)) throw new Error("Invalid deploy ancestry comparison");
|
|
32602
|
+
if (relation === "behind") {
|
|
32603
|
+
if (options.rollback) deps.warn?.(`rolling back ${stage} from ${deployed} to ${options.ref}`);
|
|
32604
|
+
else {
|
|
32605
|
+
deps.warn?.(`${options.ref} is older than the serving release ${deployed}; redeploying the serving SHA instead (use --rollback for a deliberate rollback).`);
|
|
32606
|
+
ref = deployed;
|
|
32607
|
+
}
|
|
32608
|
+
}
|
|
32582
32609
|
}
|
|
32583
|
-
const ref = options.ref ?? (stage === "dev" || singleTrackMain ? "development" : stage);
|
|
32584
32610
|
if (!isCentralDispatchModel(deployModel)) {
|
|
32585
32611
|
throw new Error(`${repo} is ${deployModel}, not a centrally-dispatched model (tenant-container/solo-container/static-cdn) \u2014 there is no central tenant-deploy run to retry (its deploy fires from its own workflow)`);
|
|
32586
32612
|
}
|
|
32587
|
-
const d = await dispatchDeploy(deps, ctx, stage, ref, deployModel, watch);
|
|
32613
|
+
const d = await dispatchDeploy(deps, ctx, stage, ref, deployModel, watch, void 0, void 0, "throw", void 0, options.rollback);
|
|
32588
32614
|
return { ...ctx, command: "tenant-redeploy", stage, ref, deployModel, dispatch: d.note, runId: d.runId, runUrl: d.runUrl, workflowRuns: d.workflowRuns, deployStatus: d.deployStatus };
|
|
32589
32615
|
}
|
|
32590
32616
|
async function runTenantReconcile(deps, options) {
|
|
@@ -33804,7 +33830,7 @@ function lockTokenPid(token) {
|
|
|
33804
33830
|
const m = /^(\d+)-/.exec(token);
|
|
33805
33831
|
return m ? Number(m[1]) : void 0;
|
|
33806
33832
|
}
|
|
33807
|
-
var sleep = (ms) => new Promise((
|
|
33833
|
+
var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
33808
33834
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
33809
33835
|
var FileLockBusyError = class extends Error {
|
|
33810
33836
|
lockPath;
|
|
@@ -34694,7 +34720,7 @@ async function defaultWatchRun(deps, runId, repo) {
|
|
|
34694
34720
|
watchSaidSuccess = false;
|
|
34695
34721
|
}
|
|
34696
34722
|
for (let attempt = 0; attempt < RUN_CONFIRM_ATTEMPTS2; attempt++) {
|
|
34697
|
-
if (attempt > 0) await new Promise((
|
|
34723
|
+
if (attempt > 0) await new Promise((resolve8) => setTimeout(resolve8, RUN_CONFIRM_DELAY_MS2));
|
|
34698
34724
|
try {
|
|
34699
34725
|
const parsed = JSON.parse(await deps.run("gh", ["run", "view", String(runId), "--repo", repo, "--json", "status,conclusion"]));
|
|
34700
34726
|
if (parsed.status === "completed") return parsed.conclusion === "success" ? "success" : "failure";
|
|
@@ -35758,11 +35784,11 @@ var defaultDeps = {
|
|
|
35758
35784
|
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
35759
35785
|
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
35760
35786
|
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
35761
|
-
reexec: (argv) => new Promise((
|
|
35787
|
+
reexec: (argv) => new Promise((resolve8) => {
|
|
35762
35788
|
const args = [...argv];
|
|
35763
35789
|
const child2 = isWin2 ? (0, import_node_child_process8.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process8.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
|
|
35764
|
-
child2.on("exit", (code) =>
|
|
35765
|
-
child2.on("error", () =>
|
|
35790
|
+
child2.on("exit", (code) => resolve8(code ?? 1));
|
|
35791
|
+
child2.on("error", () => resolve8(1));
|
|
35766
35792
|
}),
|
|
35767
35793
|
// stderr only — stdout may be carrying --json output.
|
|
35768
35794
|
warn: (line) => console.error(line)
|
|
@@ -35919,7 +35945,7 @@ var defaultDeps2 = {
|
|
|
35919
35945
|
}
|
|
35920
35946
|
},
|
|
35921
35947
|
registryWhoami: probeRegistryWhoami,
|
|
35922
|
-
sleep: (ms) => new Promise((
|
|
35948
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
35923
35949
|
env: process.env
|
|
35924
35950
|
};
|
|
35925
35951
|
function message(e) {
|
|
@@ -41535,9 +41561,9 @@ function readCommonMistakes(cmd) {
|
|
|
41535
41561
|
const notes = cmd[COMMON_MISTAKES];
|
|
41536
41562
|
return Array.isArray(notes) && notes.length ? notes : void 0;
|
|
41537
41563
|
}
|
|
41538
|
-
function buildArgument(
|
|
41539
|
-
const out = { name:
|
|
41540
|
-
if (
|
|
41564
|
+
function buildArgument(arg2) {
|
|
41565
|
+
const out = { name: arg2.name(), required: arg2.required, variadic: arg2.variadic };
|
|
41566
|
+
if (arg2.description) out.description = arg2.description;
|
|
41541
41567
|
return out;
|
|
41542
41568
|
}
|
|
41543
41569
|
function buildOption(opt) {
|
|
@@ -41767,9 +41793,9 @@ function buildDoorsCatalog(tree) {
|
|
|
41767
41793
|
};
|
|
41768
41794
|
});
|
|
41769
41795
|
}
|
|
41770
|
-
function argSignature(
|
|
41771
|
-
const inner =
|
|
41772
|
-
return
|
|
41796
|
+
function argSignature(arg2) {
|
|
41797
|
+
const inner = arg2.variadic ? `${arg2.name}...` : arg2.name;
|
|
41798
|
+
return arg2.required ? `<${inner}>` : `[${inner}]`;
|
|
41773
41799
|
}
|
|
41774
41800
|
function formatManifestHuman(manifest, options = {}) {
|
|
41775
41801
|
const root = options.all ? manifest.tree : manifest.primary_tree;
|
|
@@ -42190,7 +42216,7 @@ async function updatePrHeadFromBase(input) {
|
|
|
42190
42216
|
} catch (e) {
|
|
42191
42217
|
throw new Error(`GitHub could not update ${input.head} from ${input.base} (${e.message.trim()}) \u2014 ${remedy}`);
|
|
42192
42218
|
}
|
|
42193
|
-
const sleep2 = input.sleep ?? ((ms) => new Promise((
|
|
42219
|
+
const sleep2 = input.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42194
42220
|
for (let attempt = 0; attempt < PR_HEAD_UPDATE_API_READ_RETRIES; attempt++) {
|
|
42195
42221
|
const to = await readHead2();
|
|
42196
42222
|
if (to && to !== from) return { from, to, base: input.base, via: "update-branch-api" };
|
|
@@ -42213,7 +42239,7 @@ var PR_LAND_MERGE_RETRY_DELAY_MS = 3e4;
|
|
|
42213
42239
|
async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
42214
42240
|
const now = deps.now ?? (() => Date.now());
|
|
42215
42241
|
const deadline = now() + PR_LAND_ENQUEUE_TIMEOUT_MS;
|
|
42216
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42242
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42217
42243
|
const first = await deps.mergeAuto(prNumber, repo);
|
|
42218
42244
|
let result = first;
|
|
42219
42245
|
let transientRetried = false;
|
|
@@ -42252,7 +42278,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
|
|
|
42252
42278
|
async function confirmAutoMergeEnqueued(deps, options) {
|
|
42253
42279
|
const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
|
|
42254
42280
|
const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
|
|
42255
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42281
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42256
42282
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
42257
42283
|
if (await deps.readMerged().catch(() => false)) return "merged";
|
|
42258
42284
|
const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
|
|
@@ -42269,7 +42295,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
|
|
|
42269
42295
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
42270
42296
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
42271
42297
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
42272
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
42298
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42273
42299
|
let lastError = "empty state";
|
|
42274
42300
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
42275
42301
|
try {
|
|
@@ -42421,7 +42447,7 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42421
42447
|
if (read.isInMergeQueue || read.state === "MERGED") confirmed = read;
|
|
42422
42448
|
}
|
|
42423
42449
|
if (!confirmed) {
|
|
42424
|
-
await (deps.sleep ?? ((ms) => new Promise((
|
|
42450
|
+
await (deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(deps.pollDelayMs?.(0) ?? PR_LAND_POLL_MS);
|
|
42425
42451
|
}
|
|
42426
42452
|
return pollPrMergeQueue(prNumber, options, deps, base, confirmed, deadline);
|
|
42427
42453
|
}
|
|
@@ -42460,7 +42486,7 @@ function queueMerged(base, deps) {
|
|
|
42460
42486
|
}
|
|
42461
42487
|
async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
|
|
42462
42488
|
const now = deps.now ?? (() => Date.now());
|
|
42463
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42489
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42464
42490
|
const timeoutMs = options.queueTimeoutMs ?? PR_LAND_QUEUE_TIMEOUT_MS;
|
|
42465
42491
|
const deadline = deadlineMs ?? now() + timeoutMs;
|
|
42466
42492
|
let attempt = 0;
|
|
@@ -43401,9 +43427,9 @@ function changedFilesSince(base, cwd, head) {
|
|
|
43401
43427
|
const raw = git2(["diff", "--name-status", "-M", base, ...head ? [head] : [], "--"], cwd).trim();
|
|
43402
43428
|
const changed = raw ? raw.split("\n").map((line) => {
|
|
43403
43429
|
const parts = line.split(" ");
|
|
43404
|
-
const
|
|
43405
|
-
const row = { status: parts[0][0], path:
|
|
43406
|
-
if (parts.length > 2) row.from =
|
|
43430
|
+
const norm3 = (p) => p.replace(/\\/g, "/");
|
|
43431
|
+
const row = { status: parts[0][0], path: norm3(parts[parts.length - 1]) };
|
|
43432
|
+
if (parts.length > 2) row.from = norm3(parts[1]);
|
|
43407
43433
|
return row;
|
|
43408
43434
|
}) : [];
|
|
43409
43435
|
if (head) return changed;
|
|
@@ -43681,7 +43707,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
43681
43707
|
const rejected = (reason) => admissionRejected(reason, repo, pr);
|
|
43682
43708
|
const reconcile = async (reason, recordEntry = false, resumingSaved = false) => {
|
|
43683
43709
|
const maxReads = options.enqueueOnly ? 3 : 1;
|
|
43684
|
-
const sleep3 = options.sleep ?? ((ms) => new Promise((
|
|
43710
|
+
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
43685
43711
|
for (let readAttempt = 0; readAttempt < maxReads; readAttempt++) {
|
|
43686
43712
|
if (readAttempt) await sleep3(readAttempt * 1e3);
|
|
43687
43713
|
try {
|
|
@@ -43836,7 +43862,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
43836
43862
|
}
|
|
43837
43863
|
receipt = { ...receipt, state: "pending", uuid: details.uuid };
|
|
43838
43864
|
}
|
|
43839
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((
|
|
43865
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
43840
43866
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
43841
43867
|
if (attempt) await sleep2(attempt * 1e3);
|
|
43842
43868
|
try {
|
|
@@ -44306,7 +44332,7 @@ function observeQueuedAdmission(repo, pr, snapshot, receiptDirectory) {
|
|
|
44306
44332
|
var SETTLE_POLLS = 6;
|
|
44307
44333
|
var SETTLE_MS = 5e3;
|
|
44308
44334
|
async function settled(repo, id, deps) {
|
|
44309
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
44335
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
44310
44336
|
let status = "unknown";
|
|
44311
44337
|
for (let poll = 0; poll < SETTLE_POLLS; poll++) {
|
|
44312
44338
|
await sleep2(SETTLE_MS);
|
|
@@ -46051,8 +46077,14 @@ function registerBoardCommands(program3) {
|
|
|
46051
46077
|
process.exitCode = e.exitCode;
|
|
46052
46078
|
return;
|
|
46053
46079
|
}
|
|
46054
|
-
|
|
46055
|
-
|
|
46080
|
+
const message2 = `board read failed: ${withDiscoverMissDetail(e.message)}`;
|
|
46081
|
+
if (o.json) {
|
|
46082
|
+
if (e && typeof e === "object" && "quota" in e && e.quota) console.log(JSON.stringify(e.quota));
|
|
46083
|
+
else console.log(formatErrorEnvelope(message2, {
|
|
46084
|
+
code: /\b(401|403)\b|auth|credential|token/i.test(message2) ? ERROR_CODES.ERR_NO_AUTH : /no MMI project detected|\b404\b|not found/i.test(message2) ? ERROR_CODES.ERR_NOT_FOUND : ERROR_CODES.ERR_PARTIAL
|
|
46085
|
+
}));
|
|
46086
|
+
}
|
|
46087
|
+
return failGraceful(message2);
|
|
46056
46088
|
}
|
|
46057
46089
|
}
|
|
46058
46090
|
function formatClaimHolder(holder) {
|
|
@@ -46346,9 +46378,9 @@ function registerBoardCommands(program3) {
|
|
|
46346
46378
|
}
|
|
46347
46379
|
|
|
46348
46380
|
// src/bootstrap-commands.ts
|
|
46349
|
-
var
|
|
46381
|
+
var import_node_fs40 = require("node:fs");
|
|
46350
46382
|
var import_node_os21 = require("node:os");
|
|
46351
|
-
var
|
|
46383
|
+
var import_node_path37 = require("node:path");
|
|
46352
46384
|
init_cli_shared();
|
|
46353
46385
|
init_clean_exit();
|
|
46354
46386
|
|
|
@@ -46661,10 +46693,10 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
46661
46693
|
}
|
|
46662
46694
|
|
|
46663
46695
|
// src/box-commands.ts
|
|
46664
|
-
var
|
|
46696
|
+
var import_node_fs39 = require("node:fs");
|
|
46665
46697
|
var import_node_child_process12 = require("node:child_process");
|
|
46666
46698
|
var import_node_os20 = require("node:os");
|
|
46667
|
-
var
|
|
46699
|
+
var import_node_path36 = require("node:path");
|
|
46668
46700
|
|
|
46669
46701
|
// src/box-control.ts
|
|
46670
46702
|
var import_node_crypto16 = require("node:crypto");
|
|
@@ -47411,16 +47443,16 @@ function parseTenantTasksVar(raw) {
|
|
|
47411
47443
|
const task = rawTask;
|
|
47412
47444
|
if (Object.keys(task).some((key) => !["service", "command", "stages", "timeoutSeconds", "artifact", "output", "secretOutput"].includes(key))) throw new Error(`org project set: tenantTasks.${name} has an unknown field`);
|
|
47413
47445
|
if (typeof task.service !== "string" || !serviceRe.test(task.service)) throw new Error(`org project set: tenantTasks.${name}.service has an unsafe shape`);
|
|
47414
|
-
if (!Array.isArray(task.command) || task.command.length < 1 || task.command.length > 32 || task.command.some((
|
|
47446
|
+
if (!Array.isArray(task.command) || task.command.length < 1 || task.command.length > 32 || task.command.some((arg2) => typeof arg2 !== "string" || arg2.length < 1 || arg2.length > 256 || /[\u0000\r\n]/.test(arg2))) throw new Error(`org project set: tenantTasks.${name}.command must be 1-32 bounded argv strings`);
|
|
47415
47447
|
if (!Array.isArray(task.stages) || task.stages.length < 1 || task.stages.length > 3 || task.stages.some((stage) => typeof stage !== "string" || !validStages.has(stage)) || new Set(task.stages).size !== task.stages.length) throw new Error(`org project set: tenantTasks.${name}.stages must be unique dev/rc/main values`);
|
|
47416
47448
|
const maxTimeout = task.secretOutput !== void 0 ? 3600 : 10800;
|
|
47417
47449
|
if (task.timeoutSeconds !== void 0 && (!Number.isInteger(task.timeoutSeconds) || task.timeoutSeconds < 1 || task.timeoutSeconds > maxTimeout)) throw new Error(`org project set: tenantTasks.${name}.timeoutSeconds must be an integer 1..${maxTimeout}`);
|
|
47418
47450
|
const artifact = task.artifact ?? "none";
|
|
47419
47451
|
if (artifact !== "none" && artifact !== "required") throw new Error(`org project set: tenantTasks.${name}.artifact must be none or required`);
|
|
47420
|
-
const carriesPlaceholder = task.command.some((
|
|
47452
|
+
const carriesPlaceholder = task.command.some((arg2) => arg2.includes("{artifact}"));
|
|
47421
47453
|
if (artifact === "required" !== carriesPlaceholder) throw new Error(`org project set: tenantTasks.${name} must use {artifact} exactly when artifact is required`);
|
|
47422
47454
|
if (task.output !== void 0 && task.output !== "file") throw new Error(`org project set: tenantTasks.${name}.output must be file, use {output}, and cannot combine with secretOutput`);
|
|
47423
|
-
const outputPlaceholder = task.command.some((
|
|
47455
|
+
const outputPlaceholder = task.command.some((arg2) => arg2.includes("{output}"));
|
|
47424
47456
|
if (task.output === "file" !== outputPlaceholder || task.output !== void 0 && task.secretOutput !== void 0) throw new Error(`org project set: tenantTasks.${name}.output must be file, use {output}, and cannot combine with secretOutput`);
|
|
47425
47457
|
out[name] = {
|
|
47426
47458
|
service: task.service,
|
|
@@ -48554,6 +48586,276 @@ ${formatSshRecipe(box, remoteCommand)}
|
|
|
48554
48586
|
`;
|
|
48555
48587
|
}
|
|
48556
48588
|
|
|
48589
|
+
// ../infra/hetzner/cloud-firewall.mjs
|
|
48590
|
+
var import_node_fs38 = require("node:fs");
|
|
48591
|
+
var import_node_url = require("node:url");
|
|
48592
|
+
var import_node_path35 = require("node:path");
|
|
48593
|
+
var import_meta = {};
|
|
48594
|
+
var HZ = "https://api.hetzner.cloud/v1";
|
|
48595
|
+
var APPLY = process.env.FIREWALL_APPLY === "1";
|
|
48596
|
+
var ANY = ["0.0.0.0/0", "::/0"];
|
|
48597
|
+
function arg(name) {
|
|
48598
|
+
const i = process.argv.indexOf(`--${name}`);
|
|
48599
|
+
return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : void 0;
|
|
48600
|
+
}
|
|
48601
|
+
async function hetzner(token, path2, init = {}) {
|
|
48602
|
+
if (!token) throw new Error("no Hetzner Cloud API token (inject it with mmi-cli vault secrets use ... --name HZ_TOKEN)");
|
|
48603
|
+
const res = await fetch(`${HZ}${path2}`, {
|
|
48604
|
+
...init,
|
|
48605
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
48606
|
+
signal: AbortSignal.timeout(3e4)
|
|
48607
|
+
});
|
|
48608
|
+
const body = await res.json().catch(() => ({}));
|
|
48609
|
+
if (!res.ok) throw new Error(`Hetzner ${init.method ?? "GET"} ${path2.split("?")[0]} failed: ${res.status} ${body?.error?.code ?? ""} ${body?.error?.message ?? ""}`);
|
|
48610
|
+
return body;
|
|
48611
|
+
}
|
|
48612
|
+
async function hz(path2, init = {}) {
|
|
48613
|
+
return hetzner(process.env.HZ_TOKEN, path2, init);
|
|
48614
|
+
}
|
|
48615
|
+
function buildRules(cloudflare, extras = []) {
|
|
48616
|
+
const cf = [...cloudflare.ipv4, ...cloudflare.ipv6];
|
|
48617
|
+
if (!cloudflare.ipv4.length || !cloudflare.ipv6.length) throw new Error("refusing an empty Cloudflare range list (it would block every site)");
|
|
48618
|
+
const rules = [
|
|
48619
|
+
{ direction: "in", protocol: "tcp", port: "22", source_ips: ANY, description: "SSH (deploy, reconcile, runner)" },
|
|
48620
|
+
{ direction: "in", protocol: "icmp", source_ips: ANY, description: "ICMP" },
|
|
48621
|
+
{ direction: "in", protocol: "tcp", port: "80", source_ips: cf, description: "HTTP from Cloudflare only" },
|
|
48622
|
+
{ direction: "in", protocol: "tcp", port: "443", source_ips: cf, description: "HTTPS from Cloudflare only" }
|
|
48623
|
+
];
|
|
48624
|
+
for (const e of extras) {
|
|
48625
|
+
if (!["tcp", "udp"].includes(e.protocol) || !/^\d{1,5}(-\d{1,5})?$/.test(String(e.port)) || !e.description) {
|
|
48626
|
+
throw new Error(`invalid firewall extra ${JSON.stringify(e)}: needs protocol tcp|udp, port N or N-M, description`);
|
|
48627
|
+
}
|
|
48628
|
+
rules.push({ direction: "in", protocol: e.protocol, port: String(e.port), source_ips: ANY, description: e.description.slice(0, 255) });
|
|
48629
|
+
}
|
|
48630
|
+
return rules;
|
|
48631
|
+
}
|
|
48632
|
+
async function cloudflareRanges() {
|
|
48633
|
+
const res = await fetch("https://api.cloudflare.com/client/v4/ips", { signal: AbortSignal.timeout(3e4) });
|
|
48634
|
+
const body = await res.json();
|
|
48635
|
+
if (!res.ok || !body?.success) throw new Error(`Cloudflare IP list unreadable: ${res.status}`);
|
|
48636
|
+
return { ipv4: body.result.ipv4_cidrs ?? [], ipv6: body.result.ipv6_cidrs ?? [] };
|
|
48637
|
+
}
|
|
48638
|
+
var norm = (rules) => JSON.stringify(rules.map((r) => ({ ...r, source_ips: [...r.source_ips ?? []].sort() })).map(({ direction, protocol, port, source_ips }) => ({ direction, protocol, port: port ?? null, source_ips })));
|
|
48639
|
+
function declaredExtras(box) {
|
|
48640
|
+
const extrasFile = JSON.parse((0, import_node_fs38.readFileSync)((0, import_node_url.fileURLToPath)(new URL("./firewall-extras.json", import_meta.url)), "utf8"));
|
|
48641
|
+
return Object.prototype.hasOwnProperty.call(extrasFile, box) ? extrasFile[box] : void 0;
|
|
48642
|
+
}
|
|
48643
|
+
async function main() {
|
|
48644
|
+
const box = arg("box");
|
|
48645
|
+
if (!box || !/^[a-z0-9-]+$/.test(box)) {
|
|
48646
|
+
console.error("cloud-firewall: --box <hetzner server name> is required");
|
|
48647
|
+
process.exit(64);
|
|
48648
|
+
}
|
|
48649
|
+
const detach2 = process.argv.includes("--detach");
|
|
48650
|
+
const name = `mmi-${box}`;
|
|
48651
|
+
const servers = (await hz(`/servers?name=${encodeURIComponent(box)}`)).servers ?? [];
|
|
48652
|
+
if (servers.length !== 1) throw new Error(`expected exactly one server named ${box} in this project, found ${servers.length}`);
|
|
48653
|
+
const server = servers[0];
|
|
48654
|
+
const existing = ((await hz(`/firewalls?name=${encodeURIComponent(name)}`)).firewalls ?? [])[0];
|
|
48655
|
+
const applied = Boolean(existing?.applied_to?.some((a) => a.server?.id === server.id));
|
|
48656
|
+
console.log(`cloud-firewall: box=${box} server=${server.id} ip=${server.public_net?.ipv4?.ip} firewall=${existing ? `${name}#${existing.id}` : `${name} (absent)`} applied=${applied} mode=${APPLY ? "LIVE" : "DRY-RUN"}${detach2 ? " action=detach" : ""}`);
|
|
48657
|
+
if (detach2) {
|
|
48658
|
+
if (!existing || !applied) {
|
|
48659
|
+
console.log("cloud-firewall: not applied to this server \u2014 nothing to detach");
|
|
48660
|
+
return;
|
|
48661
|
+
}
|
|
48662
|
+
if (!APPLY) {
|
|
48663
|
+
console.log("cloud-firewall: dry-run (FIREWALL_APPLY=1 to detach)");
|
|
48664
|
+
return;
|
|
48665
|
+
}
|
|
48666
|
+
await hz(`/firewalls/${existing.id}/actions/remove_from_resources`, { method: "POST", body: JSON.stringify({ remove_from: [{ type: "server", server: { id: server.id } }] }) });
|
|
48667
|
+
console.log(`cloud-firewall: detached ${name} from ${box}`);
|
|
48668
|
+
return;
|
|
48669
|
+
}
|
|
48670
|
+
const extras = declaredExtras(box);
|
|
48671
|
+
if (extras === void 0) throw new Error(`${box} has no entry in firewall-extras.json; declare it (an empty list is fine) before applying`);
|
|
48672
|
+
const rules = buildRules(await cloudflareRanges(), extras);
|
|
48673
|
+
for (const r of rules) console.log(` in ${r.protocol}${r.port ? `/${r.port}` : ""} from ${r.source_ips.length > 2 ? `${r.source_ips.length} Cloudflare ranges` : r.source_ips.join(",")} \u2014 ${r.description}`);
|
|
48674
|
+
const rulesChanged = !existing || norm(existing.rules) !== norm(rules);
|
|
48675
|
+
console.log(`cloud-firewall: rules ${rulesChanged ? "would change" : "unchanged"}; apply-to-server ${applied ? "already done" : "needed"}`);
|
|
48676
|
+
if (!APPLY) {
|
|
48677
|
+
console.log("cloud-firewall: dry-run (FIREWALL_APPLY=1 to write)");
|
|
48678
|
+
return;
|
|
48679
|
+
}
|
|
48680
|
+
let id = existing?.id;
|
|
48681
|
+
if (!existing) {
|
|
48682
|
+
id = (await hz("/firewalls", { method: "POST", body: JSON.stringify({ name, labels: { managed_by: "mmi-hub", box }, rules }) })).firewall.id;
|
|
48683
|
+
console.log(`cloud-firewall: created ${name}#${id}`);
|
|
48684
|
+
} else if (rulesChanged) {
|
|
48685
|
+
await hz(`/firewalls/${id}/actions/set_rules`, { method: "POST", body: JSON.stringify({ rules }) });
|
|
48686
|
+
console.log(`cloud-firewall: updated rules on ${name}#${id}`);
|
|
48687
|
+
}
|
|
48688
|
+
if (!applied) {
|
|
48689
|
+
await hz(`/firewalls/${id}/actions/apply_to_resources`, { method: "POST", body: JSON.stringify({ apply_to: [{ type: "server", server: { id: server.id } }] }) });
|
|
48690
|
+
console.log(`cloud-firewall: applied ${name} to ${box}`);
|
|
48691
|
+
}
|
|
48692
|
+
}
|
|
48693
|
+
if (process.argv[1] && (0, import_node_path35.basename)(process.argv[1]).toLowerCase() === "cloud-firewall.mjs") {
|
|
48694
|
+
main().catch((err) => {
|
|
48695
|
+
console.error(`cloud-firewall: failed: ${err.message}`);
|
|
48696
|
+
process.exit(1);
|
|
48697
|
+
});
|
|
48698
|
+
}
|
|
48699
|
+
|
|
48700
|
+
// ../infra/hetzner/firewall-extras.json
|
|
48701
|
+
var firewall_extras_default = {
|
|
48702
|
+
$comment: "#7356: per-box inbound exceptions to the Cloudflare-only Hetzner Cloud Firewall (cloud-firewall.mjs). Every entry names its owner and reason. Web traffic (80/443) is always Cloudflare-only and SSH (22) is always open; list only what must reach the box directly.",
|
|
48703
|
+
"mm-work": [
|
|
48704
|
+
{ protocol: "tcp", port: "7881", description: "MM-Chat dev huddles LiveKit RTC over TCP (MMI-Hub#7353, owner-approved 2026-09-26)" },
|
|
48705
|
+
{ protocol: "udp", port: "7882", description: "MM-Chat dev huddles LiveKit RTC over UDP (MMI-Hub#7353, owner-approved 2026-09-26)" },
|
|
48706
|
+
{ protocol: "tcp", port: "7891", description: "MM-Chat main huddles LiveKit RTC over TCP (MMI-Hub#7907, owner-approved 2026-10-06)" },
|
|
48707
|
+
{ protocol: "udp", port: "7892", description: "MM-Chat main huddles LiveKit RTC over UDP (MMI-Hub#7907, owner-approved 2026-10-06)" }
|
|
48708
|
+
],
|
|
48709
|
+
"mm-central": [],
|
|
48710
|
+
"mm-recorder": [],
|
|
48711
|
+
"mmi-runner": [],
|
|
48712
|
+
"mmi-runner2": []
|
|
48713
|
+
};
|
|
48714
|
+
|
|
48715
|
+
// ../shared/runner-hosts.ts
|
|
48716
|
+
var RUNNER_HOSTS = ["mmi-runner", "mmi-runner2"];
|
|
48717
|
+
|
|
48718
|
+
// src/box-firewall.ts
|
|
48719
|
+
function declaredExtrasFromBundle(box) {
|
|
48720
|
+
const file = firewall_extras_default;
|
|
48721
|
+
return Object.prototype.hasOwnProperty.call(file, box) ? file[box] : void 0;
|
|
48722
|
+
}
|
|
48723
|
+
var FirewallRefusal = class extends Error {
|
|
48724
|
+
};
|
|
48725
|
+
var FIREWALL_NAME_PREFIX = "mmi-";
|
|
48726
|
+
var ACTION_POLL_MS = 2500;
|
|
48727
|
+
var ACTION_POLL_ATTEMPTS = 20;
|
|
48728
|
+
async function runBoxFirewall(boxName, mode, live, deps) {
|
|
48729
|
+
const runner = RUNNER_HOSTS.find((h) => h.toLowerCase() === boxName.toLowerCase());
|
|
48730
|
+
if (!runner) {
|
|
48731
|
+
throw new FirewallRefusal(
|
|
48732
|
+
`box firewall: '${boxName}' is not a registered runner box \u2014 this command manages only the firewalls of ${RUNNER_HOSTS.join(" and ")} (the reviewed runner policy does not cover any other box)`
|
|
48733
|
+
);
|
|
48734
|
+
}
|
|
48735
|
+
const token = await deps.fetchToken();
|
|
48736
|
+
const firewallName = `${FIREWALL_NAME_PREFIX}${runner}`;
|
|
48737
|
+
if (live === false && mode !== "plan") {
|
|
48738
|
+
throw new FirewallRefusal(`box firewall: ${mode} is a mutation \u2014 it needs --apply`);
|
|
48739
|
+
}
|
|
48740
|
+
const servers = (await deps.hetzner(token, `/servers?name=${encodeURIComponent(runner)}`)).servers ?? [];
|
|
48741
|
+
if (servers.length !== 1) {
|
|
48742
|
+
throw new FirewallRefusal(`box firewall: expected exactly one server named ${runner} in the MM project, found ${servers.length} \u2014 refusing to guess the target`);
|
|
48743
|
+
}
|
|
48744
|
+
const server = servers[0];
|
|
48745
|
+
const observedAt = deps.now();
|
|
48746
|
+
const existing = ((await deps.hetzner(token, `/firewalls?name=${encodeURIComponent(firewallName)}`)).firewalls ?? [])[0];
|
|
48747
|
+
const applied = Boolean(existing?.applied_to?.some((a) => a.server?.id === server.id));
|
|
48748
|
+
const target = {
|
|
48749
|
+
operation: mode,
|
|
48750
|
+
live,
|
|
48751
|
+
box: runner,
|
|
48752
|
+
server: { id: server.id, name: server.name, ...server.public_net?.ipv4?.ip ? { ipv4: server.public_net.ipv4.ip } : {} },
|
|
48753
|
+
firewall: existing ? { id: existing.id, name: existing.name } : null,
|
|
48754
|
+
applied,
|
|
48755
|
+
rulesChanged: false,
|
|
48756
|
+
currentRules: existing?.rules ?? [],
|
|
48757
|
+
plannedRules: null,
|
|
48758
|
+
actions: [],
|
|
48759
|
+
readback: void 0,
|
|
48760
|
+
note: void 0,
|
|
48761
|
+
ambiguous: false,
|
|
48762
|
+
ok: false,
|
|
48763
|
+
observedAt
|
|
48764
|
+
};
|
|
48765
|
+
if (mode === "detach") {
|
|
48766
|
+
if (!existing || !applied) {
|
|
48767
|
+
return { ...target, ok: true, note: `firewall ${firewallName} is ${existing ? "not applied to" : "absent for"} this server \u2014 nothing to detach` };
|
|
48768
|
+
}
|
|
48769
|
+
const action = await actionPost(deps, token, existing.id, "remove_from_resources", { remove_from: [{ type: "server", server: { id: server.id } }] });
|
|
48770
|
+
const waited = await waitForAction(deps, token, action);
|
|
48771
|
+
target.actions.push(actionReceipt("remove_from_resources", waited));
|
|
48772
|
+
target.ambiguous = waited.ambiguous;
|
|
48773
|
+
const readback2 = await readbackState(deps, token, existing.id, server.id, null, deps.now());
|
|
48774
|
+
target.readback = readback2;
|
|
48775
|
+
target.ok = !waited.ambiguous && readback2.attached === false;
|
|
48776
|
+
if (target.ok) target.note = `detached ${firewallName} from ${runner}`;
|
|
48777
|
+
return target;
|
|
48778
|
+
}
|
|
48779
|
+
const extras = deps.declaredExtras(runner);
|
|
48780
|
+
if (extras === void 0) {
|
|
48781
|
+
throw new FirewallRefusal(`box firewall: ${runner} has no entry in infra/hetzner/firewall-extras.json; declare it (an empty list is fine) before planning or applying`);
|
|
48782
|
+
}
|
|
48783
|
+
const planned = buildRules(await deps.cloudflareRanges(), extras);
|
|
48784
|
+
target.plannedRules = planned;
|
|
48785
|
+
const rulesChanged = !existing || norm(existing.rules ?? []) !== norm(planned);
|
|
48786
|
+
target.rulesChanged = rulesChanged;
|
|
48787
|
+
if (mode === "plan") {
|
|
48788
|
+
target.ok = !rulesChanged && applied;
|
|
48789
|
+
if (target.ok) target.note = `firewall ${firewallName}#${existing.id} matches the reviewed policy and is applied`;
|
|
48790
|
+
return target;
|
|
48791
|
+
}
|
|
48792
|
+
let firewallId = existing?.id;
|
|
48793
|
+
if (!existing) {
|
|
48794
|
+
const created = await deps.hetzner(token, "/firewalls", {
|
|
48795
|
+
method: "POST",
|
|
48796
|
+
body: JSON.stringify({ name: firewallName, labels: { managed_by: "mmi-hub", box: runner }, rules: planned })
|
|
48797
|
+
});
|
|
48798
|
+
firewallId = created.firewall.id;
|
|
48799
|
+
target.actions.push({ id: firewallId, kind: "create", status: "created" });
|
|
48800
|
+
} else if (rulesChanged) {
|
|
48801
|
+
const action = await actionPost(deps, token, firewallId, "set_rules", { rules: planned });
|
|
48802
|
+
const waited = await waitForAction(deps, token, action);
|
|
48803
|
+
target.actions.push(actionReceipt("set_rules", waited));
|
|
48804
|
+
target.ambiguous ||= waited.ambiguous;
|
|
48805
|
+
if (waited.ambiguous || waited.action.status === "error") {
|
|
48806
|
+
const readback2 = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48807
|
+
target.readback = readback2;
|
|
48808
|
+
return target;
|
|
48809
|
+
}
|
|
48810
|
+
}
|
|
48811
|
+
if (!applied) {
|
|
48812
|
+
const action = await actionPost(deps, token, firewallId, "apply_to_resources", { apply_to: [{ type: "server", server: { id: server.id } }] });
|
|
48813
|
+
const waited = await waitForAction(deps, token, action);
|
|
48814
|
+
target.actions.push(actionReceipt("apply_to_resources", waited));
|
|
48815
|
+
target.ambiguous ||= waited.ambiguous;
|
|
48816
|
+
if (waited.action.status === "error") {
|
|
48817
|
+
const readback2 = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48818
|
+
target.readback = readback2;
|
|
48819
|
+
return target;
|
|
48820
|
+
}
|
|
48821
|
+
}
|
|
48822
|
+
const readback = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48823
|
+
target.firewall = { id: firewallId, name: firewallName };
|
|
48824
|
+
target.readback = readback;
|
|
48825
|
+
target.ok = !target.ambiguous && readback.attached && readback.rulesMatch === true;
|
|
48826
|
+
if (target.ok) target.note = `firewall ${firewallName}#${firewallId} applied to ${runner} and read back independently`;
|
|
48827
|
+
return target;
|
|
48828
|
+
}
|
|
48829
|
+
async function actionPost(deps, token, firewallId, kind, body) {
|
|
48830
|
+
const res = await deps.hetzner(token, `/firewalls/${firewallId}/actions/${kind}`, { method: "POST", body: JSON.stringify(body) });
|
|
48831
|
+
return res.action;
|
|
48832
|
+
}
|
|
48833
|
+
async function waitForAction(deps, token, accepted) {
|
|
48834
|
+
let last = accepted;
|
|
48835
|
+
for (let attempt = 0; attempt < ACTION_POLL_ATTEMPTS; attempt += 1) {
|
|
48836
|
+
const res = await deps.hetzner(token, `/firewalls/actions/${accepted.id}`);
|
|
48837
|
+
last = res.action ?? last;
|
|
48838
|
+
if (last.status === "success" || last.status === "error") return { action: last, ambiguous: false };
|
|
48839
|
+
await deps.sleep(ACTION_POLL_MS);
|
|
48840
|
+
}
|
|
48841
|
+
return { action: last, ambiguous: true };
|
|
48842
|
+
}
|
|
48843
|
+
function actionReceipt(kind, waited) {
|
|
48844
|
+
return {
|
|
48845
|
+
id: waited.action.id,
|
|
48846
|
+
kind,
|
|
48847
|
+
status: waited.ambiguous ? "running (wait bound exhausted \u2014 ambiguous)" : waited.action.status,
|
|
48848
|
+
...waited.action.error ? { error: { code: waited.action.error.code, message: waited.action.error.message } } : {}
|
|
48849
|
+
};
|
|
48850
|
+
}
|
|
48851
|
+
async function readbackState(deps, token, firewallId, serverId, planned, at) {
|
|
48852
|
+
const firewall = (await deps.hetzner(token, `/firewalls/${firewallId}`)).firewall;
|
|
48853
|
+
const server = (await deps.hetzner(token, `/servers/${serverId}`)).server;
|
|
48854
|
+
const attached = server != null && server.id === serverId && Boolean(firewall?.applied_to?.some((a) => a.server?.id === serverId));
|
|
48855
|
+
const rulesMatch = planned ? norm(firewall?.rules ?? []) === norm(planned) : null;
|
|
48856
|
+
return { observedAt: at, attached, rulesMatch };
|
|
48857
|
+
}
|
|
48858
|
+
|
|
48557
48859
|
// src/box-commands.ts
|
|
48558
48860
|
var HETZNER_API = "https://api.hetzner.cloud/v1/servers";
|
|
48559
48861
|
var HETZNER_TIMEOUT_MS = 15e3;
|
|
@@ -48664,7 +48966,7 @@ function registerBoxCommands(program3) {
|
|
|
48664
48966
|
const stdinSource = o.body ? `"${o.body}"` : "<body.sh>";
|
|
48665
48967
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
48666
48968
|
const runLine = wroteScript ? useStdin ? `bash "${wroteScript}" < ${stdinSource}` : `bash "${wroteScript}"` : void 0;
|
|
48667
|
-
if (wroteScript) (0,
|
|
48969
|
+
if (wroteScript) (0, import_node_fs39.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
48668
48970
|
if (o.json) {
|
|
48669
48971
|
console.log(JSON.stringify({
|
|
48670
48972
|
box: found,
|
|
@@ -48775,6 +49077,79 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
48775
49077
|
process.exitCode = 2;
|
|
48776
49078
|
}
|
|
48777
49079
|
});
|
|
49080
|
+
box.command("firewall <box>").description("the registered runner boxes' Hetzner Cloud Firewall (#9786): --plan (default, read-only) reports the exact current rules and attachment against the reviewed policy; --apply writes it; --detach --apply is the rollback").option("--plan", "read-only: current firewall, attachment and exact rules, and the diff against the reviewed policy (the default)").option("--apply", "create/update the approved firewall (policy #7359, SSH stays world-open) and attach it to exactly this server; waits for each provider action, then re-reads the firewall and attachment independently").option("--detach", "with --apply: detach this exact firewall from this exact server (rollback; a route independent of SSH)").option("--json", "machine-readable receipt").action(async (name, o) => {
|
|
49081
|
+
try {
|
|
49082
|
+
if (o.apply && o.plan) throw new UsageError("box firewall: --plan and --apply are exclusive");
|
|
49083
|
+
if (o.detach && !o.apply) throw new UsageError("box firewall: --detach is a mutation \u2014 it needs --apply");
|
|
49084
|
+
if (!o.apply && !o.plan) o.plan = true;
|
|
49085
|
+
const mode = o.detach ? "detach" : o.apply ? "apply" : "plan";
|
|
49086
|
+
const deps = firewallDeps();
|
|
49087
|
+
if (mode === "plan") {
|
|
49088
|
+
const receipt2 = await runBoxFirewall(name, mode, false, deps);
|
|
49089
|
+
renderFirewallReceipt(receipt2, o.json === true);
|
|
49090
|
+
process.exitCode = receipt2.ok ? 0 : 1;
|
|
49091
|
+
return;
|
|
49092
|
+
}
|
|
49093
|
+
const { boxes, incomplete } = await fetchInventory();
|
|
49094
|
+
const found = lookupBox(name, boxes, incomplete);
|
|
49095
|
+
if (found.kind !== "found") {
|
|
49096
|
+
const why = found.kind === "unknown" ? `the inventory is incomplete (${found.reasons.join("; ")})` : `no box named '${name}'`;
|
|
49097
|
+
throw new UnreadableError(`box firewall: ${why}`);
|
|
49098
|
+
}
|
|
49099
|
+
const receipt = await runBoxFirewall(found.box.name, mode, true, deps);
|
|
49100
|
+
if (!o.json) warnIncomplete(incomplete);
|
|
49101
|
+
renderFirewallReceipt(receipt, o.json === true);
|
|
49102
|
+
process.exitCode = receipt.ok ? 0 : 1;
|
|
49103
|
+
} catch (e) {
|
|
49104
|
+
const message2 = e instanceof FirewallRefusal ? e.message : e.message;
|
|
49105
|
+
if (o.json) console.log(JSON.stringify({ box: name, ok: false, refused: e instanceof FirewallRefusal, error: message2 }, null, 2));
|
|
49106
|
+
else console.error(message2);
|
|
49107
|
+
process.exitCode = 2;
|
|
49108
|
+
}
|
|
49109
|
+
});
|
|
49110
|
+
}
|
|
49111
|
+
function renderFirewallReceipt(r, json) {
|
|
49112
|
+
if (json) {
|
|
49113
|
+
console.log(JSON.stringify(r, null, 2));
|
|
49114
|
+
return;
|
|
49115
|
+
}
|
|
49116
|
+
const fw = r.firewall ? `${r.firewall.name}#${r.firewall.id}` : `mmi-${r.box} (absent)`;
|
|
49117
|
+
const lines2 = [
|
|
49118
|
+
`${r.box} \u2014 firewall ${r.operation}${r.live ? " (LIVE)" : " (dry-run)"}`,
|
|
49119
|
+
` server: #${r.server.id}${r.server.ipv4 ? ` (${r.server.ipv4})` : ""}`,
|
|
49120
|
+
` firewall: ${fw}, ${r.applied ? "attached to this server" : "not attached to this server"}`
|
|
49121
|
+
];
|
|
49122
|
+
if (r.plannedRules) {
|
|
49123
|
+
lines2.push(` rules: ${r.rulesChanged ? "WOULD CHANGE \u2014 planned set differs from the provider" : "match the reviewed policy"}`);
|
|
49124
|
+
for (const rule of r.plannedRules) {
|
|
49125
|
+
const from = rule.source_ips.length > 2 ? `${rule.source_ips.length} Cloudflare ranges` : rule.source_ips.join(", ");
|
|
49126
|
+
lines2.push(` in ${rule.protocol}${rule.port ? `/${rule.port}` : ""} from ${from} \u2014 ${rule.description}`);
|
|
49127
|
+
}
|
|
49128
|
+
}
|
|
49129
|
+
for (const a of r.actions) lines2.push(` action ${a.kind} #${a.id}: ${a.status}${a.error ? ` \u2014 ${a.error.code ?? ""} ${a.error.message ?? ""}` : ""}`);
|
|
49130
|
+
if (r.readback) {
|
|
49131
|
+
lines2.push(` readback (independent, ${r.readback.observedAt}): attached=${r.readback.attached}${r.readback.rulesMatch === null ? "" : ` rulesMatch=${r.readback.rulesMatch}`}`);
|
|
49132
|
+
}
|
|
49133
|
+
if (r.ambiguous) lines2.push(" AMBIGUOUS: a provider action never reached success/error \u2014 treat the outcome as unknown, do not blindly rerun the mutation");
|
|
49134
|
+
lines2.push(` ${r.ok ? "OK" : "NOT OK"}${r.note ? ` \u2014 ${r.note}` : ""} (observed ${r.observedAt})`);
|
|
49135
|
+
console.log(lines2.join("\n"));
|
|
49136
|
+
}
|
|
49137
|
+
function firewallDeps() {
|
|
49138
|
+
return {
|
|
49139
|
+
hetzner: (token, path2, init) => hetzner(token, path2, init),
|
|
49140
|
+
cloudflareRanges: () => cloudflareRanges(),
|
|
49141
|
+
declaredExtras: (boxName) => declaredExtrasFromBundle(boxName),
|
|
49142
|
+
fetchToken: async () => {
|
|
49143
|
+
let token = null;
|
|
49144
|
+
await withSecrets(async (secretsDeps) => {
|
|
49145
|
+
token = await fetchSecretValue(secretsDeps, PROJECT_API_TOKEN.MM, { slug: HETZNER_VAULT_SLUG }, true);
|
|
49146
|
+
});
|
|
49147
|
+
if (!token) throw new Error("box firewall: could not read the Hetzner MM API token from the vault (the value is never printed)");
|
|
49148
|
+
return token;
|
|
49149
|
+
},
|
|
49150
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
49151
|
+
now: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
49152
|
+
};
|
|
48778
49153
|
}
|
|
48779
49154
|
var UsageError = class extends Error {
|
|
48780
49155
|
};
|
|
@@ -48791,17 +49166,17 @@ function hubGitShow(ref) {
|
|
|
48791
49166
|
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
48792
49167
|
}
|
|
48793
49168
|
function runOnBox(found, script) {
|
|
48794
|
-
const dir = (0,
|
|
49169
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os20.tmpdir)(), "mmi-box-control-"));
|
|
48795
49170
|
try {
|
|
48796
|
-
const recipe = (0,
|
|
48797
|
-
(0,
|
|
49171
|
+
const recipe = (0, import_node_path36.join)(dir, "recipe.sh");
|
|
49172
|
+
(0, import_node_fs39.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
48798
49173
|
const r = (0, import_node_child_process12.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
48799
49174
|
if (r.status !== 0) {
|
|
48800
49175
|
throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
|
|
48801
49176
|
}
|
|
48802
49177
|
return r.stdout;
|
|
48803
49178
|
} finally {
|
|
48804
|
-
(0,
|
|
49179
|
+
(0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
|
|
48805
49180
|
}
|
|
48806
49181
|
}
|
|
48807
49182
|
function formatControl(mode, box, rows) {
|
|
@@ -50302,12 +50677,12 @@ var declaredProjectAdmins = (meta) => {
|
|
|
50302
50677
|
};
|
|
50303
50678
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
50304
50679
|
async function gateRenderDiff(current, rendered) {
|
|
50305
|
-
const dir = (0,
|
|
50306
|
-
const before = (0,
|
|
50307
|
-
const after = (0,
|
|
50680
|
+
const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
|
|
50681
|
+
const before = (0, import_node_path37.join)(dir, "committed-gate.yml");
|
|
50682
|
+
const after = (0, import_node_path37.join)(dir, "registry-render.yml");
|
|
50308
50683
|
try {
|
|
50309
|
-
(0,
|
|
50310
|
-
(0,
|
|
50684
|
+
(0, import_node_fs40.writeFileSync)(before, current);
|
|
50685
|
+
(0, import_node_fs40.writeFileSync)(after, rendered);
|
|
50311
50686
|
try {
|
|
50312
50687
|
return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
50313
50688
|
} catch (e) {
|
|
@@ -50316,7 +50691,7 @@ async function gateRenderDiff(current, rendered) {
|
|
|
50316
50691
|
throw e;
|
|
50317
50692
|
}
|
|
50318
50693
|
} finally {
|
|
50319
|
-
(0,
|
|
50694
|
+
(0, import_node_fs40.rmSync)(dir, { recursive: true, force: true });
|
|
50320
50695
|
}
|
|
50321
50696
|
}
|
|
50322
50697
|
function bootstrapTrainDoctorDeps() {
|
|
@@ -50337,9 +50712,9 @@ async function propagationScope(classes, what) {
|
|
|
50337
50712
|
}
|
|
50338
50713
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
50339
50714
|
let independentCount = rosterRepos.length;
|
|
50340
|
-
if ((0,
|
|
50715
|
+
if ((0, import_node_fs40.existsSync)("projects.json")) {
|
|
50341
50716
|
try {
|
|
50342
|
-
const local = JSON.parse((0,
|
|
50717
|
+
const local = JSON.parse((0, import_node_fs40.readFileSync)("projects.json", "utf8"));
|
|
50343
50718
|
const localRepos = /* @__PURE__ */ new Set();
|
|
50344
50719
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
50345
50720
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -50464,13 +50839,13 @@ function registerBootstrapCommands(program3) {
|
|
|
50464
50839
|
})),
|
|
50465
50840
|
pushAllowlistOwners: pushAllowlistAuthority(verifyOwners, declaredProjectAdmins(meta)),
|
|
50466
50841
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
50467
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
50842
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null,
|
|
50468
50843
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
50469
50844
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
50470
50845
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
50471
50846
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
50472
50847
|
// sanction, which is the pre-#3664 behaviour.
|
|
50473
|
-
sanctionedAdmins: (0,
|
|
50848
|
+
sanctionedAdmins: (0, import_node_fs40.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs40.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
50474
50849
|
requiredGcpApis: (() => {
|
|
50475
50850
|
const v = meta?.requiredGcpApis;
|
|
50476
50851
|
if (Array.isArray(v)) return v;
|
|
@@ -50548,14 +50923,14 @@ function registerBootstrapCommands(program3) {
|
|
|
50548
50923
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
50549
50924
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
50550
50925
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50551
|
-
if (!(0,
|
|
50926
|
+
if (!(0, import_node_fs40.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
50552
50927
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50553
50928
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
50554
|
-
const manifest = loadBootstrapSeeds((0,
|
|
50929
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
50555
50930
|
const hubContents = /* @__PURE__ */ new Map();
|
|
50556
50931
|
for (const s of manifest.seeds) {
|
|
50557
50932
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
50558
|
-
hubContents.set(s.target, (0,
|
|
50933
|
+
hubContents.set(s.target, (0, import_node_fs40.existsSync)(s.target) ? (0, import_node_fs40.readFileSync)(s.target, "utf8") : null);
|
|
50559
50934
|
}
|
|
50560
50935
|
let targets;
|
|
50561
50936
|
let classOf = (_repo) => "deployable";
|
|
@@ -50811,24 +51186,24 @@ function registerBootstrapCommands(program3) {
|
|
|
50811
51186
|
return;
|
|
50812
51187
|
}
|
|
50813
51188
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50814
|
-
if (!(0,
|
|
51189
|
+
if (!(0, import_node_fs40.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
50815
51190
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50816
51191
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
50817
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51192
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
50818
51193
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
50819
51194
|
const slug = parsedRepo.slug;
|
|
50820
51195
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
50821
51196
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
50822
|
-
const readFile10 = (p) => (0,
|
|
51197
|
+
const readFile10 = (p) => (0, import_node_fs40.existsSync)(p) ? (0, import_node_fs40.readFileSync)(p, "utf8") : null;
|
|
50823
51198
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
50824
51199
|
const putSeed = async (target, content, ref, sha) => {
|
|
50825
|
-
const tmp = (0,
|
|
50826
|
-
(0,
|
|
51200
|
+
const tmp = (0, import_node_path37.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
51201
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
50827
51202
|
try {
|
|
50828
51203
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
50829
51204
|
} finally {
|
|
50830
51205
|
try {
|
|
50831
|
-
(0,
|
|
51206
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
50832
51207
|
} catch {
|
|
50833
51208
|
}
|
|
50834
51209
|
}
|
|
@@ -51466,10 +51841,10 @@ LIVE apply to ${repo}:
|
|
|
51466
51841
|
if (rawFlag("--pins")) return propagatePins(rawFlag("--apply"), rawFlag("--json"));
|
|
51467
51842
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
51468
51843
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
51469
|
-
if (!(0,
|
|
51844
|
+
if (!(0, import_node_fs40.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
51470
51845
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
51471
51846
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
51472
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51847
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
51473
51848
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
51474
51849
|
if (!o.target) {
|
|
51475
51850
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -51478,9 +51853,9 @@ LIVE apply to ${repo}:
|
|
|
51478
51853
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
51479
51854
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
51480
51855
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
51481
|
-
if (!seed.managedBlock && !(0,
|
|
51482
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
51483
|
-
const readSeedFile2 = (path2) => (0,
|
|
51856
|
+
if (!seed.managedBlock && !(0, import_node_fs40.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
51857
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs40.readFileSync)(seed.target, "utf8");
|
|
51858
|
+
const readSeedFile2 = (path2) => (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null;
|
|
51484
51859
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
51485
51860
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
51486
51861
|
const repos = inClassRepos.map((repo) => {
|
|
@@ -51599,7 +51974,7 @@ LIVE apply to ${repo}:
|
|
|
51599
51974
|
if (plan.refusedNoCanary) return fail("bootstrap propagate --execute: no canary declared for this target \u2014 refusing to write (set seedCanary:true on exactly one registry repo)");
|
|
51600
51975
|
const headSha = seedSource.sha;
|
|
51601
51976
|
const sourceBlob = (await execGitForSeedSource(["rev-parse", `${headSha}:${seedSourcePath(seed)}`])).trim();
|
|
51602
|
-
const runbookUnaffected = (0,
|
|
51977
|
+
const runbookUnaffected = (0, import_node_fs40.existsSync)(RUNBOOK_UNAFFECTED_PATH) ? runbookUnaffectedReason(JSON.parse((0, import_node_fs40.readFileSync)(RUNBOOK_UNAFFECTED_PATH, "utf8")), seed.target, sourceBlob) : void 0;
|
|
51603
51978
|
for (const rec of plan.records) {
|
|
51604
51979
|
if (rec.action !== "open-pr") continue;
|
|
51605
51980
|
const baseBranch = classOf(rec.repo) === "content" ? "main" : "development";
|
|
@@ -51621,15 +51996,15 @@ LIVE apply to ${repo}:
|
|
|
51621
51996
|
} catch {
|
|
51622
51997
|
existingSha = void 0;
|
|
51623
51998
|
}
|
|
51624
|
-
const tmp = (0,
|
|
51999
|
+
const tmp = (0, import_node_path37.join)((0, import_node_os21.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
51625
52000
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
51626
52001
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
51627
|
-
(0,
|
|
52002
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha, runbookUnaffected)), "utf8");
|
|
51628
52003
|
try {
|
|
51629
52004
|
await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
51630
52005
|
} finally {
|
|
51631
52006
|
try {
|
|
51632
|
-
(0,
|
|
52007
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
51633
52008
|
} catch {
|
|
51634
52009
|
}
|
|
51635
52010
|
}
|
|
@@ -51698,10 +52073,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51698
52073
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
51699
52074
|
}
|
|
51700
52075
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
51701
|
-
if (!(0,
|
|
52076
|
+
if (!(0, import_node_fs40.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
51702
52077
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
51703
52078
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
51704
|
-
const manifest = loadBootstrapSeeds((0,
|
|
52079
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
51705
52080
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
51706
52081
|
if (!o.target) {
|
|
51707
52082
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -51718,10 +52093,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51718
52093
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
51719
52094
|
let candidates;
|
|
51720
52095
|
if (o.record) {
|
|
51721
|
-
if (!(0,
|
|
52096
|
+
if (!(0, import_node_fs40.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
51722
52097
|
let parsed;
|
|
51723
52098
|
try {
|
|
51724
|
-
parsed = JSON.parse((0,
|
|
52099
|
+
parsed = JSON.parse((0, import_node_fs40.readFileSync)(o.record, "utf8"));
|
|
51725
52100
|
} catch (e) {
|
|
51726
52101
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
51727
52102
|
}
|
|
@@ -51798,13 +52173,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51798
52173
|
} catch {
|
|
51799
52174
|
existingSha = void 0;
|
|
51800
52175
|
}
|
|
51801
|
-
const tmp = (0,
|
|
51802
|
-
(0,
|
|
52176
|
+
const tmp = (0, import_node_path37.join)((0, import_node_os21.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
52177
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
51803
52178
|
try {
|
|
51804
52179
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
51805
52180
|
} finally {
|
|
51806
52181
|
try {
|
|
51807
|
-
(0,
|
|
52182
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
51808
52183
|
} catch {
|
|
51809
52184
|
}
|
|
51810
52185
|
}
|
|
@@ -52309,8 +52684,8 @@ function formatSuiteMarkdown(report) {
|
|
|
52309
52684
|
const lines2 = ["| Member | dev | rc | main | Pinned by suite |", "| --- | --- | --- | --- | --- |"];
|
|
52310
52685
|
if (report.lead) lines2.push(`| MM-Work (suite) | ${short(report.lead.dev)} | ${report.lead.rc} | ${report.lead.main} | - |`);
|
|
52311
52686
|
for (const m of report.members.slice(report.lead ? 1 : 0)) {
|
|
52312
|
-
const [dev, rc,
|
|
52313
|
-
lines2.push(`| ${m.repo.split("/")[1]} | ${cell(dev)} | ${cell(rc)} | ${cell(
|
|
52687
|
+
const [dev, rc, main2] = ["dev", "rc", "main"].map((s) => m.stages.find((x) => x.stage === s));
|
|
52688
|
+
lines2.push(`| ${m.repo.split("/")[1]} | ${cell(dev)} | ${cell(rc)} | ${cell(main2)} | ${[by(rc, "rc"), by(main2, "main")].filter(Boolean).join(", ") || "-"} |`);
|
|
52314
52689
|
}
|
|
52315
52690
|
return lines2.join("\n");
|
|
52316
52691
|
}
|
|
@@ -52400,8 +52775,8 @@ async function waitSuiteDev(deps, opts) {
|
|
|
52400
52775
|
}
|
|
52401
52776
|
|
|
52402
52777
|
// src/suite-train.ts
|
|
52403
|
-
var
|
|
52404
|
-
var
|
|
52778
|
+
var import_node_fs41 = require("node:fs");
|
|
52779
|
+
var import_node_path38 = require("node:path");
|
|
52405
52780
|
var import_node_crypto20 = require("node:crypto");
|
|
52406
52781
|
|
|
52407
52782
|
// src/kit-version.ts
|
|
@@ -52487,7 +52862,7 @@ function suiteReleaseTag(rc) {
|
|
|
52487
52862
|
function readSuiteCut(path2) {
|
|
52488
52863
|
let raw;
|
|
52489
52864
|
try {
|
|
52490
|
-
raw = (0,
|
|
52865
|
+
raw = (0, import_node_fs41.readFileSync)(path2, "utf8");
|
|
52491
52866
|
} catch (e) {
|
|
52492
52867
|
if (e.code === "ENOENT") return void 0;
|
|
52493
52868
|
throw e;
|
|
@@ -52503,11 +52878,11 @@ function readSuiteCut(path2) {
|
|
|
52503
52878
|
return record2;
|
|
52504
52879
|
}
|
|
52505
52880
|
function writeSuiteCut(path2, record2) {
|
|
52506
|
-
(0,
|
|
52881
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
|
|
52507
52882
|
const temp = `${path2}.${(0, import_node_crypto20.randomBytes)(8).toString("hex")}.tmp`;
|
|
52508
52883
|
try {
|
|
52509
|
-
(0,
|
|
52510
|
-
(0,
|
|
52884
|
+
(0, import_node_fs41.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
|
|
52885
|
+
(0, import_node_fs41.renameSync)(temp, path2);
|
|
52511
52886
|
} catch (e) {
|
|
52512
52887
|
throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
|
|
52513
52888
|
}
|
|
@@ -52523,15 +52898,15 @@ function pinVersion(url) {
|
|
|
52523
52898
|
}
|
|
52524
52899
|
}
|
|
52525
52900
|
async function carriedPins(deps, member, bundles) {
|
|
52526
|
-
const
|
|
52527
|
-
if (!
|
|
52528
|
-
if (!await deps.tagSha(member.repo, pinVersion(
|
|
52529
|
-
const mainPin = mainPinFor(new URL(
|
|
52530
|
-
for (const url of [bundles.rc,
|
|
52901
|
+
const main2 = bundles.main;
|
|
52902
|
+
if (!main2 || !RELEASE_TAG.test(pinVersion(main2))) throw new Error(`${member.repo}: nothing new since rc, but its MM-Work main pin ${main2 ?? "(none)"} has no ?v=vX.Y.Z release; pin it before the suite cut`);
|
|
52903
|
+
if (!await deps.tagSha(member.repo, pinVersion(main2))) throw new Error(`${member.repo}: nothing new since rc, but its MM-Work main pin names ${pinVersion(main2)}, which was never released; release it or pin its live release before the suite cut`);
|
|
52904
|
+
const mainPin = mainPinFor(new URL(main2).host, bundles, pinVersion(main2));
|
|
52905
|
+
for (const url of [bundles.rc, main2]) {
|
|
52531
52906
|
const tag = pinVersion(url);
|
|
52532
52907
|
if (!RC_TAG.test(tag) && !RELEASE_TAG.test(tag)) continue;
|
|
52533
52908
|
const sha = await deps.tagSha(member.repo, tag);
|
|
52534
|
-
if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url ===
|
|
52909
|
+
if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url === main2 ? mainPin : url, mainPin };
|
|
52535
52910
|
}
|
|
52536
52911
|
throw new Error(`${member.repo}: nothing new since rc, but no pinned MM-Work bundle carries development ${member.development.slice(0, 12)}; pin it before the suite cut`);
|
|
52537
52912
|
}
|
|
@@ -52556,25 +52931,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
|
|
|
52556
52931
|
return tags.map((t) => `${t.repo}@${t.tag}`);
|
|
52557
52932
|
}
|
|
52558
52933
|
async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
52559
|
-
if (!(0,
|
|
52934
|
+
if (!(0, import_node_fs41.existsSync)(deps.ledgerPath)) return void 0;
|
|
52560
52935
|
return withLedgerLock(deps.ledgerPath, async (fence) => {
|
|
52561
|
-
const ledger = parseReleaseLedger((0,
|
|
52936
|
+
const ledger = parseReleaseLedger((0, import_node_fs41.readFileSync)(deps.ledgerPath, "utf8"));
|
|
52562
52937
|
const states = Object.values(ledger.phases).map((p) => p.state);
|
|
52563
52938
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
52564
52939
|
if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
|
|
52565
52940
|
if ((await deps.rcHead())?.toLowerCase() === ledger.tagSha.toLowerCase()) throw new Error(`origin rc already points at ${ledger.tag}; it was promoted, so its ledger is not retired`);
|
|
52566
|
-
const archive = (0,
|
|
52567
|
-
if ((0,
|
|
52941
|
+
const archive = (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
52942
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
52568
52943
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
52569
|
-
await fence(() => (0,
|
|
52944
|
+
await fence(() => (0, import_node_fs41.renameSync)(deps.ledgerPath, archive));
|
|
52570
52945
|
return `${ledger.tag} (ledger archived to ${archive})`;
|
|
52571
52946
|
});
|
|
52572
52947
|
}
|
|
52573
52948
|
function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
|
|
52574
52949
|
const record2 = abandonableCut(recordPath, ownerGo);
|
|
52575
52950
|
const archive = `${recordPath}.abandoned-${record2.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
|
|
52576
|
-
if ((0,
|
|
52577
|
-
(0,
|
|
52951
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
|
|
52952
|
+
(0, import_node_fs41.renameSync)(recordPath, archive);
|
|
52578
52953
|
return { archive, orphanTags: record2.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
|
|
52579
52954
|
}
|
|
52580
52955
|
async function suiteRestoreMainPins(deps) {
|
|
@@ -52661,12 +53036,12 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52661
53036
|
}
|
|
52662
53037
|
if ((record2?.announcement === "sent" || record2?.announcement === "skipped") && record2.leadReleased) {
|
|
52663
53038
|
const archive = `${deps.recordPath}.${record2.leadRcTag}.json`;
|
|
52664
|
-
if ((0,
|
|
52665
|
-
(0,
|
|
53039
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
|
|
53040
|
+
(0, import_node_fs41.renameSync)(deps.recordPath, archive);
|
|
52666
53041
|
record2 = void 0;
|
|
52667
53042
|
}
|
|
52668
53043
|
if (record2 && !record2.pinPr && !record2.leadRcTag && record2.members.every((m) => !m.tag)) {
|
|
52669
|
-
(0,
|
|
53044
|
+
(0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52670
53045
|
record2 = void 0;
|
|
52671
53046
|
}
|
|
52672
53047
|
if (!record2) {
|
|
@@ -52720,7 +53095,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52720
53095
|
quiet.push(member);
|
|
52721
53096
|
}
|
|
52722
53097
|
if (record2.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record2.leadDevelopment, record2.leadPreviousRc)) {
|
|
52723
|
-
(0,
|
|
53098
|
+
(0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52724
53099
|
throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
|
|
52725
53100
|
}
|
|
52726
53101
|
for (const member of quiet) {
|
|
@@ -52738,7 +53113,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52738
53113
|
}
|
|
52739
53114
|
if (refusals.length) {
|
|
52740
53115
|
const untouched = record2.members.every((m) => !m.tag) && !record2.pinPr;
|
|
52741
|
-
if (untouched) (0,
|
|
53116
|
+
if (untouched) (0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52742
53117
|
throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
|
|
52743
53118
|
${refusals.join("\n")}
|
|
52744
53119
|
Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
|
|
@@ -52757,7 +53132,7 @@ Each module must ship a live release that bind-mounts <deployPath>/data read-onl
|
|
|
52757
53132
|
let running = 0;
|
|
52758
53133
|
const waiting = [];
|
|
52759
53134
|
const results = await Promise.allSettled(record2.members.map(async (member) => {
|
|
52760
|
-
if (running >= 4) await new Promise((
|
|
53135
|
+
if (running >= 4) await new Promise((resolve8) => waiting.push(resolve8));
|
|
52761
53136
|
else running++;
|
|
52762
53137
|
try {
|
|
52763
53138
|
if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
|
|
@@ -52930,11 +53305,11 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
52930
53305
|
}
|
|
52931
53306
|
return record2;
|
|
52932
53307
|
}
|
|
52933
|
-
var hotfixRecordPath = (deps) => (0,
|
|
53308
|
+
var hotfixRecordPath = (deps) => (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.recordPath), "hotfix.json");
|
|
52934
53309
|
function readSuiteHotfix(path2) {
|
|
52935
53310
|
let raw;
|
|
52936
53311
|
try {
|
|
52937
|
-
raw = (0,
|
|
53312
|
+
raw = (0, import_node_fs41.readFileSync)(path2, "utf8");
|
|
52938
53313
|
} catch (e) {
|
|
52939
53314
|
if (e.code === "ENOENT") return void 0;
|
|
52940
53315
|
throw e;
|
|
@@ -52977,7 +53352,7 @@ async function suiteHotfix(deps, opts) {
|
|
|
52977
53352
|
if (completed) record2 = void 0;
|
|
52978
53353
|
if (record2 && record2.repo !== found.repo) throw new Error(`an unfinished suite hotfix for ${record2.repo} is recorded; finish it (rerun with that member) first`);
|
|
52979
53354
|
if (!record2?.tag) await deps.validateHotfixSummary();
|
|
52980
|
-
if (completed) (0,
|
|
53355
|
+
if (completed) (0, import_node_fs41.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
|
|
52981
53356
|
const rec = record2 ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
|
|
52982
53357
|
const save = () => writeSuiteHotfix(path2, rec);
|
|
52983
53358
|
save();
|
|
@@ -53245,9 +53620,9 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
53245
53620
|
|
|
53246
53621
|
// src/suite-train-io.ts
|
|
53247
53622
|
var import_node_child_process19 = require("node:child_process");
|
|
53248
|
-
var import_node_path50 = require("node:path");
|
|
53249
|
-
var import_node_fs54 = require("node:fs");
|
|
53250
53623
|
var import_node_path51 = require("node:path");
|
|
53624
|
+
var import_node_fs55 = require("node:fs");
|
|
53625
|
+
var import_node_path52 = require("node:path");
|
|
53251
53626
|
init_cli_shared();
|
|
53252
53627
|
|
|
53253
53628
|
// src/command-register-train-operations.ts
|
|
@@ -53428,7 +53803,7 @@ function clean3(out) {
|
|
|
53428
53803
|
return out.trim();
|
|
53429
53804
|
}
|
|
53430
53805
|
function sleeper(deps) {
|
|
53431
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
53806
|
+
return deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
53432
53807
|
}
|
|
53433
53808
|
function normalizeHotfixVersion(input) {
|
|
53434
53809
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -54939,8 +55314,8 @@ function checkHotfixCarries(options) {
|
|
|
54939
55314
|
}
|
|
54940
55315
|
|
|
54941
55316
|
// src/merge-cleanup.ts
|
|
54942
|
-
var
|
|
54943
|
-
var
|
|
55317
|
+
var import_node_fs42 = require("node:fs");
|
|
55318
|
+
var import_node_path39 = require("node:path");
|
|
54944
55319
|
var import_node_os22 = require("node:os");
|
|
54945
55320
|
init_cli_shared();
|
|
54946
55321
|
init_github_client();
|
|
@@ -55348,13 +55723,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
55348
55723
|
return rewritten === base ? null : rewritten;
|
|
55349
55724
|
}
|
|
55350
55725
|
function writeSquashBodyFile(body) {
|
|
55351
|
-
const dir = (0,
|
|
55352
|
-
const path2 = (0,
|
|
55353
|
-
(0,
|
|
55726
|
+
const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path39.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
|
|
55727
|
+
const path2 = (0, import_node_path39.join)(dir, "body.txt");
|
|
55728
|
+
(0, import_node_fs42.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
55354
55729
|
`, "utf8");
|
|
55355
55730
|
return { path: path2, cleanup: () => {
|
|
55356
55731
|
try {
|
|
55357
|
-
(0,
|
|
55732
|
+
(0, import_node_fs42.rmSync)(dir, { recursive: true, force: true });
|
|
55358
55733
|
} catch {
|
|
55359
55734
|
}
|
|
55360
55735
|
} };
|
|
@@ -55606,8 +55981,8 @@ init_github_client();
|
|
|
55606
55981
|
|
|
55607
55982
|
// src/release-custody.ts
|
|
55608
55983
|
var import_node_crypto21 = require("node:crypto");
|
|
55609
|
-
var
|
|
55610
|
-
var
|
|
55984
|
+
var import_node_fs43 = require("node:fs");
|
|
55985
|
+
var import_node_path40 = require("node:path");
|
|
55611
55986
|
init_github_client();
|
|
55612
55987
|
|
|
55613
55988
|
// src/release-custody-bootstrap.ts
|
|
@@ -55740,9 +56115,9 @@ async function verifyCustodyBootstrap(value, api, controller, requiredContexts,
|
|
|
55740
56115
|
if (legacy.length > 20) reject("local evidence exceeds the bounded migration limit");
|
|
55741
56116
|
const root = `repos/${HUB_RELEASE_REPO}`;
|
|
55742
56117
|
const tag = await api.rest("GET", `${root}/git/ref/tags/${input.tag}`);
|
|
55743
|
-
const
|
|
56118
|
+
const main2 = await api.rest("GET", `${root}/git/ref/heads/main`);
|
|
55744
56119
|
const release = await api.rest("GET", `${root}/releases/tags/${input.tag}`);
|
|
55745
|
-
if (tag.object.sha !== input.tagSha ||
|
|
56120
|
+
if (tag.object.sha !== input.tagSha || main2.object.sha !== input.tagSha || release.tag_name !== input.tag || release.draft || release.prerelease || !Number.isFinite(Date.parse(release.published_at))) reject("tag, main and published Release do not identify one completed cut");
|
|
55746
56121
|
const observed = {};
|
|
55747
56122
|
for (const name of ["gate", "publish", "deploy", "installer", "tenant"]) {
|
|
55748
56123
|
const run = await runAttempt(api, input[name]);
|
|
@@ -56077,7 +56452,7 @@ function reclassifySupersededArchives(state) {
|
|
|
56077
56452
|
}
|
|
56078
56453
|
function localRaw(path2) {
|
|
56079
56454
|
try {
|
|
56080
|
-
return (0,
|
|
56455
|
+
return (0, import_node_fs43.readFileSync)(path2, "utf8");
|
|
56081
56456
|
} catch (e) {
|
|
56082
56457
|
if (e.code === "ENOENT") return null;
|
|
56083
56458
|
throw e;
|
|
@@ -56086,19 +56461,19 @@ function localRaw(path2) {
|
|
|
56086
56461
|
function hydrateLocal(path2, raw) {
|
|
56087
56462
|
const previous = localRaw(path2);
|
|
56088
56463
|
if (previous === raw) return;
|
|
56089
|
-
(0,
|
|
56464
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(path2), { recursive: true });
|
|
56090
56465
|
if (previous !== null) {
|
|
56091
56466
|
const ledger = parseReleaseLedger(previous);
|
|
56092
|
-
const archive = (0,
|
|
56093
|
-
if (!(0,
|
|
56094
|
-
if (raw === null) (0,
|
|
56467
|
+
const archive = (0, import_node_path40.join)((0, import_node_path40.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
|
|
56468
|
+
if (!(0, import_node_fs43.existsSync)(archive)) (0, import_node_fs43.writeFileSync)(archive, previous, { flag: "wx" });
|
|
56469
|
+
if (raw === null) (0, import_node_fs43.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
|
|
56095
56470
|
}
|
|
56096
|
-
if (raw !== null) (0,
|
|
56471
|
+
if (raw !== null) (0, import_node_fs43.writeFileSync)(path2, raw, "utf8");
|
|
56097
56472
|
}
|
|
56098
56473
|
async function commandIdentity(file, args, run, repo) {
|
|
56099
56474
|
if (file === "git" && args[0] === "push") {
|
|
56100
56475
|
const deleting = args.includes("--delete");
|
|
56101
|
-
const refs = args.slice(2).filter((
|
|
56476
|
+
const refs = args.slice(2).filter((arg2) => !arg2.startsWith("-"));
|
|
56102
56477
|
const targets = [];
|
|
56103
56478
|
for (const refspec of refs) {
|
|
56104
56479
|
const [source, destination] = refspec.split(":");
|
|
@@ -56113,7 +56488,7 @@ async function commandIdentity(file, args, run, repo) {
|
|
|
56113
56488
|
return { kind: "github-release", identity: { repo, tag, sha: (await run("git", ["rev-parse", `${tag}^{commit}`])).trim() } };
|
|
56114
56489
|
}
|
|
56115
56490
|
if (file === "gh" && args[0] === "workflow" && args[1] === "run") {
|
|
56116
|
-
const fields = Object.fromEntries(args.flatMap((
|
|
56491
|
+
const fields = Object.fromEntries(args.flatMap((arg2, i) => ["-f", "--field", "-F", "--raw-field"].includes(arg2) ? [args[i + 1].split(/=(.*)/s).slice(0, 2)] : []));
|
|
56117
56492
|
const ref = args.includes("--ref") ? args[args.indexOf("--ref") + 1] : "main";
|
|
56118
56493
|
const sha = (await run("git", ["rev-parse", `origin/${ref}`])).trim();
|
|
56119
56494
|
const before = JSON.parse(await run("gh", [
|
|
@@ -56130,7 +56505,7 @@ async function commandIdentity(file, args, run, repo) {
|
|
|
56130
56505
|
return { kind: "workflow-rerun", identity: { repo, runId, attempt: prior.run_attempt + 1 } };
|
|
56131
56506
|
}
|
|
56132
56507
|
if (file === "gh" && !["run", "release", "pr", "api", "workflow"].includes(args[0])) return null;
|
|
56133
|
-
const write = file === "gh" && (args[0] === "pr" && ["create", "merge", "close", "edit"].includes(args[1]) || args[0] === "release" && ["delete", "edit", "upload"].includes(args[1]) || args[0] === "api" && (args[1] === "graphql" ? args.some((
|
|
56508
|
+
const write = file === "gh" && (args[0] === "pr" && ["create", "merge", "close", "edit"].includes(args[1]) || args[0] === "release" && ["delete", "edit", "upload"].includes(args[1]) || args[0] === "api" && (args[1] === "graphql" ? args.some((arg2) => /(?:^query=|^)\s*mutation\b/.test(arg2)) : args.some((arg2) => ["POST", "PUT", "PATCH", "DELETE"].includes(arg2)) || args.includes("-f") || args.includes("-F") || args.includes("--input")));
|
|
56134
56509
|
return write ? { kind: "github-write", identity: { repo, command: args } } : null;
|
|
56135
56510
|
}
|
|
56136
56511
|
function effectObserver(api) {
|
|
@@ -56283,8 +56658,8 @@ function withHubReleaseCustody(deps) {
|
|
|
56283
56658
|
}
|
|
56284
56659
|
|
|
56285
56660
|
// src/project-info-sync.ts
|
|
56286
|
-
var
|
|
56287
|
-
var
|
|
56661
|
+
var import_node_fs44 = require("node:fs");
|
|
56662
|
+
var import_node_path41 = require("node:path");
|
|
56288
56663
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
56289
56664
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
56290
56665
|
projectV2 { id }
|
|
@@ -56331,14 +56706,14 @@ function sharedName(entries, fallback) {
|
|
|
56331
56706
|
}
|
|
56332
56707
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
56333
56708
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
56334
|
-
const readmePath = (0,
|
|
56335
|
-
if (!(0,
|
|
56709
|
+
const readmePath = (0, import_node_path41.join)(repoRoot2, "README.md");
|
|
56710
|
+
if (!(0, import_node_fs44.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
56336
56711
|
const entries = entriesFor(project2, projects);
|
|
56337
56712
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
56338
56713
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
56339
56714
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
56340
56715
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
56341
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
56716
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs44.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
56342
56717
|
const lines2 = [
|
|
56343
56718
|
`# ${projectName}`,
|
|
56344
56719
|
"",
|
|
@@ -56357,8 +56732,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
56357
56732
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
56358
56733
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
56359
56734
|
const orgDocs = [
|
|
56360
|
-
(0,
|
|
56361
|
-
(0,
|
|
56735
|
+
(0, import_node_fs44.existsSync)((0, import_node_path41.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
56736
|
+
(0, import_node_fs44.existsSync)((0, import_node_path41.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
|
|
56362
56737
|
].filter(Boolean);
|
|
56363
56738
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
56364
56739
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -56380,8 +56755,8 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
56380
56755
|
}
|
|
56381
56756
|
|
|
56382
56757
|
// src/train-commands.ts
|
|
56383
|
-
var
|
|
56384
|
-
var
|
|
56758
|
+
var import_node_fs45 = require("node:fs");
|
|
56759
|
+
var import_node_path42 = require("node:path");
|
|
56385
56760
|
init_cli_shared();
|
|
56386
56761
|
init_clean_exit();
|
|
56387
56762
|
init_client_version();
|
|
@@ -56396,7 +56771,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
56396
56771
|
}
|
|
56397
56772
|
function readRepoVersion() {
|
|
56398
56773
|
try {
|
|
56399
|
-
return JSON.parse((0,
|
|
56774
|
+
return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
56400
56775
|
} catch {
|
|
56401
56776
|
return void 0;
|
|
56402
56777
|
}
|
|
@@ -56507,7 +56882,7 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
56507
56882
|
|
|
56508
56883
|
// src/suite-pin.ts
|
|
56509
56884
|
var import_promises6 = require("node:fs/promises");
|
|
56510
|
-
var
|
|
56885
|
+
var import_node_path43 = require("node:path");
|
|
56511
56886
|
var SuitePinError = class _SuitePinError extends Error {
|
|
56512
56887
|
diagnostic;
|
|
56513
56888
|
constructor(repo, tag, error) {
|
|
@@ -56575,8 +56950,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
56575
56950
|
if (input.carriedOnly) return { status: "unchanged" };
|
|
56576
56951
|
throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
56577
56952
|
}
|
|
56578
|
-
await (0, import_promises6.mkdir)((0,
|
|
56579
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
56953
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
56954
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56580
56955
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56581
56956
|
await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56582
56957
|
if (prs.length) {
|
|
@@ -56588,7 +56963,7 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
56588
56963
|
await run("git", ["switch", "-c", branch]);
|
|
56589
56964
|
}
|
|
56590
56965
|
for (const stage of ["rc", "main"]) {
|
|
56591
|
-
const path2 = (0,
|
|
56966
|
+
const path2 = (0, import_node_path43.join)(scratch, `${stage}-pins.json`);
|
|
56592
56967
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
56593
56968
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
56594
56969
|
}
|
|
@@ -56641,12 +57016,12 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
56641
57016
|
}
|
|
56642
57017
|
});
|
|
56643
57018
|
if (!todo.length) return void 0;
|
|
56644
|
-
await (0, import_promises6.mkdir)((0,
|
|
56645
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
57019
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
57020
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56646
57021
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56647
57022
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56648
57023
|
await run("git", ["switch", "-c", branch]);
|
|
56649
|
-
const path2 = (0,
|
|
57024
|
+
const path2 = (0, import_node_path43.join)(scratch, "main-pins.json");
|
|
56650
57025
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
|
|
56651
57026
|
await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
|
|
56652
57027
|
await run("git", ["add", "seed/modules.json"]);
|
|
@@ -56720,8 +57095,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
56720
57095
|
if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
|
|
56721
57096
|
const open2 = onBranch.find((p) => p.state === "OPEN");
|
|
56722
57097
|
if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
|
|
56723
|
-
await (0, import_promises6.mkdir)((0,
|
|
56724
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
57098
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
57099
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56725
57100
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56726
57101
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56727
57102
|
await run("git", ["switch", "-c", branch]);
|
|
@@ -56773,7 +57148,7 @@ function prTitleRefusal(title) {
|
|
|
56773
57148
|
|
|
56774
57149
|
// src/command-register-collaboration.ts
|
|
56775
57150
|
var import_node_child_process18 = require("node:child_process");
|
|
56776
|
-
var
|
|
57151
|
+
var import_node_fs54 = require("node:fs");
|
|
56777
57152
|
var import_promises8 = require("node:fs/promises");
|
|
56778
57153
|
init_clean_exit();
|
|
56779
57154
|
init_cli_shared();
|
|
@@ -56797,7 +57172,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
56797
57172
|
}
|
|
56798
57173
|
|
|
56799
57174
|
// src/command-register-collaboration.ts
|
|
56800
|
-
var
|
|
57175
|
+
var import_node_path50 = require("node:path");
|
|
56801
57176
|
init_house_map();
|
|
56802
57177
|
|
|
56803
57178
|
// src/issue-body.ts
|
|
@@ -56914,7 +57289,7 @@ init_github_client();
|
|
|
56914
57289
|
init_github_quota();
|
|
56915
57290
|
|
|
56916
57291
|
// src/issue-commands.ts
|
|
56917
|
-
var
|
|
57292
|
+
var import_node_fs46 = require("node:fs");
|
|
56918
57293
|
var import_node_crypto22 = require("node:crypto");
|
|
56919
57294
|
init_cli_shared();
|
|
56920
57295
|
init_clean_exit();
|
|
@@ -57107,7 +57482,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
57107
57482
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
57108
57483
|
const patch = {};
|
|
57109
57484
|
let bodyChanged = false;
|
|
57110
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
57485
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs46.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
57111
57486
|
if (options.titleFile !== void 0) {
|
|
57112
57487
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
57113
57488
|
} else if (options.title !== void 0) {
|
|
@@ -57774,7 +58149,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
57774
58149
|
if (opts.batch) {
|
|
57775
58150
|
let specs;
|
|
57776
58151
|
try {
|
|
57777
|
-
const raw = (0,
|
|
58152
|
+
const raw = (0, import_node_fs46.readFileSync)(opts.batch, "utf8");
|
|
57778
58153
|
specs = JSON.parse(raw);
|
|
57779
58154
|
if (!Array.isArray(specs)) {
|
|
57780
58155
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -57851,8 +58226,8 @@ ${lines2}`, {
|
|
|
57851
58226
|
}
|
|
57852
58227
|
|
|
57853
58228
|
// src/post-merge-recon.ts
|
|
57854
|
-
var
|
|
57855
|
-
var
|
|
58229
|
+
var import_node_fs47 = require("node:fs");
|
|
58230
|
+
var import_node_path44 = require("node:path");
|
|
57856
58231
|
|
|
57857
58232
|
// src/cross-repo-filing-issue.ts
|
|
57858
58233
|
init_github_client();
|
|
@@ -58022,16 +58397,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
58022
58397
|
}
|
|
58023
58398
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
58024
58399
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
58025
|
-
(0,
|
|
58026
|
-
(0,
|
|
58400
|
+
(0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(path2), { recursive: true });
|
|
58401
|
+
(0, import_node_fs47.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
58027
58402
|
`, "utf8");
|
|
58028
58403
|
return path2;
|
|
58029
58404
|
}
|
|
58030
58405
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
58031
58406
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
58032
|
-
if (!(0,
|
|
58407
|
+
if (!(0, import_node_fs47.existsSync)(path2)) return;
|
|
58033
58408
|
try {
|
|
58034
|
-
(0,
|
|
58409
|
+
(0, import_node_fs47.unlinkSync)(path2);
|
|
58035
58410
|
} catch {
|
|
58036
58411
|
}
|
|
58037
58412
|
}
|
|
@@ -58066,7 +58441,7 @@ var import_promises7 = require("node:fs/promises");
|
|
|
58066
58441
|
init_cli_shared();
|
|
58067
58442
|
|
|
58068
58443
|
// src/api-read.ts
|
|
58069
|
-
var
|
|
58444
|
+
var import_node_fs48 = require("node:fs");
|
|
58070
58445
|
init_cli_shared();
|
|
58071
58446
|
var API_ACCEPT_HEADERS = {
|
|
58072
58447
|
json: "application/vnd.github+json",
|
|
@@ -58331,7 +58706,7 @@ function registerApiReadCommands(program3, overrides) {
|
|
|
58331
58706
|
if (req.workflowRuns) body = sliceWorkflowRunsPage(body, req.workflowRuns);
|
|
58332
58707
|
if (o.jq !== void 0) body = await jqRun(o.jq, body, localJqEnv(), ...bindings.length ? [bindings] : []);
|
|
58333
58708
|
if (zip) {
|
|
58334
|
-
(0,
|
|
58709
|
+
(0, import_node_fs48.writeFileSync)(o.out, body, "latin1");
|
|
58335
58710
|
return;
|
|
58336
58711
|
}
|
|
58337
58712
|
} catch (e) {
|
|
@@ -59872,9 +60247,9 @@ function registerPrLifecycleCommands(program3) {
|
|
|
59872
60247
|
|
|
59873
60248
|
// src/review-verdict.ts
|
|
59874
60249
|
var import_node_child_process15 = require("node:child_process");
|
|
59875
|
-
var
|
|
60250
|
+
var import_node_fs49 = require("node:fs");
|
|
59876
60251
|
var import_node_os24 = require("node:os");
|
|
59877
|
-
var
|
|
60252
|
+
var import_node_path45 = require("node:path");
|
|
59878
60253
|
init_cli_shared();
|
|
59879
60254
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
59880
60255
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -59970,7 +60345,7 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
59970
60345
|
}
|
|
59971
60346
|
}
|
|
59972
60347
|
function computePrPatchId(number, repo) {
|
|
59973
|
-
return new Promise((
|
|
60348
|
+
return new Promise((resolve8, reject2) => {
|
|
59974
60349
|
const gh2 = (0, import_node_child_process15.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
59975
60350
|
const git3 = (0, import_node_child_process15.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
59976
60351
|
let out = "";
|
|
@@ -60008,7 +60383,7 @@ function computePrPatchId(number, repo) {
|
|
|
60008
60383
|
if (gitCode !== 0) return reject2(new Error(`git patch-id --stable exited ${gitCode}: ${gitErr.trim()}`));
|
|
60009
60384
|
const id = out.trim().split(/\s+/)[0];
|
|
60010
60385
|
if (!/^[0-9a-f]{40}$/.test(id ?? "")) return reject2(new Error(`patch-id: PR #${number} diff is empty or unreadable (gh: ${ghErr.trim() || "no stderr"})`));
|
|
60011
|
-
|
|
60386
|
+
resolve8(id);
|
|
60012
60387
|
};
|
|
60013
60388
|
gh2.on("close", (code) => {
|
|
60014
60389
|
ghCode = code;
|
|
@@ -60039,15 +60414,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
60039
60414
|
return comments;
|
|
60040
60415
|
}
|
|
60041
60416
|
async function postPrCommentFromFile(number, repo, body) {
|
|
60042
|
-
const dir = (0,
|
|
60043
|
-
const path2 = (0,
|
|
60417
|
+
const dir = (0, import_node_fs49.mkdtempSync)((0, import_node_path45.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
|
|
60418
|
+
const path2 = (0, import_node_path45.join)(dir, "body.md");
|
|
60044
60419
|
try {
|
|
60045
|
-
(0,
|
|
60420
|
+
(0, import_node_fs49.writeFileSync)(path2, body, "utf8");
|
|
60046
60421
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
60047
60422
|
return stdout.trim();
|
|
60048
60423
|
} finally {
|
|
60049
60424
|
try {
|
|
60050
|
-
(0,
|
|
60425
|
+
(0, import_node_fs49.rmSync)(dir, { recursive: true, force: true });
|
|
60051
60426
|
} catch {
|
|
60052
60427
|
}
|
|
60053
60428
|
}
|
|
@@ -60202,7 +60577,7 @@ init_cli_shared();
|
|
|
60202
60577
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
60203
60578
|
function catFileBatch(root, ref, paths) {
|
|
60204
60579
|
if (paths.length === 0) return Promise.resolve([]);
|
|
60205
|
-
return new Promise((
|
|
60580
|
+
return new Promise((resolve8) => {
|
|
60206
60581
|
const chunks = [];
|
|
60207
60582
|
let settled2 = false;
|
|
60208
60583
|
const child2 = (0, import_node_child_process16.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
@@ -60210,7 +60585,7 @@ function catFileBatch(root, ref, paths) {
|
|
|
60210
60585
|
if (settled2) return;
|
|
60211
60586
|
settled2 = true;
|
|
60212
60587
|
clearTimeout(timer);
|
|
60213
|
-
|
|
60588
|
+
resolve8(ok ? parseCatFileBatch(Buffer.concat(chunks), paths.length) : []);
|
|
60214
60589
|
};
|
|
60215
60590
|
const timer = setTimeout(() => {
|
|
60216
60591
|
child2.kill("SIGKILL");
|
|
@@ -60460,18 +60835,18 @@ function createPreparationDeps(input, io = {}) {
|
|
|
60460
60835
|
}
|
|
60461
60836
|
|
|
60462
60837
|
// src/worktree-residue-sweep.ts
|
|
60463
|
-
var
|
|
60464
|
-
var
|
|
60838
|
+
var import_node_fs53 = require("node:fs");
|
|
60839
|
+
var import_node_path49 = require("node:path");
|
|
60465
60840
|
|
|
60466
60841
|
// src/worktree-merge-cleanup.ts
|
|
60467
|
-
var
|
|
60468
|
-
var
|
|
60842
|
+
var import_node_fs52 = require("node:fs");
|
|
60843
|
+
var import_node_path48 = require("node:path");
|
|
60469
60844
|
init_cli_shared();
|
|
60470
60845
|
|
|
60471
60846
|
// src/stage-runner.ts
|
|
60472
60847
|
var import_node_child_process17 = require("node:child_process");
|
|
60473
|
-
var
|
|
60474
|
-
var
|
|
60848
|
+
var import_node_fs51 = require("node:fs");
|
|
60849
|
+
var import_node_path47 = require("node:path");
|
|
60475
60850
|
var import_node_net = require("node:net");
|
|
60476
60851
|
var import_node_util5 = require("node:util");
|
|
60477
60852
|
|
|
@@ -60481,8 +60856,8 @@ function normalizeEol2(s) {
|
|
|
60481
60856
|
}
|
|
60482
60857
|
|
|
60483
60858
|
// src/env-detection.ts
|
|
60484
|
-
var
|
|
60485
|
-
var
|
|
60859
|
+
var import_node_fs50 = require("node:fs");
|
|
60860
|
+
var import_node_path46 = require("node:path");
|
|
60486
60861
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
60487
60862
|
function isEnvBaseName(name) {
|
|
60488
60863
|
return name.toLowerCase() === ".env";
|
|
@@ -60492,7 +60867,7 @@ function findEnvFiles(root) {
|
|
|
60492
60867
|
const walk2 = (dir, rel) => {
|
|
60493
60868
|
let entries;
|
|
60494
60869
|
try {
|
|
60495
|
-
entries = (0,
|
|
60870
|
+
entries = (0, import_node_fs50.readdirSync)(dir, { withFileTypes: true });
|
|
60496
60871
|
} catch {
|
|
60497
60872
|
return;
|
|
60498
60873
|
}
|
|
@@ -60500,13 +60875,13 @@ function findEnvFiles(root) {
|
|
|
60500
60875
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
60501
60876
|
if (entry.isDirectory()) {
|
|
60502
60877
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
60503
|
-
walk2((0,
|
|
60878
|
+
walk2((0, import_node_path46.join)(dir, entry.name), relPath);
|
|
60504
60879
|
continue;
|
|
60505
60880
|
}
|
|
60506
60881
|
if (isEnvBaseName(entry.name)) {
|
|
60507
60882
|
let isDir = false;
|
|
60508
60883
|
try {
|
|
60509
|
-
isDir = (0,
|
|
60884
|
+
isDir = (0, import_node_fs50.lstatSync)((0, import_node_path46.join)(dir, entry.name)).isDirectory();
|
|
60510
60885
|
} catch {
|
|
60511
60886
|
}
|
|
60512
60887
|
if (!isDir) found.push(relPath);
|
|
@@ -60645,12 +61020,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
60645
61020
|
function stageBash() {
|
|
60646
61021
|
if (process.platform !== "win32") return "bash";
|
|
60647
61022
|
const configured = process.env.SHELL;
|
|
60648
|
-
if (configured && (0,
|
|
61023
|
+
if (configured && (0, import_node_path47.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs51.existsSync)(configured)) return configured;
|
|
60649
61024
|
const candidates = [
|
|
60650
|
-
(0,
|
|
60651
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
61025
|
+
(0, import_node_path47.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
61026
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path47.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
60652
61027
|
];
|
|
60653
|
-
const bash = candidates.find((path2) => (0,
|
|
61028
|
+
const bash = candidates.find((path2) => (0, import_node_fs51.existsSync)(path2));
|
|
60654
61029
|
if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
|
|
60655
61030
|
return bash;
|
|
60656
61031
|
}
|
|
@@ -60671,7 +61046,7 @@ function healthPollIntervalMs() {
|
|
|
60671
61046
|
return HEALTH_POLL_INTERVAL_MS;
|
|
60672
61047
|
}
|
|
60673
61048
|
function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
60674
|
-
return new Promise((
|
|
61049
|
+
return new Promise((resolve8, reject2) => {
|
|
60675
61050
|
let settled2 = false;
|
|
60676
61051
|
const finish = (fn) => {
|
|
60677
61052
|
if (settled2) return;
|
|
@@ -60681,7 +61056,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
|
60681
61056
|
child2.removeAllListeners("exit");
|
|
60682
61057
|
fn();
|
|
60683
61058
|
};
|
|
60684
|
-
const timer = setTimeout(() => finish(
|
|
61059
|
+
const timer = setTimeout(() => finish(resolve8), graceMs);
|
|
60685
61060
|
child2.on("error", (err) => finish(() => reject2(new Error(`stage process failed to start: ${err.message}`))));
|
|
60686
61061
|
child2.on("exit", (code, signal) => {
|
|
60687
61062
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -60788,7 +61163,7 @@ function appendForceRecreate(up) {
|
|
|
60788
61163
|
return `${up.trimEnd()} --force-recreate`;
|
|
60789
61164
|
}
|
|
60790
61165
|
function stageStatePath(cwd = process.cwd()) {
|
|
60791
|
-
return (0,
|
|
61166
|
+
return (0, import_node_path47.join)(cwd, "tmp", "stage", "state.json");
|
|
60792
61167
|
}
|
|
60793
61168
|
function normPath(path2) {
|
|
60794
61169
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -60874,10 +61249,10 @@ function pickStagePort(range, isFree) {
|
|
|
60874
61249
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
60875
61250
|
}
|
|
60876
61251
|
function isPortFree(port) {
|
|
60877
|
-
return new Promise((
|
|
61252
|
+
return new Promise((resolve8) => {
|
|
60878
61253
|
const srv = (0, import_node_net.createServer)();
|
|
60879
|
-
srv.once("error", () =>
|
|
60880
|
-
srv.once("listening", () => srv.close(() =>
|
|
61254
|
+
srv.once("error", () => resolve8(false));
|
|
61255
|
+
srv.once("listening", () => srv.close(() => resolve8(true)));
|
|
60881
61256
|
srv.listen(port, "127.0.0.1");
|
|
60882
61257
|
});
|
|
60883
61258
|
}
|
|
@@ -61005,8 +61380,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
61005
61380
|
}
|
|
61006
61381
|
function composeResolvesPort(cwd) {
|
|
61007
61382
|
if (process.env.PORT) return true;
|
|
61008
|
-
const envFile = (0,
|
|
61009
|
-
return (0,
|
|
61383
|
+
const envFile = (0, import_node_path47.join)(cwd, ".env");
|
|
61384
|
+
return (0, import_node_fs51.existsSync)(envFile) && envFileKeys((0, import_node_fs51.readFileSync)(envFile, "utf8")).has("PORT");
|
|
61010
61385
|
}
|
|
61011
61386
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
61012
61387
|
const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
|
|
@@ -61021,13 +61396,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
61021
61396
|
function stageComposeFiles(cwd) {
|
|
61022
61397
|
const files = ["docker-compose.yml"];
|
|
61023
61398
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
61024
|
-
if ((0,
|
|
61399
|
+
if ((0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, conventional))) files.push(conventional);
|
|
61025
61400
|
}
|
|
61026
61401
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
61027
61402
|
}
|
|
61028
61403
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
61029
61404
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
61030
|
-
if (!keys.length || !(0,
|
|
61405
|
+
if (!keys.length || !(0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, "docker-compose.yml"))) return {};
|
|
61031
61406
|
let configJson;
|
|
61032
61407
|
try {
|
|
61033
61408
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -61043,21 +61418,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
61043
61418
|
}
|
|
61044
61419
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
|
|
61045
61420
|
if (!body) return {};
|
|
61046
|
-
const overridePath = (0,
|
|
61047
|
-
(0,
|
|
61048
|
-
(0,
|
|
61421
|
+
const overridePath = (0, import_node_path47.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
61422
|
+
(0, import_node_fs51.mkdirSync)((0, import_node_path47.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
61423
|
+
(0, import_node_fs51.writeFileSync)(overridePath, body, "utf8");
|
|
61049
61424
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
61050
61425
|
}
|
|
61051
61426
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
61052
61427
|
if (!config.ensureEnv) return;
|
|
61053
|
-
const target = (0,
|
|
61054
|
-
const example = (0,
|
|
61055
|
-
if (!(0,
|
|
61056
|
-
(0,
|
|
61057
|
-
} else if ((0,
|
|
61058
|
-
const stale = detectStaleEnvFile((0,
|
|
61059
|
-
exampleMtimeMs: (0,
|
|
61060
|
-
targetMtimeMs: (0,
|
|
61428
|
+
const target = (0, import_node_path47.join)(cwd, config.ensureEnv.target);
|
|
61429
|
+
const example = (0, import_node_path47.join)(cwd, config.ensureEnv.example);
|
|
61430
|
+
if (!(0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
61431
|
+
(0, import_node_fs51.copyFileSync)(example, target);
|
|
61432
|
+
} else if ((0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
61433
|
+
const stale = detectStaleEnvFile((0, import_node_fs51.readFileSync)(example, "utf8"), (0, import_node_fs51.readFileSync)(target, "utf8"), {
|
|
61434
|
+
exampleMtimeMs: (0, import_node_fs51.statSync)(example).mtimeMs,
|
|
61435
|
+
targetMtimeMs: (0, import_node_fs51.statSync)(target).mtimeMs
|
|
61061
61436
|
});
|
|
61062
61437
|
if (stale) {
|
|
61063
61438
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running mmi-cli stage`;
|
|
@@ -61065,8 +61440,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
61065
61440
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
61066
61441
|
}
|
|
61067
61442
|
}
|
|
61068
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
61069
|
-
(0,
|
|
61443
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs51.existsSync)(target)) {
|
|
61444
|
+
(0, import_node_fs51.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs51.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
61070
61445
|
}
|
|
61071
61446
|
}
|
|
61072
61447
|
async function gitText(cwd, args) {
|
|
@@ -61096,20 +61471,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
61096
61471
|
return void 0;
|
|
61097
61472
|
}
|
|
61098
61473
|
function readState(path2) {
|
|
61099
|
-
if (!(0,
|
|
61474
|
+
if (!(0, import_node_fs51.existsSync)(path2)) return null;
|
|
61100
61475
|
try {
|
|
61101
|
-
return JSON.parse((0,
|
|
61476
|
+
return JSON.parse((0, import_node_fs51.readFileSync)(path2, "utf8"));
|
|
61102
61477
|
} catch {
|
|
61103
61478
|
return null;
|
|
61104
61479
|
}
|
|
61105
61480
|
}
|
|
61106
61481
|
function mkdirFor(path2) {
|
|
61107
61482
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
61108
|
-
(0,
|
|
61483
|
+
(0, import_node_fs51.mkdirSync)(dir, { recursive: true });
|
|
61109
61484
|
}
|
|
61110
61485
|
function writeState(path2, state) {
|
|
61111
61486
|
mkdirFor(path2);
|
|
61112
|
-
(0,
|
|
61487
|
+
(0, import_node_fs51.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
61113
61488
|
}
|
|
61114
61489
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
61115
61490
|
const reservation = {
|
|
@@ -61138,7 +61513,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
61138
61513
|
);
|
|
61139
61514
|
}
|
|
61140
61515
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
61141
|
-
(0,
|
|
61516
|
+
(0, import_node_fs51.rmSync)(path2, { force: true });
|
|
61142
61517
|
}
|
|
61143
61518
|
}
|
|
61144
61519
|
async function killTree(pid) {
|
|
@@ -61155,7 +61530,7 @@ async function killTree(pid) {
|
|
|
61155
61530
|
} catch {
|
|
61156
61531
|
}
|
|
61157
61532
|
}
|
|
61158
|
-
await new Promise((
|
|
61533
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
61159
61534
|
try {
|
|
61160
61535
|
process.kill(-pid, "SIGKILL");
|
|
61161
61536
|
} catch {
|
|
@@ -61228,7 +61603,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
61228
61603
|
} catch (e) {
|
|
61229
61604
|
last = e.message;
|
|
61230
61605
|
}
|
|
61231
|
-
await new Promise((
|
|
61606
|
+
await new Promise((resolve8) => setTimeout(resolve8, healthPollIntervalMs()));
|
|
61232
61607
|
}
|
|
61233
61608
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
61234
61609
|
}
|
|
@@ -61385,8 +61760,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
61385
61760
|
});
|
|
61386
61761
|
}
|
|
61387
61762
|
} catch (e) {
|
|
61388
|
-
(0,
|
|
61389
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
61763
|
+
(0, import_node_fs51.rmSync)(statePath, { force: true });
|
|
61764
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs51.rmSync)(globalStatePath, { force: true });
|
|
61390
61765
|
throw e;
|
|
61391
61766
|
}
|
|
61392
61767
|
const started = await startStage(config, {
|
|
@@ -61405,9 +61780,9 @@ function normPath2(p) {
|
|
|
61405
61780
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
61406
61781
|
}
|
|
61407
61782
|
function unlinkNodeModulesJunction(wtPath) {
|
|
61408
|
-
const nm = (0,
|
|
61783
|
+
const nm = (0, import_node_path48.join)(wtPath, "node_modules");
|
|
61409
61784
|
try {
|
|
61410
|
-
if ((0,
|
|
61785
|
+
if ((0, import_node_fs52.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs52.rmdirSync)(nm);
|
|
61411
61786
|
return { ok: true };
|
|
61412
61787
|
} catch (e) {
|
|
61413
61788
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -61415,11 +61790,11 @@ function unlinkNodeModulesJunction(wtPath) {
|
|
|
61415
61790
|
}
|
|
61416
61791
|
}
|
|
61417
61792
|
function defaultSleep2(ms) {
|
|
61418
|
-
return new Promise((
|
|
61793
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
61419
61794
|
}
|
|
61420
61795
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
61421
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
61422
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
61796
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs52.rmdirSync, exists = import_node_fs52.existsSync } = options;
|
|
61797
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs52.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
61423
61798
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
61424
61799
|
if (!junction.ok) return junction;
|
|
61425
61800
|
try {
|
|
@@ -61542,28 +61917,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
61542
61917
|
}
|
|
61543
61918
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
61544
61919
|
const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
|
|
61545
|
-
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0,
|
|
61920
|
+
return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path48.join)(wtPath, line.slice(0, -1)));
|
|
61546
61921
|
}
|
|
61547
61922
|
function safeRemoveTree(path2) {
|
|
61548
|
-
const stat4 = (0,
|
|
61923
|
+
const stat4 = (0, import_node_fs52.lstatSync)(path2);
|
|
61549
61924
|
if (stat4.isSymbolicLink()) {
|
|
61550
61925
|
try {
|
|
61551
|
-
(0,
|
|
61926
|
+
(0, import_node_fs52.rmdirSync)(path2);
|
|
61552
61927
|
} catch {
|
|
61553
|
-
(0,
|
|
61928
|
+
(0, import_node_fs52.unlinkSync)(path2);
|
|
61554
61929
|
}
|
|
61555
61930
|
return;
|
|
61556
61931
|
}
|
|
61557
61932
|
if (stat4.isDirectory()) {
|
|
61558
|
-
for (const entry of (0,
|
|
61559
|
-
(0,
|
|
61933
|
+
for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0, import_node_path48.join)(path2, entry));
|
|
61934
|
+
(0, import_node_fs52.rmdirSync)(path2);
|
|
61560
61935
|
return;
|
|
61561
61936
|
}
|
|
61562
|
-
(0,
|
|
61937
|
+
(0, import_node_fs52.unlinkSync)(path2);
|
|
61563
61938
|
}
|
|
61564
61939
|
async function removeWorktreeNodeModules(wtPath) {
|
|
61565
61940
|
try {
|
|
61566
|
-
safeRemoveTree((0,
|
|
61941
|
+
safeRemoveTree((0, import_node_path48.join)(wtPath, "node_modules"));
|
|
61567
61942
|
return { ok: true };
|
|
61568
61943
|
} catch (e) {
|
|
61569
61944
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -61575,7 +61950,7 @@ function errorMessage(e) {
|
|
|
61575
61950
|
}
|
|
61576
61951
|
function resolvedOrRaw(path2) {
|
|
61577
61952
|
try {
|
|
61578
|
-
return normPath2((0,
|
|
61953
|
+
return normPath2((0, import_node_fs52.realpathSync)(path2));
|
|
61579
61954
|
} catch {
|
|
61580
61955
|
return normPath2(path2);
|
|
61581
61956
|
}
|
|
@@ -61583,10 +61958,10 @@ function resolvedOrRaw(path2) {
|
|
|
61583
61958
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
61584
61959
|
let realRoot;
|
|
61585
61960
|
try {
|
|
61586
|
-
if ((0,
|
|
61961
|
+
if ((0, import_node_fs52.lstatSync)(root).isSymbolicLink()) {
|
|
61587
61962
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
61588
61963
|
}
|
|
61589
|
-
realRoot = normPath2((0,
|
|
61964
|
+
realRoot = normPath2((0, import_node_fs52.realpathSync)(root));
|
|
61590
61965
|
} catch (e) {
|
|
61591
61966
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
61592
61967
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -61602,17 +61977,17 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
61602
61977
|
const dir = stack.pop();
|
|
61603
61978
|
let entries;
|
|
61604
61979
|
try {
|
|
61605
|
-
entries = (0,
|
|
61980
|
+
entries = (0, import_node_fs52.readdirSync)(dir, { withFileTypes: true });
|
|
61606
61981
|
} catch (e) {
|
|
61607
61982
|
if (e.code === "ENOENT") continue;
|
|
61608
61983
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
61609
61984
|
}
|
|
61610
61985
|
for (const entry of entries) {
|
|
61611
|
-
const child2 = (0,
|
|
61986
|
+
const child2 = (0, import_node_path48.join)(dir, entry.name);
|
|
61612
61987
|
if (entry.isSymbolicLink()) {
|
|
61613
61988
|
let target = "";
|
|
61614
61989
|
try {
|
|
61615
|
-
target = normPath2((0,
|
|
61990
|
+
target = normPath2((0, import_node_fs52.realpathSync)(child2));
|
|
61616
61991
|
} catch {
|
|
61617
61992
|
target = "";
|
|
61618
61993
|
}
|
|
@@ -61795,7 +62170,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61795
62170
|
return report;
|
|
61796
62171
|
}
|
|
61797
62172
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
61798
|
-
const pathExists = options.pathExists ??
|
|
62173
|
+
const pathExists = options.pathExists ?? import_node_fs52.existsSync;
|
|
61799
62174
|
let afterWorktrees = [];
|
|
61800
62175
|
try {
|
|
61801
62176
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -61935,7 +62310,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61935
62310
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
61936
62311
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
61937
62312
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
61938
|
-
if (pathExists((0,
|
|
62313
|
+
if (pathExists((0, import_node_path48.join)(wtPath, "node_modules"))) {
|
|
61939
62314
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
61940
62315
|
if (!nmRemoved.ok) {
|
|
61941
62316
|
report.worktree = {
|
|
@@ -62071,18 +62446,18 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
62071
62446
|
// src/worktree-residue-sweep.ts
|
|
62072
62447
|
var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
|
|
62073
62448
|
function canonicalWorktreeRoot(primary) {
|
|
62074
|
-
return (0,
|
|
62449
|
+
return (0, import_node_path49.join)((0, import_node_path49.dirname)(primary), "mmi-worktrees", (0, import_node_path49.basename)(primary));
|
|
62075
62450
|
}
|
|
62076
|
-
function
|
|
62451
|
+
function norm2(p) {
|
|
62077
62452
|
const s = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
62078
62453
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
62079
62454
|
}
|
|
62080
62455
|
function residueLedgerPath(gitCommonDir) {
|
|
62081
|
-
return (0,
|
|
62456
|
+
return (0, import_node_path49.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
62082
62457
|
}
|
|
62083
62458
|
function readPaths(file, deps) {
|
|
62084
62459
|
try {
|
|
62085
|
-
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0,
|
|
62460
|
+
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs53.readFileSync)(f, "utf8")))(file));
|
|
62086
62461
|
return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
|
|
62087
62462
|
} catch {
|
|
62088
62463
|
return [];
|
|
@@ -62091,8 +62466,8 @@ function readPaths(file, deps) {
|
|
|
62091
62466
|
function writePaths(file, paths, deps) {
|
|
62092
62467
|
try {
|
|
62093
62468
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
62094
|
-
(0,
|
|
62095
|
-
(0,
|
|
62469
|
+
(0, import_node_fs53.mkdirSync)((0, import_node_path49.dirname)(f), { recursive: true });
|
|
62470
|
+
(0, import_node_fs53.writeFileSync)(f, t, "utf8");
|
|
62096
62471
|
});
|
|
62097
62472
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
62098
62473
|
`);
|
|
@@ -62102,29 +62477,29 @@ function writePaths(file, paths, deps) {
|
|
|
62102
62477
|
function recordResidue(gitCommonDir, path2, deps = {}) {
|
|
62103
62478
|
const file = residueLedgerPath(gitCommonDir);
|
|
62104
62479
|
const paths = readPaths(file, deps);
|
|
62105
|
-
if (!paths.some((p) =>
|
|
62480
|
+
if (!paths.some((p) => norm2(p) === norm2(path2))) paths.push(path2);
|
|
62106
62481
|
writePaths(file, paths, deps);
|
|
62107
62482
|
}
|
|
62108
62483
|
async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
62109
|
-
const exists = deps.exists ??
|
|
62110
|
-
const readDir = deps.readDir ?? ((p) => (0,
|
|
62484
|
+
const exists = deps.exists ?? import_node_fs53.existsSync;
|
|
62485
|
+
const readDir = deps.readDir ?? ((p) => (0, import_node_fs53.readdirSync)(p));
|
|
62111
62486
|
const isDir = deps.isDir ?? ((p) => {
|
|
62112
62487
|
try {
|
|
62113
|
-
return (0,
|
|
62488
|
+
return (0, import_node_fs53.statSync)(p).isDirectory();
|
|
62114
62489
|
} catch {
|
|
62115
62490
|
return false;
|
|
62116
62491
|
}
|
|
62117
62492
|
});
|
|
62118
62493
|
const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
|
|
62119
|
-
const removeEmpty = deps.removeEmpty ?? ((p) => (0,
|
|
62494
|
+
const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs53.rmdirSync)(p));
|
|
62120
62495
|
const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
|
|
62121
|
-
const live = new Set(registered.map(
|
|
62122
|
-
const primary = registered[0] ?
|
|
62496
|
+
const live = new Set(registered.map(norm2));
|
|
62497
|
+
const primary = registered[0] ? norm2(registered[0]) : void 0;
|
|
62123
62498
|
const file = residueLedgerPath(gitCommonDir);
|
|
62124
62499
|
const recorded = readPaths(file, deps);
|
|
62125
62500
|
const remaining = [];
|
|
62126
62501
|
for (const path2 of recorded) {
|
|
62127
|
-
const key =
|
|
62502
|
+
const key = norm2(path2);
|
|
62128
62503
|
if (live.has(key) || key === primary) {
|
|
62129
62504
|
report.dropped.push(path2);
|
|
62130
62505
|
continue;
|
|
@@ -62141,9 +62516,9 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
62141
62516
|
}
|
|
62142
62517
|
}
|
|
62143
62518
|
if (recorded.length) writePaths(file, remaining, deps);
|
|
62144
|
-
const keptKeys = new Set(remaining.map(
|
|
62519
|
+
const keptKeys = new Set(remaining.map(norm2));
|
|
62145
62520
|
const root = registered[0] ? canonicalWorktreeRoot(registered[0]) : void 0;
|
|
62146
|
-
if (root && isDir(root) && !live.has(
|
|
62521
|
+
if (root && isDir(root) && !live.has(norm2(root))) {
|
|
62147
62522
|
report.root = root;
|
|
62148
62523
|
let children = [];
|
|
62149
62524
|
try {
|
|
@@ -62151,8 +62526,8 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
62151
62526
|
} catch {
|
|
62152
62527
|
}
|
|
62153
62528
|
for (const name of children) {
|
|
62154
|
-
const child2 = (0,
|
|
62155
|
-
const key =
|
|
62529
|
+
const child2 = (0, import_node_path49.join)(root, name);
|
|
62530
|
+
const key = norm2(child2);
|
|
62156
62531
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
62157
62532
|
let entries;
|
|
62158
62533
|
try {
|
|
@@ -62194,10 +62569,10 @@ function argvWantsJson2() {
|
|
|
62194
62569
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
62195
62570
|
}
|
|
62196
62571
|
function hubRoot() {
|
|
62197
|
-
const fromPkg = (0,
|
|
62572
|
+
const fromPkg = (0, import_node_path50.join)(__dirname, "..", "..");
|
|
62198
62573
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
62199
|
-
if ((0,
|
|
62200
|
-
if ((0,
|
|
62574
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(fromPkg, marker))) return fromPkg;
|
|
62575
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(process.cwd(), marker))) return process.cwd();
|
|
62201
62576
|
return null;
|
|
62202
62577
|
}
|
|
62203
62578
|
function ciAuditDeps() {
|
|
@@ -62209,8 +62584,8 @@ function ciAuditDeps() {
|
|
|
62209
62584
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
62210
62585
|
readSeedFile: (path2) => {
|
|
62211
62586
|
if (!root) return null;
|
|
62212
|
-
const fullPath = (0,
|
|
62213
|
-
return (0,
|
|
62587
|
+
const fullPath = (0, import_node_path50.join)(root, path2);
|
|
62588
|
+
return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
|
|
62214
62589
|
}
|
|
62215
62590
|
};
|
|
62216
62591
|
}
|
|
@@ -63077,11 +63452,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63077
63452
|
}
|
|
63078
63453
|
});
|
|
63079
63454
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
63080
|
-
const wfDir = (0,
|
|
63081
|
-
if (!(0,
|
|
63082
|
-
return (0,
|
|
63455
|
+
const wfDir = (0, import_node_path50.join)(cwd, ".github", "workflows");
|
|
63456
|
+
if (!(0, import_node_fs54.existsSync)(wfDir)) return [];
|
|
63457
|
+
return (0, import_node_fs54.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
63083
63458
|
try {
|
|
63084
|
-
return workflowReportsPrChecks((0,
|
|
63459
|
+
return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0, import_node_path50.join)(wfDir, name), "utf8"));
|
|
63085
63460
|
} catch {
|
|
63086
63461
|
return true;
|
|
63087
63462
|
}
|
|
@@ -63269,7 +63644,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63269
63644
|
// #5400: after grace, name "GitHub delivered zero runs" instead of burning the full budget as pending.
|
|
63270
63645
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr checks-wait", number, repo),
|
|
63271
63646
|
baseBranch,
|
|
63272
|
-
sleep: (ms) => new Promise((
|
|
63647
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63273
63648
|
log: (message2) => console.warn(message2),
|
|
63274
63649
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
63275
63650
|
// Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
|
|
@@ -63286,7 +63661,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63286
63661
|
jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
|
|
63287
63662
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
63288
63663
|
const verdict = o.verdict;
|
|
63289
|
-
const findings = o.findingsFile ? (0,
|
|
63664
|
+
const findings = o.findingsFile ? (0, import_node_fs54.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
63290
63665
|
let patch;
|
|
63291
63666
|
let head;
|
|
63292
63667
|
try {
|
|
@@ -63449,7 +63824,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63449
63824
|
// #5400: same zero-runs delivery probe as checks-wait — do not burn the land budget on silence.
|
|
63450
63825
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr land", prNumber, repo),
|
|
63451
63826
|
baseBranch: "development",
|
|
63452
|
-
sleep: (ms) => new Promise((
|
|
63827
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63453
63828
|
log: (message2) => console.warn(message2),
|
|
63454
63829
|
// `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
|
|
63455
63830
|
// 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
|
|
@@ -63513,7 +63888,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63513
63888
|
} else {
|
|
63514
63889
|
lastFailure2 = void 0;
|
|
63515
63890
|
}
|
|
63516
|
-
await new Promise((
|
|
63891
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_LAND_POLL_MS));
|
|
63517
63892
|
}
|
|
63518
63893
|
if (lastFailure2) {
|
|
63519
63894
|
throw new Error(
|
|
@@ -63564,7 +63939,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63564
63939
|
const cleanupMerged = async (r) => {
|
|
63565
63940
|
if (opts.preserveWorktree) return "cleanup skipped (--preserve-worktree)";
|
|
63566
63941
|
const ran = await execFileP(process.execPath, [process.argv[1], ...canonicalArgvFor(["pr", "merge", r.pr, "--repo", r.repo])], {
|
|
63567
|
-
cwd: (0,
|
|
63942
|
+
cwd: (0, import_node_fs54.existsSync)(cwd0) ? cwd0 : primaryRoot0,
|
|
63568
63943
|
timeout: GH_MUTATION_TIMEOUT_MS * 3
|
|
63569
63944
|
}).then((x) => ({ stdout: x.stdout, stderr: x.stderr }), (e) => ({ stdout: e.stdout ?? "", stderr: e.stderr ?? "" }));
|
|
63570
63945
|
const json = ran.stdout.split("\n").reverse().find((l) => l.startsWith("{"));
|
|
@@ -63635,7 +64010,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63635
64010
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
63636
64011
|
closingGuardInput,
|
|
63637
64012
|
method === "--squash",
|
|
63638
|
-
o.squashBodyFile ? (0,
|
|
64013
|
+
o.squashBodyFile ? (0, import_node_fs54.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
63639
64014
|
);
|
|
63640
64015
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
63641
64016
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -63667,7 +64042,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63667
64042
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
63668
64043
|
let foreignCheckout;
|
|
63669
64044
|
if (foreignCwd) {
|
|
63670
|
-
const sibling = (0,
|
|
64045
|
+
const sibling = (0, import_node_path50.join)((0, import_node_path50.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
63671
64046
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
63672
64047
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
63673
64048
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -63705,7 +64080,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63705
64080
|
checksDiagnosticCommand: `gh pr checks ${number} --repo ${repo}`,
|
|
63706
64081
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr merge --wait", number, repo),
|
|
63707
64082
|
baseBranch,
|
|
63708
|
-
sleep: (ms) => new Promise((
|
|
64083
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63709
64084
|
log: (message2) => console.warn(message2),
|
|
63710
64085
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
63711
64086
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -63786,7 +64161,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63786
64161
|
}
|
|
63787
64162
|
if (!repoForPostCleanup) throw e;
|
|
63788
64163
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
63789
|
-
const commitMessage = bodyFile ? (0,
|
|
64164
|
+
const commitMessage = bodyFile ? (0, import_node_fs54.readFileSync)(bodyFile, "utf8") : void 0;
|
|
63790
64165
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
63791
64166
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
63792
64167
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -63795,7 +64170,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63795
64170
|
}
|
|
63796
64171
|
if (!o.auto && mergeRejectedBaseBranchModified(message2)) {
|
|
63797
64172
|
console.warn(`pr merge: the base branch was modified by a concurrent merge \u2014 waiting ${PR_MERGE_BASE_RACE_RETRY_DELAY_MS / 1e3}s and retrying PR #${number}'s merge once (#6052).`);
|
|
63798
|
-
await new Promise((
|
|
64173
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
|
|
63799
64174
|
await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
63800
64175
|
const m2 = String(e2.message || "");
|
|
63801
64176
|
if (/already been merged/i.test(m2)) {
|
|
@@ -63901,7 +64276,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63901
64276
|
let residueSweep;
|
|
63902
64277
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
63903
64278
|
try {
|
|
63904
|
-
gitCommonDir = (0,
|
|
64279
|
+
gitCommonDir = (0, import_node_path50.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
63905
64280
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
63906
64281
|
} catch {
|
|
63907
64282
|
}
|
|
@@ -63919,7 +64294,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63919
64294
|
preserveWorktree: o.preserveWorktree,
|
|
63920
64295
|
gcAcknowledged: o.gc,
|
|
63921
64296
|
expectedHeadOid: headRefOid,
|
|
63922
|
-
pathExists: (p) => (0,
|
|
64297
|
+
pathExists: (p) => (0, import_node_fs54.existsSync)(p),
|
|
63923
64298
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
63924
64299
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
63925
64300
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -64060,7 +64435,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64060
64435
|
console.warn(`ci checks-wait: zero-runs probe FAILED (${read.error}) \u2014 not treating as delivery failure this cycle`);
|
|
64061
64436
|
return null;
|
|
64062
64437
|
},
|
|
64063
|
-
sleep: (ms) => new Promise((
|
|
64438
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
64064
64439
|
log: (message2) => console.warn(message2),
|
|
64065
64440
|
timeoutMs: timeoutMs ?? PR_CHECKS_TIMEOUT_MS,
|
|
64066
64441
|
progress: ({ state, elapsedMs, remainingMs, detail }) => console.warn(`ci checks-wait: ${detail && detail.startsWith("pending (") ? detail : state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -64197,8 +64572,14 @@ function trainApplyDeps() {
|
|
|
64197
64572
|
},
|
|
64198
64573
|
// Hub-App-authority dispatch of the central tenant deploy (#953) — the Hub fires the
|
|
64199
64574
|
// workflow_dispatch with its App token, so the caller needs no MMI-Hub Actions write.
|
|
64200
|
-
|
|
64201
|
-
|
|
64575
|
+
readDeployedSha: async (slug, stage) => (await fetchDeployFactsBySlug(slug, registryClientDeps(await loadConfig())))?.stages[stage]?.deployedTag,
|
|
64576
|
+
compareDeployRef: async (repo, base, head) => (await defaultGitHubClient().rest(
|
|
64577
|
+
"GET",
|
|
64578
|
+
`repos/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}?per_page=1`,
|
|
64579
|
+
{ timeoutMs: GH_TRAIN_TIMEOUT_MS }
|
|
64580
|
+
)).status,
|
|
64581
|
+
dispatchTenantDeploy: async ({ repo, stage, ref, rollback }) => {
|
|
64582
|
+
const res = await tenantDeploy({ repo, stage, ref, ...rollback ? { rollback } : {} }, registryClientDeps(await loadConfig()));
|
|
64202
64583
|
if (!res.ok) {
|
|
64203
64584
|
const detail = res.body?.error ?? res.error ?? `HTTP ${res.status}`;
|
|
64204
64585
|
throw new Error(`tenant deploy dispatch failed: ${detail}`);
|
|
@@ -64450,7 +64831,7 @@ function hotfixFollowUpLegs(runs2, foldPort, alignment, foldNote, deployNote) {
|
|
|
64450
64831
|
}
|
|
64451
64832
|
return legs;
|
|
64452
64833
|
}
|
|
64453
|
-
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((
|
|
64834
|
+
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))) {
|
|
64454
64835
|
for (let attempt = 0; ; attempt++) {
|
|
64455
64836
|
try {
|
|
64456
64837
|
return await cb(repo, true);
|
|
@@ -65185,7 +65566,7 @@ function rcRunCauseFromLog(log) {
|
|
|
65185
65566
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
65186
65567
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
65187
65568
|
}
|
|
65188
|
-
var suiteStateDir = (root) => (0,
|
|
65569
|
+
var suiteStateDir = (root) => (0, import_node_path52.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
65189
65570
|
async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
|
|
65190
65571
|
const deps = trainApplyDeps();
|
|
65191
65572
|
deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
|
|
@@ -65204,8 +65585,8 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
65204
65585
|
const meta = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
65205
65586
|
if (!meta.ok || meta.project?.releaseTrack !== "full" || meta.project.suite !== "mm-work")
|
|
65206
65587
|
throw new Error(`${repo}: first rc branch requires releaseTrack full and suite=mm-work in META; no branch created`);
|
|
65207
|
-
const
|
|
65208
|
-
if (
|
|
65588
|
+
const main2 = (await deps.run("git", ["rev-parse", "origin/main"])).trim();
|
|
65589
|
+
if (main2.toLowerCase() !== previousRc.toLowerCase()) throw new Error(`${repo}: main moved since the cut snapshot; no rc branch created`);
|
|
65209
65590
|
await deps.run("git", ["push", "origin", `${previousRc}:refs/heads/rc`]);
|
|
65210
65591
|
await deps.run("git", ["fetch", "origin", "rc"]);
|
|
65211
65592
|
rcNow = (await deps.run("git", ["rev-parse", "origin/rc"])).trim();
|
|
@@ -65232,16 +65613,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
65232
65613
|
}
|
|
65233
65614
|
}
|
|
65234
65615
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
65235
|
-
const recordPath = (0,
|
|
65236
|
-
const base = (0,
|
|
65616
|
+
const recordPath = (0, import_node_path52.join)(suiteStateDir(root), "cut.json");
|
|
65617
|
+
const base = (0, import_node_path52.join)(suiteStateDir(root), "checkouts");
|
|
65237
65618
|
const checkout = async (repo) => {
|
|
65238
65619
|
if (repo === SUITE_REPO) return root;
|
|
65239
|
-
const target = (0,
|
|
65240
|
-
if (!(0,
|
|
65241
|
-
(0,
|
|
65620
|
+
const target = (0, import_node_path52.join)(base, repo.split("/")[1]);
|
|
65621
|
+
if (!(0, import_node_fs55.existsSync)(target)) {
|
|
65622
|
+
(0, import_node_fs55.mkdirSync)(base, { recursive: true });
|
|
65242
65623
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
65243
65624
|
}
|
|
65244
|
-
if (!(0,
|
|
65625
|
+
if (!(0, import_node_fs55.existsSync)((0, import_node_path52.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
65245
65626
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
65246
65627
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
65247
65628
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -65305,7 +65686,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65305
65686
|
if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
|
|
65306
65687
|
const target = await checkout(repo);
|
|
65307
65688
|
return new Promise((resolveResult, reject2) => {
|
|
65308
|
-
const child2 = (0, import_node_child_process19.fork)((0,
|
|
65689
|
+
const child2 = (0, import_node_child_process19.fork)((0, import_node_path51.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
|
|
65309
65690
|
cwd: target,
|
|
65310
65691
|
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
65311
65692
|
execArgv: []
|
|
@@ -65377,7 +65758,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65377
65758
|
if (!reuse) await gh(["workflow", "run", "module-rc-publish.yml", "--repo", "mutmutco/MMI-Hub", ...fields.flatMap((f) => ["--field", f])]);
|
|
65378
65759
|
let candidate = reuse;
|
|
65379
65760
|
for (let i = 0; !candidate && i < 25; i++) {
|
|
65380
|
-
await new Promise((
|
|
65761
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
65381
65762
|
candidate = (await rcRuns()).find((r) => r.displayTitle === title && Date.parse(r.createdAt) >= since);
|
|
65382
65763
|
}
|
|
65383
65764
|
if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
|
|
@@ -65533,7 +65914,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65533
65914
|
for (let i = 0; ; i++) {
|
|
65534
65915
|
const pin = await openSuitePinPr({ root, run: (file, args, cwd) => exec(file, args, cwd, GIT_TIMEOUT_MS) }, { repo, tag, stage: "main" });
|
|
65535
65916
|
if (pin.status !== "pending" || i >= 40) return pin;
|
|
65536
|
-
await new Promise((
|
|
65917
|
+
await new Promise((resolve8) => setTimeout(resolve8, 3e4));
|
|
65537
65918
|
}
|
|
65538
65919
|
},
|
|
65539
65920
|
suiteMapping: async (leadTag) => {
|
|
@@ -65548,9 +65929,9 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65548
65929
|
const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
|
|
65549
65930
|
if (parseSuiteMapping(body)) return;
|
|
65550
65931
|
const dir = suiteStateDir(root);
|
|
65551
|
-
(0,
|
|
65552
|
-
const file = (0,
|
|
65553
|
-
(0,
|
|
65932
|
+
(0, import_node_fs55.mkdirSync)(dir, { recursive: true });
|
|
65933
|
+
const file = (0, import_node_path52.join)(dir, `notes-${leadTag}.md`);
|
|
65934
|
+
(0, import_node_fs55.writeFileSync)(file, `${body}
|
|
65554
65935
|
|
|
65555
65936
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
65556
65937
|
await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
|
|
@@ -65591,7 +65972,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
|
|
|
65591
65972
|
|
|
65592
65973
|
// src/suite-commands.ts
|
|
65593
65974
|
init_cli_shared();
|
|
65594
|
-
var
|
|
65975
|
+
var import_node_fs56 = require("node:fs");
|
|
65595
65976
|
function parseMemberSummaryFiles(entries) {
|
|
65596
65977
|
const memberSummaryFiles = {};
|
|
65597
65978
|
for (const entry of entries) {
|
|
@@ -65615,7 +65996,7 @@ function suiteRcDataProdDeps() {
|
|
|
65615
65996
|
const rc = facts.stages.rc;
|
|
65616
65997
|
return rc?.present ? rc.substrate ?? "an unnamed substrate" : null;
|
|
65617
65998
|
},
|
|
65618
|
-
sleep: (ms) => new Promise((
|
|
65999
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))
|
|
65619
66000
|
};
|
|
65620
66001
|
}
|
|
65621
66002
|
function registerSuiteCommands(program3) {
|
|
@@ -65685,7 +66066,7 @@ function registerSuiteCommands(program3) {
|
|
|
65685
66066
|
try {
|
|
65686
66067
|
const io = suiteTrainIo(root);
|
|
65687
66068
|
retired = await retireFailedLeadRcLedger({ ledgerPath: releaseLedgerPath(root), rcHead: () => io.head(SUITE_REPO, "rc").then((h) => h ?? void 0), markAbandoned: io.markAbandoned }, o.ownerGo === true);
|
|
65688
|
-
if (retired && !(0,
|
|
66069
|
+
if (retired && !(0, import_node_fs56.existsSync)(io.recordPath)) {
|
|
65689
66070
|
console.log(o.json ? JSON.stringify({ retiredLeadRc: retired }, null, 2) : `suite abandon: retired the lead's failed, never-promoted rc candidate ${retired}; marked abandoned on origin (#7854)`);
|
|
65690
66071
|
return;
|
|
65691
66072
|
}
|
|
@@ -65925,9 +66306,9 @@ function registerJevCommands(program3) {
|
|
|
65925
66306
|
|
|
65926
66307
|
// src/packages-adopt-export.ts
|
|
65927
66308
|
var import_node_child_process20 = require("node:child_process");
|
|
65928
|
-
var
|
|
66309
|
+
var import_node_fs57 = require("node:fs");
|
|
65929
66310
|
var import_node_os25 = require("node:os");
|
|
65930
|
-
var
|
|
66311
|
+
var import_node_path53 = require("node:path");
|
|
65931
66312
|
var import_node_util6 = require("node:util");
|
|
65932
66313
|
init_cli_shared();
|
|
65933
66314
|
|
|
@@ -66547,38 +66928,38 @@ function expectedFromRun(run, opts) {
|
|
|
66547
66928
|
}
|
|
66548
66929
|
async function adoptExport(opts, deps) {
|
|
66549
66930
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
66550
|
-
const packagePath = (0,
|
|
66551
|
-
const lockPath = (0,
|
|
66552
|
-
const packageText = (0,
|
|
66553
|
-
const lockText = (0,
|
|
66554
|
-
const work = (0,
|
|
66931
|
+
const packagePath = (0, import_node_path53.join)(opts.dir, "package.json");
|
|
66932
|
+
const lockPath = (0, import_node_path53.join)(opts.dir, "package-lock.json");
|
|
66933
|
+
const packageText = (0, import_node_fs57.readFileSync)(packagePath, "utf8");
|
|
66934
|
+
const lockText = (0, import_node_fs57.readFileSync)(lockPath, "utf8");
|
|
66935
|
+
const work = (0, import_node_fs57.mkdtempSync)((0, import_node_path53.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
|
|
66555
66936
|
try {
|
|
66556
66937
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
66557
|
-
(0,
|
|
66558
|
-
(0,
|
|
66559
|
-
(0,
|
|
66560
|
-
const script = (0,
|
|
66561
|
-
(0,
|
|
66938
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
66939
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
66940
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
66941
|
+
const script = (0, import_node_path53.join)(work, "package_export.py");
|
|
66942
|
+
(0, import_node_fs57.writeFileSync)(script, package_export_default);
|
|
66562
66943
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
66563
|
-
const verified = (0,
|
|
66564
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0,
|
|
66944
|
+
const verified = (0, import_node_path53.join)(work, "verified");
|
|
66945
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs57.readFileSync)((0, import_node_path53.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
66565
66946
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
66566
66947
|
for (const { name } of plan.result.adopted) {
|
|
66567
66948
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
66568
|
-
await deps.npm(["cache", "add", (0,
|
|
66949
|
+
await deps.npm(["cache", "add", (0, import_node_path53.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
66569
66950
|
}
|
|
66570
|
-
(0,
|
|
66571
|
-
(0,
|
|
66951
|
+
(0, import_node_fs57.writeFileSync)(packagePath, plan.packageText);
|
|
66952
|
+
(0, import_node_fs57.writeFileSync)(lockPath, plan.lockText);
|
|
66572
66953
|
try {
|
|
66573
66954
|
await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
|
|
66574
66955
|
} catch (error) {
|
|
66575
|
-
(0,
|
|
66576
|
-
(0,
|
|
66956
|
+
(0, import_node_fs57.writeFileSync)(packagePath, packageText);
|
|
66957
|
+
(0, import_node_fs57.writeFileSync)(lockPath, lockText);
|
|
66577
66958
|
throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
|
|
66578
66959
|
}
|
|
66579
66960
|
return plan.result;
|
|
66580
66961
|
} finally {
|
|
66581
|
-
(0,
|
|
66962
|
+
(0, import_node_fs57.rmSync)(work, { recursive: true, force: true });
|
|
66582
66963
|
}
|
|
66583
66964
|
}
|
|
66584
66965
|
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process20.execFile);
|
|
@@ -66682,12 +67063,12 @@ function registerPackagesCommands(program3) {
|
|
|
66682
67063
|
init_cli_shared();
|
|
66683
67064
|
|
|
66684
67065
|
// src/wave-status.ts
|
|
66685
|
-
var
|
|
67066
|
+
var import_node_fs58 = require("node:fs");
|
|
66686
67067
|
function observeStage(worktreePath) {
|
|
66687
67068
|
const statePath = stageStatePath(worktreePath);
|
|
66688
67069
|
let raw;
|
|
66689
67070
|
try {
|
|
66690
|
-
raw = (0,
|
|
67071
|
+
raw = (0, import_node_fs58.readFileSync)(statePath, "utf8");
|
|
66691
67072
|
} catch (error) {
|
|
66692
67073
|
if (error.code === "ENOENT") return { state: "ok", value: { running: false } };
|
|
66693
67074
|
return { state: "unavailable", reason: "stage state could not be read" };
|
|
@@ -66754,9 +67135,9 @@ async function collectWaveStatus(deps) {
|
|
|
66754
67135
|
// src/discovery-commands.ts
|
|
66755
67136
|
init_github_client();
|
|
66756
67137
|
init_cli_shared();
|
|
66757
|
-
var
|
|
67138
|
+
var import_node_fs59 = require("node:fs");
|
|
66758
67139
|
var import_node_os26 = require("node:os");
|
|
66759
|
-
var
|
|
67140
|
+
var import_node_path54 = require("node:path");
|
|
66760
67141
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
66761
67142
|
var defaultStatusDeps = {
|
|
66762
67143
|
repo: resolveRepo,
|
|
@@ -66977,8 +67358,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
66977
67358
|
}
|
|
66978
67359
|
const home = (0, import_node_os26.homedir)();
|
|
66979
67360
|
const plugin = onboardPluginGate({
|
|
66980
|
-
readKnown: () => readFileSyncSafe((0,
|
|
66981
|
-
readSettings: () => readFileSyncSafe((0,
|
|
67361
|
+
readKnown: () => readFileSyncSafe((0, import_node_path54.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs59.readFileSync),
|
|
67362
|
+
readSettings: () => readFileSyncSafe((0, import_node_path54.join)(home, ".claude", "settings.json"), import_node_fs59.readFileSync)
|
|
66982
67363
|
});
|
|
66983
67364
|
return {
|
|
66984
67365
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -68679,9 +69060,9 @@ function formatExplainGroup(cmd, rootName) {
|
|
|
68679
69060
|
lines2.push(...formatOwnDetail(cmd));
|
|
68680
69061
|
lines2.push("Commands:");
|
|
68681
69062
|
for (const child2 of cmd.subcommands) {
|
|
68682
|
-
const args = child2.arguments.map((
|
|
68683
|
-
const name =
|
|
68684
|
-
return
|
|
69063
|
+
const args = child2.arguments.map((arg2) => {
|
|
69064
|
+
const name = arg2.variadic ? `${arg2.name}...` : arg2.name;
|
|
69065
|
+
return arg2.required ? `<${name}>` : `[${name}]`;
|
|
68685
69066
|
}).join(" ");
|
|
68686
69067
|
lines2.push(` ${canonical(child2)}${args ? ` ${args}` : ""}${child2.description ? ` \u2014 ${child2.description}` : ""}`);
|
|
68687
69068
|
}
|
|
@@ -68702,11 +69083,11 @@ function briefCommand(cmd) {
|
|
|
68702
69083
|
return {
|
|
68703
69084
|
path: cmd.path,
|
|
68704
69085
|
...cmd.description ? { description: cmd.description } : {},
|
|
68705
|
-
arguments: cmd.arguments.map((
|
|
68706
|
-
name:
|
|
68707
|
-
required:
|
|
68708
|
-
...
|
|
68709
|
-
...
|
|
69086
|
+
arguments: cmd.arguments.map((arg2) => ({
|
|
69087
|
+
name: arg2.name,
|
|
69088
|
+
required: arg2.required,
|
|
69089
|
+
...arg2.variadic ? { variadic: true } : {},
|
|
69090
|
+
...arg2.description ? { description: arg2.description } : {}
|
|
68710
69091
|
})),
|
|
68711
69092
|
options: ownOptions(cmd).map((opt) => ({
|
|
68712
69093
|
flags: opt.flags,
|
|
@@ -68729,15 +69110,15 @@ function explainBriefManifest(manifest, command) {
|
|
|
68729
69110
|
}
|
|
68730
69111
|
function formatExplainBrief(cmd, rootName) {
|
|
68731
69112
|
const lines2 = [`${rootName} ${cmd.path}${cmd.description ? ` \u2014 ${cmd.description}` : ""}`];
|
|
68732
|
-
for (const
|
|
68733
|
-
const flag =
|
|
68734
|
-
lines2.push(` ${flag} ${
|
|
69113
|
+
for (const arg2 of cmd.arguments) {
|
|
69114
|
+
const flag = arg2.required ? `<${arg2.name}>` : `[${arg2.name}]`;
|
|
69115
|
+
lines2.push(` ${flag} ${arg2.required ? "required" : "optional"}${arg2.description ? ` ${arg2.description}` : ""}`);
|
|
68735
69116
|
}
|
|
68736
69117
|
for (const opt of ownOptions(cmd)) {
|
|
68737
69118
|
lines2.push(` ${opt.flags} ${opt.takesValue ? "takes-value" : "flag"}${opt.mandatory ? " required" : ""}${opt.description ? ` ${opt.description}` : ""}`);
|
|
68738
69119
|
}
|
|
68739
69120
|
for (const child2 of cmd.subcommands) {
|
|
68740
|
-
const args = child2.arguments.map((
|
|
69121
|
+
const args = child2.arguments.map((arg2) => arg2.required ? `<${arg2.name}>` : `[${arg2.name}]`).join(" ");
|
|
68741
69122
|
lines2.push(`${child2.path}${args ? ` ${args}` : ""}${child2.description ? ` \u2014 ${child2.description}` : ""}`);
|
|
68742
69123
|
for (const opt of ownOptions(child2)) {
|
|
68743
69124
|
lines2.push(` ${opt.flags} ${opt.takesValue ? "takes-value" : "flag"}${opt.mandatory ? " required" : ""}${opt.description ? ` ${opt.description}` : ""}`);
|
|
@@ -69644,8 +70025,8 @@ async function repoWorkflowEntries(client, repo) {
|
|
|
69644
70025
|
for (const wf of workflowsList) {
|
|
69645
70026
|
if (typeof wf?.path !== "string" || !wf.path) continue;
|
|
69646
70027
|
if (!wf.path.startsWith(".github/workflows/")) continue;
|
|
69647
|
-
const
|
|
69648
|
-
const name = `${repo}/${
|
|
70028
|
+
const basename6 = wf.path.split("/").pop() ?? wf.path;
|
|
70029
|
+
const name = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
|
|
69649
70030
|
if (wf.state !== "active") {
|
|
69650
70031
|
disabled.push(name);
|
|
69651
70032
|
continue;
|
|
@@ -70109,8 +70490,8 @@ function registerSessionReport(program3) {
|
|
|
70109
70490
|
}
|
|
70110
70491
|
|
|
70111
70492
|
// src/stage-commands.ts
|
|
70112
|
-
var
|
|
70113
|
-
var
|
|
70493
|
+
var import_node_fs60 = require("node:fs");
|
|
70494
|
+
var import_node_path55 = require("node:path");
|
|
70114
70495
|
init_cli_shared();
|
|
70115
70496
|
init_clean_exit();
|
|
70116
70497
|
|
|
@@ -70358,8 +70739,8 @@ function registerStageCommands(program3) {
|
|
|
70358
70739
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
70359
70740
|
const resolution = decideStage({
|
|
70360
70741
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
70361
|
-
hasCompose: (0,
|
|
70362
|
-
hasEnvExample: (0,
|
|
70742
|
+
hasCompose: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), "docker-compose.yml")),
|
|
70743
|
+
hasEnvExample: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), ".env.example")),
|
|
70363
70744
|
repo
|
|
70364
70745
|
});
|
|
70365
70746
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -70652,19 +71033,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
70652
71033
|
|
|
70653
71034
|
// src/tenant-artifact.ts
|
|
70654
71035
|
var import_node_crypto23 = require("node:crypto");
|
|
70655
|
-
var
|
|
71036
|
+
var import_node_fs61 = require("node:fs");
|
|
70656
71037
|
var import_promises11 = require("node:fs/promises");
|
|
70657
|
-
var
|
|
71038
|
+
var import_node_path56 = require("node:path");
|
|
70658
71039
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
70659
71040
|
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
70660
71041
|
async function sha256File(path2) {
|
|
70661
71042
|
const hash = (0, import_node_crypto23.createHash)("sha256");
|
|
70662
|
-
for await (const chunk of (0,
|
|
71043
|
+
for await (const chunk of (0, import_node_fs61.createReadStream)(path2)) hash.update(chunk);
|
|
70663
71044
|
return hash.digest("hex");
|
|
70664
71045
|
}
|
|
70665
71046
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
70666
71047
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
70667
|
-
const path2 = (0,
|
|
71048
|
+
const path2 = (0, import_node_path56.resolve)(inputPath);
|
|
70668
71049
|
const info = await (0, import_promises11.stat)(path2);
|
|
70669
71050
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
70670
71051
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
|
|
@@ -70682,7 +71063,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
70682
71063
|
return [key, value];
|
|
70683
71064
|
}));
|
|
70684
71065
|
headers["content-length"] = String(info.size);
|
|
70685
|
-
const stream = (0,
|
|
71066
|
+
const stream = (0, import_node_fs61.createReadStream)(path2);
|
|
70686
71067
|
let uploaded;
|
|
70687
71068
|
try {
|
|
70688
71069
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -71036,7 +71417,7 @@ function renderVerifySecrets(body) {
|
|
|
71036
71417
|
}
|
|
71037
71418
|
|
|
71038
71419
|
// src/command-register-developer.ts
|
|
71039
|
-
var
|
|
71420
|
+
var import_node_fs66 = require("node:fs");
|
|
71040
71421
|
init_clean_exit();
|
|
71041
71422
|
init_cli_shared();
|
|
71042
71423
|
init_error_codes();
|
|
@@ -71069,12 +71450,13 @@ async function resolveWhoami(deps) {
|
|
|
71069
71450
|
}
|
|
71070
71451
|
|
|
71071
71452
|
// src/command-register-developer.ts
|
|
71072
|
-
var
|
|
71453
|
+
var import_node_path63 = require("node:path");
|
|
71073
71454
|
|
|
71074
71455
|
// src/doc-refs-command-source.ts
|
|
71075
71456
|
var import_node_child_process21 = require("node:child_process");
|
|
71076
|
-
var
|
|
71077
|
-
var
|
|
71457
|
+
var import_node_fs62 = require("node:fs");
|
|
71458
|
+
var import_node_path57 = require("node:path");
|
|
71459
|
+
var IN_PROCESS_SOURCE = "in-process";
|
|
71078
71460
|
var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
|
|
71079
71461
|
var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
|
|
71080
71462
|
var flagsOf = (options) => new Set((options ?? []).flatMap((option) => String(option?.flags ?? "").match(/--[\w-]+/g) ?? []));
|
|
@@ -71095,7 +71477,7 @@ function inventoryFromManifest(manifest, source) {
|
|
|
71095
71477
|
function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
|
|
71096
71478
|
try {
|
|
71097
71479
|
const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
|
|
71098
|
-
cwd: (0,
|
|
71480
|
+
cwd: (0, import_node_path57.dirname)(bundle),
|
|
71099
71481
|
encoding: "utf8",
|
|
71100
71482
|
windowsHide: true,
|
|
71101
71483
|
timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
|
|
@@ -71110,20 +71492,29 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
|
|
|
71110
71492
|
}
|
|
71111
71493
|
function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
|
|
71112
71494
|
if (!argv1) return false;
|
|
71113
|
-
const
|
|
71114
|
-
const absolute = (0,
|
|
71495
|
+
const norm3 = (path2) => {
|
|
71496
|
+
const absolute = (0, import_node_path57.resolve)(path2);
|
|
71115
71497
|
return platform2 === "win32" ? absolute.toLowerCase() : absolute;
|
|
71116
71498
|
};
|
|
71117
|
-
const running =
|
|
71118
|
-
if (!["main.cjs", "index.cjs"].includes((0,
|
|
71119
|
-
return (0,
|
|
71499
|
+
const running = norm3(argv1);
|
|
71500
|
+
if (!["main.cjs", "index.cjs"].includes((0, import_node_path57.basename)(running))) return false;
|
|
71501
|
+
return (0, import_node_path57.dirname)(running) === (0, import_node_path57.dirname)(norm3(bundle));
|
|
71502
|
+
}
|
|
71503
|
+
function nameUnknownCommandSources(findings, source, root) {
|
|
71504
|
+
const checkoutBundle = (0, import_node_path57.resolve)(root, "cli", "dist", "main.cjs");
|
|
71505
|
+
const recovery = source !== IN_PROCESS_SOURCE && (0, import_node_path57.resolve)(source) === checkoutBundle ? "; rebuild with `npm --prefix cli run build` and retry, or pass `--commands-from <bundle>`" : "";
|
|
71506
|
+
for (const finding of findings) {
|
|
71507
|
+
if (finding.kind !== "unknown-command") continue;
|
|
71508
|
+
finding.commandSource = source;
|
|
71509
|
+
finding.detail += ` (source: ${source}${recovery})`;
|
|
71510
|
+
}
|
|
71120
71511
|
}
|
|
71121
71512
|
function resolveCommandInventory(deps) {
|
|
71122
|
-
const exists = deps.exists ??
|
|
71513
|
+
const exists = deps.exists ?? import_node_fs62.existsSync;
|
|
71123
71514
|
const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
|
|
71124
71515
|
const warnings = [];
|
|
71125
71516
|
if (deps.from) {
|
|
71126
|
-
const bundle = (0,
|
|
71517
|
+
const bundle = (0, import_node_path57.isAbsolute)(deps.from) ? deps.from : (0, import_node_path57.resolve)(deps.root, deps.from);
|
|
71127
71518
|
if (!exists(bundle)) {
|
|
71128
71519
|
throw new Error(`docs refs --commands-from: ${bundle} does not exist \u2014 name the CLI bundle to read commands from (usually cli/dist/main.cjs)`);
|
|
71129
71520
|
}
|
|
@@ -71133,10 +71524,13 @@ function resolveCommandInventory(deps) {
|
|
|
71133
71524
|
}
|
|
71134
71525
|
return { inventory: inventory2, warnings };
|
|
71135
71526
|
}
|
|
71136
|
-
const treeBundle = (0,
|
|
71137
|
-
if (!exists(treeBundle)
|
|
71527
|
+
const treeBundle = (0, import_node_path57.join)(deps.root, "cli", "dist", "main.cjs");
|
|
71528
|
+
if (!exists(treeBundle)) {
|
|
71138
71529
|
return { inventory: deps.inProcess(), warnings };
|
|
71139
71530
|
}
|
|
71531
|
+
if (isRunningBundle(treeBundle, deps.argv1, deps.platform)) {
|
|
71532
|
+
return { inventory: { ...deps.inProcess(), source: treeBundle }, warnings };
|
|
71533
|
+
}
|
|
71140
71534
|
const inventory = inventoryFromManifest(read(treeBundle), treeBundle);
|
|
71141
71535
|
if (!inventory) {
|
|
71142
71536
|
warnings.push({
|
|
@@ -71251,19 +71645,19 @@ async function executeWaveLand(plan, deps) {
|
|
|
71251
71645
|
// src/dist-drift.ts
|
|
71252
71646
|
var import_node_child_process22 = require("node:child_process");
|
|
71253
71647
|
var import_node_crypto25 = require("node:crypto");
|
|
71254
|
-
var
|
|
71648
|
+
var import_node_fs64 = require("node:fs");
|
|
71255
71649
|
var import_node_os27 = require("node:os");
|
|
71256
|
-
var
|
|
71650
|
+
var import_node_path59 = require("node:path");
|
|
71257
71651
|
|
|
71258
71652
|
// ../scripts/distribution-digest.mjs
|
|
71259
71653
|
var import_node_crypto24 = require("node:crypto");
|
|
71260
|
-
var
|
|
71261
|
-
var
|
|
71654
|
+
var import_node_fs63 = require("node:fs");
|
|
71655
|
+
var import_node_path58 = require("node:path");
|
|
71262
71656
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
71263
71657
|
function repoPath(root, declaredPath, label) {
|
|
71264
|
-
const absoluteRoot = (0,
|
|
71265
|
-
const target = (0,
|
|
71266
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
71658
|
+
const absoluteRoot = (0, import_node_path58.resolve)(root);
|
|
71659
|
+
const target = (0, import_node_path58.resolve)(root, declaredPath);
|
|
71660
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path58.sep}`)) {
|
|
71267
71661
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
71268
71662
|
}
|
|
71269
71663
|
return target;
|
|
@@ -71271,7 +71665,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
71271
71665
|
function digestFiles(files) {
|
|
71272
71666
|
const hash = (0, import_node_crypto24.createHash)("sha256");
|
|
71273
71667
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
71274
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
71668
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs63.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs63.readFileSync)(file.absolute);
|
|
71275
71669
|
hash.update(file.relative, "utf8");
|
|
71276
71670
|
hash.update("\0");
|
|
71277
71671
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -71286,8 +71680,8 @@ function digestFiles(files) {
|
|
|
71286
71680
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
71287
71681
|
return digestFiles(packedFiles.map((path2) => {
|
|
71288
71682
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
71289
|
-
if (!(0,
|
|
71290
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
71683
|
+
if (!(0, import_node_fs63.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
71684
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs63.lstatSync)(absolute) };
|
|
71291
71685
|
}));
|
|
71292
71686
|
}
|
|
71293
71687
|
|
|
@@ -71372,13 +71766,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
71372
71766
|
return lines2;
|
|
71373
71767
|
}
|
|
71374
71768
|
function readOrNull(path2) {
|
|
71375
|
-
return (0,
|
|
71769
|
+
return (0, import_node_fs64.existsSync)(path2) ? (0, import_node_fs64.readFileSync)(path2) : null;
|
|
71376
71770
|
}
|
|
71377
71771
|
function walkFiles(root) {
|
|
71378
71772
|
const files = [];
|
|
71379
71773
|
const walk2 = (directory) => {
|
|
71380
|
-
for (const entry of (0,
|
|
71381
|
-
const child2 = (0,
|
|
71774
|
+
for (const entry of (0, import_node_fs64.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
71775
|
+
const child2 = (0, import_node_path59.join)(directory, entry.name);
|
|
71382
71776
|
if (entry.isDirectory()) walk2(child2);
|
|
71383
71777
|
else files.push(child2);
|
|
71384
71778
|
}
|
|
@@ -71388,10 +71782,10 @@ function walkFiles(root) {
|
|
|
71388
71782
|
}
|
|
71389
71783
|
function bomPathFor(root) {
|
|
71390
71784
|
try {
|
|
71391
|
-
const registry2 = JSON.parse((0,
|
|
71392
|
-
return (0,
|
|
71785
|
+
const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0, import_node_path59.join)(root, "surfaces.json"), "utf8"));
|
|
71786
|
+
return (0, import_node_path59.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
71393
71787
|
} catch {
|
|
71394
|
-
return (0,
|
|
71788
|
+
return (0, import_node_path59.join)(root, "distribution-bom.json");
|
|
71395
71789
|
}
|
|
71396
71790
|
}
|
|
71397
71791
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -71404,40 +71798,40 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
71404
71798
|
});
|
|
71405
71799
|
}
|
|
71406
71800
|
function runDistStatus(root) {
|
|
71407
|
-
const stage = (0,
|
|
71801
|
+
const stage = (0, import_node_fs64.mkdtempSync)((0, import_node_path59.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
|
|
71408
71802
|
let overlayCount = 0;
|
|
71409
71803
|
try {
|
|
71410
|
-
const cliOut = (0,
|
|
71411
|
-
const hubOut = (0,
|
|
71412
|
-
rebuildTo((0,
|
|
71413
|
-
rebuildTo((0,
|
|
71804
|
+
const cliOut = (0, import_node_path59.join)(stage, "cli-dist");
|
|
71805
|
+
const hubOut = (0, import_node_path59.join)(stage, "hub-dist");
|
|
71806
|
+
rebuildTo((0, import_node_path59.join)(root, "cli"), cliOut);
|
|
71807
|
+
rebuildTo((0, import_node_path59.join)(root, "updater"), hubOut);
|
|
71414
71808
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
71415
71809
|
const rebuilt = (path2) => {
|
|
71416
71810
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
71417
|
-
return spec ? readOrNull((0,
|
|
71811
|
+
return spec ? readOrNull((0, import_node_path59.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
71418
71812
|
};
|
|
71419
|
-
const committed = (path2) => readOrNull((0,
|
|
71420
|
-
const tree = (path2) => readOrNull((0,
|
|
71421
|
-
const distRoot = (0,
|
|
71422
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
71423
|
-
const bom = JSON.parse((0,
|
|
71813
|
+
const committed = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
|
|
71814
|
+
const tree = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
|
|
71815
|
+
const distRoot = (0, import_node_path59.join)(root, "cli", "dist");
|
|
71816
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path59.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
71817
|
+
const bom = JSON.parse((0, import_node_fs64.readFileSync)(bomPathFor(root), "utf8"));
|
|
71424
71818
|
const digest = (entries) => {
|
|
71425
|
-
const overlay = (0,
|
|
71819
|
+
const overlay = (0, import_node_path59.join)(stage, `overlay-${overlayCount++}`);
|
|
71426
71820
|
for (const entry of entries) {
|
|
71427
|
-
const target = (0,
|
|
71428
|
-
(0,
|
|
71429
|
-
(0,
|
|
71821
|
+
const target = (0, import_node_path59.join)(overlay, entry.path);
|
|
71822
|
+
(0, import_node_fs64.mkdirSync)((0, import_node_path59.dirname)(target), { recursive: true });
|
|
71823
|
+
(0, import_node_fs64.writeFileSync)(target, entry.bytes);
|
|
71430
71824
|
}
|
|
71431
71825
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
71432
71826
|
};
|
|
71433
71827
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
71434
71828
|
} finally {
|
|
71435
|
-
(0,
|
|
71829
|
+
(0, import_node_fs64.rmSync)(stage, { recursive: true, force: true });
|
|
71436
71830
|
}
|
|
71437
71831
|
}
|
|
71438
71832
|
|
|
71439
71833
|
// src/doc-refs-live.ts
|
|
71440
|
-
var
|
|
71834
|
+
var import_node_path60 = require("node:path");
|
|
71441
71835
|
var concrete = (value) => !!value && !/[<>{}$]/.test(value);
|
|
71442
71836
|
var liveOption = (tokens2, flag) => {
|
|
71443
71837
|
const index = tokens2.indexOf(flag);
|
|
@@ -71510,7 +71904,7 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
71510
71904
|
const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
|
|
71511
71905
|
const read = deps.readFile ?? readFileOrNull;
|
|
71512
71906
|
const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
|
|
71513
|
-
const body = read((0,
|
|
71907
|
+
const body = read((0, import_node_path60.join)(root, doc));
|
|
71514
71908
|
return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
|
|
71515
71909
|
}));
|
|
71516
71910
|
const remote = await checkRunbookLive(docs, live);
|
|
@@ -71547,7 +71941,7 @@ function createRunbookLiveDeps(cliBin) {
|
|
|
71547
71941
|
};
|
|
71548
71942
|
const first = await probe().catch(() => 599);
|
|
71549
71943
|
if (first < 500) return first;
|
|
71550
|
-
await new Promise((
|
|
71944
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e4));
|
|
71551
71945
|
return probe();
|
|
71552
71946
|
}
|
|
71553
71947
|
};
|
|
@@ -71721,7 +72115,7 @@ async function diagnoseHub(deps) {
|
|
|
71721
72115
|
|
|
71722
72116
|
// src/schedules-lift-command.ts
|
|
71723
72117
|
var import_promises12 = require("node:fs/promises");
|
|
71724
|
-
var
|
|
72118
|
+
var import_node_path61 = require("node:path");
|
|
71725
72119
|
init_clean_exit();
|
|
71726
72120
|
init_cli_shared();
|
|
71727
72121
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -71750,7 +72144,7 @@ async function readWorkflowFiles(dir) {
|
|
|
71750
72144
|
const files = [];
|
|
71751
72145
|
for (const name of names.sort()) {
|
|
71752
72146
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
71753
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0,
|
|
72147
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path61.join)(dir, name), "utf8") });
|
|
71754
72148
|
}
|
|
71755
72149
|
return files;
|
|
71756
72150
|
}
|
|
@@ -71833,8 +72227,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
71833
72227
|
|
|
71834
72228
|
// src/spawn-policy-core.ts
|
|
71835
72229
|
var import_node_child_process24 = require("node:child_process");
|
|
71836
|
-
var
|
|
71837
|
-
var
|
|
72230
|
+
var import_node_fs65 = require("node:fs");
|
|
72231
|
+
var import_node_path62 = require("node:path");
|
|
71838
72232
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
71839
72233
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
71840
72234
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -71920,7 +72314,7 @@ function runSpawnPolicy(root) {
|
|
|
71920
72314
|
for (const file of files) {
|
|
71921
72315
|
let raw;
|
|
71922
72316
|
try {
|
|
71923
|
-
raw = (0,
|
|
72317
|
+
raw = (0, import_node_fs65.readFileSync)((0, import_node_path62.join)(root, file), "utf8");
|
|
71924
72318
|
} catch {
|
|
71925
72319
|
continue;
|
|
71926
72320
|
}
|
|
@@ -71940,19 +72334,19 @@ function runSpawnPolicy(root) {
|
|
|
71940
72334
|
function registerDeveloperCommands(program3) {
|
|
71941
72335
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
71942
72336
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
71943
|
-
const path2 = (0,
|
|
71944
|
-
const current = (0,
|
|
72337
|
+
const path2 = (0, import_node_path63.join)(process.cwd(), ".gitignore");
|
|
72338
|
+
const current = (0, import_node_fs66.existsSync)(path2) ? (0, import_node_fs66.readFileSync)(path2, "utf8") : null;
|
|
71945
72339
|
const plan = planManagedGitignore(current);
|
|
71946
72340
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
71947
72341
|
if (opts.json) {
|
|
71948
|
-
if (opts.write && plan.changed) (0,
|
|
72342
|
+
if (opts.write && plan.changed) (0, import_node_fs66.writeFileSync)(path2, plan.content, "utf8");
|
|
71949
72343
|
console.log(JSON.stringify(plan, null, 2));
|
|
71950
72344
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
71951
72345
|
return;
|
|
71952
72346
|
}
|
|
71953
72347
|
if (opts.write) {
|
|
71954
72348
|
if (plan.changed) {
|
|
71955
|
-
(0,
|
|
72349
|
+
(0, import_node_fs66.writeFileSync)(path2, plan.content, "utf8");
|
|
71956
72350
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
71957
72351
|
} else {
|
|
71958
72352
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -72148,12 +72542,13 @@ function registerDeveloperCommands(program3) {
|
|
|
72148
72542
|
const deps = { commandPaths, commandOptions, commandSource: source, base: o.base, runbooksOnly: o.runbooksOnly };
|
|
72149
72543
|
const result = o.live ? await runLiveDocRefs(root, deps, createRunbookLiveDeps(process.argv[1])) : runDocRefs(root, deps);
|
|
72150
72544
|
result.warnings.push(...resolved.warnings);
|
|
72545
|
+
nameUnknownCommandSources(result.findings, source, root);
|
|
72151
72546
|
if (o.json) {
|
|
72152
72547
|
consoleIo.log(JSON.stringify({ ok: result.ok, docCount: result.docCount, findings: result.findings, warnings: result.warnings }, null, 2));
|
|
72153
72548
|
if (!result.ok) process.exitCode = 1;
|
|
72154
72549
|
return;
|
|
72155
72550
|
}
|
|
72156
|
-
for (const w of result.warnings) console.warn(`WARNING: ${w
|
|
72551
|
+
for (const w of result.warnings) console.warn(`WARNING: ${formatDocRefFinding(w)}`);
|
|
72157
72552
|
if (result.ok) {
|
|
72158
72553
|
console.log(
|
|
72159
72554
|
`doc refs: ${result.docCount} docs, every reference resolves` + (result.warnings.length ? ` (${result.warnings.length} warning(s))` : "")
|
|
@@ -72163,7 +72558,7 @@ function registerDeveloperCommands(program3) {
|
|
|
72163
72558
|
const hasStale = result.findings.some((f) => f.kind === "runbook-section-stale" || f.kind === "seed-runbook-unreviewed");
|
|
72164
72559
|
const hasRefs = result.findings.some((f) => f.kind !== "runbook-section-stale" && f.kind !== "seed-runbook-unreviewed");
|
|
72165
72560
|
console.error(hasStale && !hasRefs ? "doc refs: stale runbook section(s)" : hasStale && hasRefs ? "doc refs: unresolved references and stale runbook section(s)" : "doc refs: unresolved references");
|
|
72166
|
-
for (const f of result.findings) console.error(`- ${f
|
|
72561
|
+
for (const f of result.findings) console.error(`- ${formatDocRefFinding(f)}`);
|
|
72167
72562
|
process.exitCode = 1;
|
|
72168
72563
|
} catch (e) {
|
|
72169
72564
|
await failGraceful(e.message);
|
|
@@ -72269,7 +72664,7 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
72269
72664
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
72270
72665
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
72271
72666
|
function envHealLockPath(home) {
|
|
72272
|
-
return (0,
|
|
72667
|
+
return (0, import_node_path64.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
72273
72668
|
}
|
|
72274
72669
|
async function withEnvHealLock(what, run) {
|
|
72275
72670
|
try {
|
|
@@ -72407,7 +72802,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72407
72802
|
);
|
|
72408
72803
|
const result = applyPluginCachePlan(
|
|
72409
72804
|
plan,
|
|
72410
|
-
(p) => (0,
|
|
72805
|
+
(p) => (0, import_node_fs67.rmSync)(p, { recursive: true }),
|
|
72411
72806
|
stagingApplyFsGuard(configRoot)
|
|
72412
72807
|
);
|
|
72413
72808
|
return {
|
|
@@ -72442,7 +72837,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72442
72837
|
// has no generated routing index would
|
|
72443
72838
|
// get a permanent — demanding an artifact it never asked for.
|
|
72444
72839
|
docsIndexState: (root) => {
|
|
72445
|
-
if (!(0,
|
|
72840
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
72446
72841
|
const real = createDocsIndexDeps(root);
|
|
72447
72842
|
let docs;
|
|
72448
72843
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -72451,7 +72846,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72451
72846
|
},
|
|
72452
72847
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
72453
72848
|
healDocsIndex: (root) => {
|
|
72454
|
-
if (!(0,
|
|
72849
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
72455
72850
|
const real = createDocsIndexDeps(root);
|
|
72456
72851
|
let docs;
|
|
72457
72852
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -72472,8 +72867,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72472
72867
|
});
|
|
72473
72868
|
const raced = await Promise.race([
|
|
72474
72869
|
work.then((r) => ({ ...r, timedOut: false })),
|
|
72475
|
-
new Promise((
|
|
72476
|
-
ceiling = setTimeout(() =>
|
|
72870
|
+
new Promise((resolve8) => {
|
|
72871
|
+
ceiling = setTimeout(() => resolve8({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
|
|
72477
72872
|
})
|
|
72478
72873
|
]);
|
|
72479
72874
|
if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
|
|
@@ -72506,8 +72901,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72506
72901
|
incomplete: nb.incomplete,
|
|
72507
72902
|
timedOut: false
|
|
72508
72903
|
})),
|
|
72509
|
-
new Promise((
|
|
72510
|
-
ceiling = setTimeout(() =>
|
|
72904
|
+
new Promise((resolve8) => {
|
|
72905
|
+
ceiling = setTimeout(() => resolve8({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
|
|
72511
72906
|
})
|
|
72512
72907
|
]);
|
|
72513
72908
|
if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
|
|
@@ -72991,10 +73386,10 @@ tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenan
|
|
|
72991
73386
|
return failGraceful(`runtime tenant logs: ${e.message}`);
|
|
72992
73387
|
}
|
|
72993
73388
|
});
|
|
72994
|
-
tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the central tenant-deploy.yml for an already-promoted ref (no re-tag/merge); train-authority gated").option("--ref <ref>", "ref to deploy (defaults to the
|
|
73389
|
+
tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the central tenant-deploy.yml for an already-promoted ref (no re-tag/merge); train-authority gated").option("--ref <ref>", "ref to deploy (defaults to the currently serving SHA; older refs require --rollback)").option("--rollback", "allow deliberately deploying an older --ref").option("--watch", "block on the dispatched run and report its outcome (gh run watch --exit-status)").option("--json", "machine-readable output").action(async (repo, stage, o) => {
|
|
72995
73390
|
if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime tenant redeploy: <stage> must be dev, rc, or main");
|
|
72996
73391
|
try {
|
|
72997
|
-
const raw = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, watch: o.watch });
|
|
73392
|
+
const raw = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, rollback: o.rollback, watch: o.watch });
|
|
72998
73393
|
const result = raw.workflowRuns ? { ...raw, workflowRuns: raw.workflowRuns.map(workflowRunWithEvidence) } : raw;
|
|
72999
73394
|
return printLine(o.json ? JSON.stringify(result, null, 2) : renderTenantRedeploy(result));
|
|
73000
73395
|
} catch (e) {
|
|
@@ -73254,7 +73649,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
73254
73649
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
73255
73650
|
if (o.secretsFile) {
|
|
73256
73651
|
try {
|
|
73257
|
-
vars.push(`secrets=${(0,
|
|
73652
|
+
vars.push(`secrets=${(0, import_node_fs67.readFileSync)(o.secretsFile, "utf8")}`);
|
|
73258
73653
|
} catch (e) {
|
|
73259
73654
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
73260
73655
|
}
|
|
@@ -73388,7 +73783,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
73388
73783
|
const cfg = await loadConfig();
|
|
73389
73784
|
const reg = registryClientDeps(cfg);
|
|
73390
73785
|
const slug = slugOf(repo);
|
|
73391
|
-
const [metaRead, deployFacts, authority, dev, rc,
|
|
73786
|
+
const [metaRead, deployFacts, authority, dev, rc, main2] = await Promise.all([
|
|
73392
73787
|
fetchProjectBySlugChecked(slug, reg),
|
|
73393
73788
|
fetchDeployFactsBySlug(slug, reg),
|
|
73394
73789
|
fetchTrainAuthority(repo, reg),
|
|
@@ -73403,7 +73798,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
73403
73798
|
meta: metaRead.project,
|
|
73404
73799
|
deployFacts,
|
|
73405
73800
|
trainAuthority: authority.ok ? authority.authority : void 0,
|
|
73406
|
-
runtime: { dev, rc, main }
|
|
73801
|
+
runtime: { dev, rc, main: main2 }
|
|
73407
73802
|
});
|
|
73408
73803
|
console.log(JSON.stringify(report, null, 2));
|
|
73409
73804
|
if (!report.rcand.canApply) process.exitCode = 1;
|
|
@@ -73646,16 +74041,16 @@ function ciAuditDeps2() {
|
|
|
73646
74041
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
73647
74042
|
readSeedFile: (path2) => {
|
|
73648
74043
|
if (!root) return null;
|
|
73649
|
-
const fullPath = (0,
|
|
73650
|
-
return (0,
|
|
74044
|
+
const fullPath = (0, import_node_path64.join)(root, path2);
|
|
74045
|
+
return (0, import_node_fs67.existsSync)(fullPath) ? (0, import_node_fs67.readFileSync)(fullPath, "utf8") : null;
|
|
73651
74046
|
}
|
|
73652
74047
|
};
|
|
73653
74048
|
}
|
|
73654
74049
|
function hubRoot2() {
|
|
73655
|
-
const fromPkg = (0,
|
|
74050
|
+
const fromPkg = (0, import_node_path64.join)(__dirname, "..", "..");
|
|
73656
74051
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
73657
|
-
if ((0,
|
|
73658
|
-
if ((0,
|
|
74052
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(fromPkg, marker))) return fromPkg;
|
|
74053
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(process.cwd(), marker))) return process.cwd();
|
|
73659
74054
|
return null;
|
|
73660
74055
|
}
|
|
73661
74056
|
registerQueryCommands(program2);
|
|
@@ -73812,10 +74207,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
73812
74207
|
targets = resolution.targets;
|
|
73813
74208
|
}
|
|
73814
74209
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
73815
|
-
const fileMatrix = (0,
|
|
74210
|
+
const fileMatrix = (0, import_node_fs67.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs67.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73816
74211
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
73817
74212
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
73818
|
-
const sanctioned = (0,
|
|
74213
|
+
const sanctioned = (0, import_node_fs67.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs67.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73819
74214
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
73820
74215
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
73821
74216
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -73852,16 +74247,16 @@ function directoryBytes(path2) {
|
|
|
73852
74247
|
let total = 0;
|
|
73853
74248
|
let entries;
|
|
73854
74249
|
try {
|
|
73855
|
-
entries = (0,
|
|
74250
|
+
entries = (0, import_node_fs67.readdirSync)(path2, { withFileTypes: true });
|
|
73856
74251
|
} catch {
|
|
73857
74252
|
return 0;
|
|
73858
74253
|
}
|
|
73859
74254
|
for (const entry of entries) {
|
|
73860
|
-
const child2 = (0,
|
|
74255
|
+
const child2 = (0, import_node_path64.join)(path2, entry.name);
|
|
73861
74256
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
73862
74257
|
else {
|
|
73863
74258
|
try {
|
|
73864
|
-
total += (0,
|
|
74259
|
+
total += (0, import_node_fs67.statSync)(child2).size;
|
|
73865
74260
|
} catch {
|
|
73866
74261
|
}
|
|
73867
74262
|
}
|
|
@@ -73869,25 +74264,25 @@ function directoryBytes(path2) {
|
|
|
73869
74264
|
return total;
|
|
73870
74265
|
}
|
|
73871
74266
|
function listDirEntries(dir) {
|
|
73872
|
-
return (0,
|
|
74267
|
+
return (0, import_node_fs67.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
73873
74268
|
}
|
|
73874
74269
|
function readInstalledPluginRefs(configRoot) {
|
|
73875
74270
|
const p = installedPluginsPathForConfig(configRoot);
|
|
73876
|
-
if (!(0,
|
|
74271
|
+
if (!(0, import_node_fs67.existsSync)(p)) return [];
|
|
73877
74272
|
try {
|
|
73878
|
-
return installedPluginPaths((0,
|
|
74273
|
+
return installedPluginPaths((0, import_node_fs67.readFileSync)(p, "utf8"));
|
|
73879
74274
|
} catch {
|
|
73880
74275
|
return null;
|
|
73881
74276
|
}
|
|
73882
74277
|
}
|
|
73883
74278
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
73884
74279
|
return {
|
|
73885
|
-
exists: (p) => (0,
|
|
73886
|
-
listVersionDirs: (root) => (0,
|
|
74280
|
+
exists: (p) => (0, import_node_fs67.existsSync)(p),
|
|
74281
|
+
listVersionDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
73887
74282
|
dirBytes,
|
|
73888
|
-
listStagingDirs: (root) => (0,
|
|
74283
|
+
listStagingDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
73889
74284
|
try {
|
|
73890
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
74285
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path64.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs67.statSync)(p).mtimeMs) };
|
|
73891
74286
|
} catch {
|
|
73892
74287
|
return { name: d.name, mtimeMs: Date.now() };
|
|
73893
74288
|
}
|
|
@@ -73901,10 +74296,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
73901
74296
|
return {
|
|
73902
74297
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
73903
74298
|
mtimeMs: (name) => {
|
|
73904
|
-
const p = (0,
|
|
73905
|
-
if (!(0,
|
|
74299
|
+
const p = (0, import_node_path64.join)(stagingRoot, name);
|
|
74300
|
+
if (!(0, import_node_fs67.existsSync)(p)) return null;
|
|
73906
74301
|
try {
|
|
73907
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
74302
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs67.statSync)(q).mtimeMs);
|
|
73908
74303
|
} catch {
|
|
73909
74304
|
return null;
|
|
73910
74305
|
}
|
|
@@ -73930,7 +74325,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
73930
74325
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
73931
74326
|
);
|
|
73932
74327
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
73933
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
74328
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs67.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
73934
74329
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
73935
74330
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
73936
74331
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -73940,7 +74335,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
|
|
|
73940
74335
|
let primary = o.repoRoot;
|
|
73941
74336
|
if (!primary) {
|
|
73942
74337
|
const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
73943
|
-
primary = common ? (0,
|
|
74338
|
+
primary = common ? (0, import_node_path64.dirname)(common) : "";
|
|
73944
74339
|
}
|
|
73945
74340
|
if (!primary) {
|
|
73946
74341
|
const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";
|