@mutmutco/cli 4.6.24 → 4.6.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +938 -556
- package/package.json +1 -1
package/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;
|
|
@@ -28166,10 +28166,10 @@ var rollout_plan_default = {
|
|
|
28166
28166
|
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
28167
|
},
|
|
28168
28168
|
baseline: {
|
|
28169
|
-
version: "4.6.
|
|
28170
|
-
tag: "v4.6.
|
|
28171
|
-
commit: "
|
|
28172
|
-
npm: "@mutmutco/cli@4.6.
|
|
28169
|
+
version: "4.6.25",
|
|
28170
|
+
tag: "v4.6.25",
|
|
28171
|
+
commit: "8ca2371aca5a",
|
|
28172
|
+
npm: "@mutmutco/cli@4.6.25"
|
|
28173
28173
|
},
|
|
28174
28174
|
exitCriterion: "fleet-n-of-n",
|
|
28175
28175
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28186,14 +28186,14 @@ var rollout_plan_default = {
|
|
|
28186
28186
|
repo: "mutmutco/mmi-hub",
|
|
28187
28187
|
role: "canary",
|
|
28188
28188
|
schedule: "train",
|
|
28189
|
-
v3Target: "v4.6.
|
|
28189
|
+
v3Target: "v4.6.25"
|
|
28190
28190
|
}
|
|
28191
28191
|
],
|
|
28192
28192
|
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
28193
|
rollback: {
|
|
28194
28194
|
independent: true,
|
|
28195
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28196
|
-
v3Target: "v4.6.
|
|
28195
|
+
mechanism: "npm dist-tag latest -> 4.6.25 and redeploy the Hub Lambda from tag v4.6.25 (8ca2371aca5a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28196
|
+
v3Target: "v4.6.25 (@mutmutco/cli@4.6.25, tag commit 8ca2371aca5a \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28197
28197
|
}
|
|
28198
28198
|
},
|
|
28199
28199
|
{
|
|
@@ -30288,7 +30288,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
30288
30288
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
30289
30289
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
30290
30290
|
}
|
|
30291
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
30291
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve8, extraTolerated = []) {
|
|
30292
30292
|
try {
|
|
30293
30293
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
30294
30294
|
return;
|
|
@@ -30302,7 +30302,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, ex
|
|
|
30302
30302
|
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
30303
|
);
|
|
30304
30304
|
}
|
|
30305
|
-
await deps.run("git", ["checkout", `--${
|
|
30305
|
+
await deps.run("git", ["checkout", `--${resolve8}`, "--", ...unmerged]);
|
|
30306
30306
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
30307
30307
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
30308
30308
|
}
|
|
@@ -30500,7 +30500,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
|
30500
30500
|
var CORRELATE_PAGE_LIMIT = 50;
|
|
30501
30501
|
var RUN_CONFIRM_ATTEMPTS = 3;
|
|
30502
30502
|
var RUN_CONFIRM_DELAY_MS = 1e3;
|
|
30503
|
-
var defaultSleep = (ms) => new Promise((
|
|
30503
|
+
var defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
30504
30504
|
function resolveSleep(deps) {
|
|
30505
30505
|
return deps.sleep ?? defaultSleep;
|
|
30506
30506
|
}
|
|
@@ -32391,7 +32391,7 @@ async function discoverShaWorkflowRuns(deps, repo, headSha, seenRunIds) {
|
|
|
32391
32391
|
}
|
|
32392
32392
|
return extra;
|
|
32393
32393
|
}
|
|
32394
|
-
async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince, autoRunHeadSha, dispatchFailure = "throw", publishDir) {
|
|
32394
|
+
async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince, autoRunHeadSha, dispatchFailure = "throw", publishDir, rollback = false) {
|
|
32395
32395
|
if (validSuiteCutContext(ctx.repo, deps.suiteCut) && stage !== "dev") {
|
|
32396
32396
|
return { note: "suite bundle-only: live backend not deployed", deployStatus: "success" };
|
|
32397
32397
|
}
|
|
@@ -32399,7 +32399,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
32399
32399
|
deps.warn?.("Release: Starting deployment");
|
|
32400
32400
|
const since = (deps.now ?? Date.now)();
|
|
32401
32401
|
try {
|
|
32402
|
-
await dispatchTenantDeployWithRetry(deps, { repo: ctx.repo, slug: ctx.slug, ref, stage });
|
|
32402
|
+
await dispatchTenantDeployWithRetry(deps, { repo: ctx.repo, slug: ctx.slug, ref, stage, ...rollback ? { rollback: true } : {} });
|
|
32403
32403
|
} catch (e) {
|
|
32404
32404
|
if (dispatchFailure === "throw") throw e;
|
|
32405
32405
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -32484,19 +32484,19 @@ async function buildEnvironments(deps, ctx, model, deployStatus, retirement) {
|
|
|
32484
32484
|
const mainDomains = domains?.main;
|
|
32485
32485
|
const rcDomains = domains?.rc;
|
|
32486
32486
|
const healthStatus = deployStatus === "success" ? "success" : deployStatus === "failure" ? "failure" : "pending";
|
|
32487
|
-
const
|
|
32487
|
+
const main2 = { healthStatus };
|
|
32488
32488
|
if (mainDomains?.length) {
|
|
32489
|
-
|
|
32490
|
-
|
|
32489
|
+
main2.domains = mainDomains;
|
|
32490
|
+
main2.healthUrl = `https://${mainDomains[0]}/`;
|
|
32491
32491
|
}
|
|
32492
32492
|
const rc = { retirement: retirement.status };
|
|
32493
32493
|
if (retirement.category) rc.retirementCategory = retirement.category;
|
|
32494
32494
|
if (rcDomains?.length) rc.domains = rcDomains;
|
|
32495
32495
|
if (domainsRead === "failed") {
|
|
32496
|
-
|
|
32496
|
+
main2.domainsRead = "failed";
|
|
32497
32497
|
rc.domainsRead = "failed";
|
|
32498
32498
|
}
|
|
32499
|
-
return { main, rc };
|
|
32499
|
+
return { main: main2, rc };
|
|
32500
32500
|
}
|
|
32501
32501
|
var RETIRE_MAX_ATTEMPTS = 3;
|
|
32502
32502
|
var RETIRE_BACKOFF_MS = 1500;
|
|
@@ -32577,14 +32577,27 @@ async function runTenantRedeploy(deps, options) {
|
|
|
32577
32577
|
const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), repo);
|
|
32578
32578
|
const deployModel = requireDeployModel(meta, repo);
|
|
32579
32579
|
const singleTrackMain = stage === "main" && meta.releaseTrack === "single";
|
|
32580
|
-
if (
|
|
32581
|
-
|
|
32580
|
+
if (options.rollback && !options.ref) throw new Error("--rollback requires --ref");
|
|
32581
|
+
const deployed = await deps.readDeployedSha?.(ctx.slug, stage).catch(() => void 0);
|
|
32582
|
+
let ref = options.ref ?? deployed ?? (stage === "dev" || singleTrackMain ? "development" : stage);
|
|
32583
|
+
if (!deployed) {
|
|
32584
|
+
deps.warn?.(`Serving DEPLOY#${stage} stamp unreadable or absent; falling back to ${ref}.`);
|
|
32585
|
+
} else if (options.ref) {
|
|
32586
|
+
if (!deps.compareDeployRef) throw new Error("Deploy ancestry comparison unavailable");
|
|
32587
|
+
const relation = await deps.compareDeployRef(repo, deployed, options.ref);
|
|
32588
|
+
if (!["ahead", "identical", "behind", "diverged"].includes(relation)) throw new Error("Invalid deploy ancestry comparison");
|
|
32589
|
+
if (relation === "behind") {
|
|
32590
|
+
if (options.rollback) deps.warn?.(`rolling back ${stage} from ${deployed} to ${options.ref}`);
|
|
32591
|
+
else {
|
|
32592
|
+
deps.warn?.(`${options.ref} is older than the serving release ${deployed}; redeploying the serving SHA instead (use --rollback for a deliberate rollback).`);
|
|
32593
|
+
ref = deployed;
|
|
32594
|
+
}
|
|
32595
|
+
}
|
|
32582
32596
|
}
|
|
32583
|
-
const ref = options.ref ?? (stage === "dev" || singleTrackMain ? "development" : stage);
|
|
32584
32597
|
if (!isCentralDispatchModel(deployModel)) {
|
|
32585
32598
|
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
32599
|
}
|
|
32587
|
-
const d = await dispatchDeploy(deps, ctx, stage, ref, deployModel, watch);
|
|
32600
|
+
const d = await dispatchDeploy(deps, ctx, stage, ref, deployModel, watch, void 0, void 0, "throw", void 0, options.rollback);
|
|
32588
32601
|
return { ...ctx, command: "tenant-redeploy", stage, ref, deployModel, dispatch: d.note, runId: d.runId, runUrl: d.runUrl, workflowRuns: d.workflowRuns, deployStatus: d.deployStatus };
|
|
32589
32602
|
}
|
|
32590
32603
|
async function runTenantReconcile(deps, options) {
|
|
@@ -33804,7 +33817,7 @@ function lockTokenPid(token) {
|
|
|
33804
33817
|
const m = /^(\d+)-/.exec(token);
|
|
33805
33818
|
return m ? Number(m[1]) : void 0;
|
|
33806
33819
|
}
|
|
33807
|
-
var sleep = (ms) => new Promise((
|
|
33820
|
+
var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
33808
33821
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
33809
33822
|
var FileLockBusyError = class extends Error {
|
|
33810
33823
|
lockPath;
|
|
@@ -34694,7 +34707,7 @@ async function defaultWatchRun(deps, runId, repo) {
|
|
|
34694
34707
|
watchSaidSuccess = false;
|
|
34695
34708
|
}
|
|
34696
34709
|
for (let attempt = 0; attempt < RUN_CONFIRM_ATTEMPTS2; attempt++) {
|
|
34697
|
-
if (attempt > 0) await new Promise((
|
|
34710
|
+
if (attempt > 0) await new Promise((resolve8) => setTimeout(resolve8, RUN_CONFIRM_DELAY_MS2));
|
|
34698
34711
|
try {
|
|
34699
34712
|
const parsed = JSON.parse(await deps.run("gh", ["run", "view", String(runId), "--repo", repo, "--json", "status,conclusion"]));
|
|
34700
34713
|
if (parsed.status === "completed") return parsed.conclusion === "success" ? "success" : "failure";
|
|
@@ -35758,11 +35771,11 @@ var defaultDeps = {
|
|
|
35758
35771
|
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
35759
35772
|
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
35760
35773
|
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
35761
|
-
reexec: (argv) => new Promise((
|
|
35774
|
+
reexec: (argv) => new Promise((resolve8) => {
|
|
35762
35775
|
const args = [...argv];
|
|
35763
35776
|
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", () =>
|
|
35777
|
+
child2.on("exit", (code) => resolve8(code ?? 1));
|
|
35778
|
+
child2.on("error", () => resolve8(1));
|
|
35766
35779
|
}),
|
|
35767
35780
|
// stderr only — stdout may be carrying --json output.
|
|
35768
35781
|
warn: (line) => console.error(line)
|
|
@@ -35919,7 +35932,7 @@ var defaultDeps2 = {
|
|
|
35919
35932
|
}
|
|
35920
35933
|
},
|
|
35921
35934
|
registryWhoami: probeRegistryWhoami,
|
|
35922
|
-
sleep: (ms) => new Promise((
|
|
35935
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
35923
35936
|
env: process.env
|
|
35924
35937
|
};
|
|
35925
35938
|
function message(e) {
|
|
@@ -41535,9 +41548,9 @@ function readCommonMistakes(cmd) {
|
|
|
41535
41548
|
const notes = cmd[COMMON_MISTAKES];
|
|
41536
41549
|
return Array.isArray(notes) && notes.length ? notes : void 0;
|
|
41537
41550
|
}
|
|
41538
|
-
function buildArgument(
|
|
41539
|
-
const out = { name:
|
|
41540
|
-
if (
|
|
41551
|
+
function buildArgument(arg2) {
|
|
41552
|
+
const out = { name: arg2.name(), required: arg2.required, variadic: arg2.variadic };
|
|
41553
|
+
if (arg2.description) out.description = arg2.description;
|
|
41541
41554
|
return out;
|
|
41542
41555
|
}
|
|
41543
41556
|
function buildOption(opt) {
|
|
@@ -41767,9 +41780,9 @@ function buildDoorsCatalog(tree) {
|
|
|
41767
41780
|
};
|
|
41768
41781
|
});
|
|
41769
41782
|
}
|
|
41770
|
-
function argSignature(
|
|
41771
|
-
const inner =
|
|
41772
|
-
return
|
|
41783
|
+
function argSignature(arg2) {
|
|
41784
|
+
const inner = arg2.variadic ? `${arg2.name}...` : arg2.name;
|
|
41785
|
+
return arg2.required ? `<${inner}>` : `[${inner}]`;
|
|
41773
41786
|
}
|
|
41774
41787
|
function formatManifestHuman(manifest, options = {}) {
|
|
41775
41788
|
const root = options.all ? manifest.tree : manifest.primary_tree;
|
|
@@ -42190,7 +42203,7 @@ async function updatePrHeadFromBase(input) {
|
|
|
42190
42203
|
} catch (e) {
|
|
42191
42204
|
throw new Error(`GitHub could not update ${input.head} from ${input.base} (${e.message.trim()}) \u2014 ${remedy}`);
|
|
42192
42205
|
}
|
|
42193
|
-
const sleep2 = input.sleep ?? ((ms) => new Promise((
|
|
42206
|
+
const sleep2 = input.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42194
42207
|
for (let attempt = 0; attempt < PR_HEAD_UPDATE_API_READ_RETRIES; attempt++) {
|
|
42195
42208
|
const to = await readHead2();
|
|
42196
42209
|
if (to && to !== from) return { from, to, base: input.base, via: "update-branch-api" };
|
|
@@ -42213,7 +42226,7 @@ var PR_LAND_MERGE_RETRY_DELAY_MS = 3e4;
|
|
|
42213
42226
|
async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
42214
42227
|
const now = deps.now ?? (() => Date.now());
|
|
42215
42228
|
const deadline = now() + PR_LAND_ENQUEUE_TIMEOUT_MS;
|
|
42216
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42229
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42217
42230
|
const first = await deps.mergeAuto(prNumber, repo);
|
|
42218
42231
|
let result = first;
|
|
42219
42232
|
let transientRetried = false;
|
|
@@ -42252,7 +42265,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
|
|
|
42252
42265
|
async function confirmAutoMergeEnqueued(deps, options) {
|
|
42253
42266
|
const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
|
|
42254
42267
|
const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
|
|
42255
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42268
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42256
42269
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
42257
42270
|
if (await deps.readMerged().catch(() => false)) return "merged";
|
|
42258
42271
|
const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
|
|
@@ -42269,7 +42282,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
|
|
|
42269
42282
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
42270
42283
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
42271
42284
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
42272
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
42285
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42273
42286
|
let lastError = "empty state";
|
|
42274
42287
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
42275
42288
|
try {
|
|
@@ -42421,7 +42434,7 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42421
42434
|
if (read.isInMergeQueue || read.state === "MERGED") confirmed = read;
|
|
42422
42435
|
}
|
|
42423
42436
|
if (!confirmed) {
|
|
42424
|
-
await (deps.sleep ?? ((ms) => new Promise((
|
|
42437
|
+
await (deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(deps.pollDelayMs?.(0) ?? PR_LAND_POLL_MS);
|
|
42425
42438
|
}
|
|
42426
42439
|
return pollPrMergeQueue(prNumber, options, deps, base, confirmed, deadline);
|
|
42427
42440
|
}
|
|
@@ -42460,7 +42473,7 @@ function queueMerged(base, deps) {
|
|
|
42460
42473
|
}
|
|
42461
42474
|
async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
|
|
42462
42475
|
const now = deps.now ?? (() => Date.now());
|
|
42463
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
42476
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
42464
42477
|
const timeoutMs = options.queueTimeoutMs ?? PR_LAND_QUEUE_TIMEOUT_MS;
|
|
42465
42478
|
const deadline = deadlineMs ?? now() + timeoutMs;
|
|
42466
42479
|
let attempt = 0;
|
|
@@ -43401,9 +43414,9 @@ function changedFilesSince(base, cwd, head) {
|
|
|
43401
43414
|
const raw = git2(["diff", "--name-status", "-M", base, ...head ? [head] : [], "--"], cwd).trim();
|
|
43402
43415
|
const changed = raw ? raw.split("\n").map((line) => {
|
|
43403
43416
|
const parts = line.split(" ");
|
|
43404
|
-
const
|
|
43405
|
-
const row = { status: parts[0][0], path:
|
|
43406
|
-
if (parts.length > 2) row.from =
|
|
43417
|
+
const norm3 = (p) => p.replace(/\\/g, "/");
|
|
43418
|
+
const row = { status: parts[0][0], path: norm3(parts[parts.length - 1]) };
|
|
43419
|
+
if (parts.length > 2) row.from = norm3(parts[1]);
|
|
43407
43420
|
return row;
|
|
43408
43421
|
}) : [];
|
|
43409
43422
|
if (head) return changed;
|
|
@@ -43681,7 +43694,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
43681
43694
|
const rejected = (reason) => admissionRejected(reason, repo, pr);
|
|
43682
43695
|
const reconcile = async (reason, recordEntry = false, resumingSaved = false) => {
|
|
43683
43696
|
const maxReads = options.enqueueOnly ? 3 : 1;
|
|
43684
|
-
const sleep3 = options.sleep ?? ((ms) => new Promise((
|
|
43697
|
+
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
43685
43698
|
for (let readAttempt = 0; readAttempt < maxReads; readAttempt++) {
|
|
43686
43699
|
if (readAttempt) await sleep3(readAttempt * 1e3);
|
|
43687
43700
|
try {
|
|
@@ -43836,7 +43849,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
43836
43849
|
}
|
|
43837
43850
|
receipt = { ...receipt, state: "pending", uuid: details.uuid };
|
|
43838
43851
|
}
|
|
43839
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((
|
|
43852
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
43840
43853
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
43841
43854
|
if (attempt) await sleep2(attempt * 1e3);
|
|
43842
43855
|
try {
|
|
@@ -44306,7 +44319,7 @@ function observeQueuedAdmission(repo, pr, snapshot, receiptDirectory) {
|
|
|
44306
44319
|
var SETTLE_POLLS = 6;
|
|
44307
44320
|
var SETTLE_MS = 5e3;
|
|
44308
44321
|
async function settled(repo, id, deps) {
|
|
44309
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
44322
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
44310
44323
|
let status = "unknown";
|
|
44311
44324
|
for (let poll = 0; poll < SETTLE_POLLS; poll++) {
|
|
44312
44325
|
await sleep2(SETTLE_MS);
|
|
@@ -46051,8 +46064,14 @@ function registerBoardCommands(program3) {
|
|
|
46051
46064
|
process.exitCode = e.exitCode;
|
|
46052
46065
|
return;
|
|
46053
46066
|
}
|
|
46054
|
-
|
|
46055
|
-
|
|
46067
|
+
const message2 = `board read failed: ${withDiscoverMissDetail(e.message)}`;
|
|
46068
|
+
if (o.json) {
|
|
46069
|
+
if (e && typeof e === "object" && "quota" in e && e.quota) console.log(JSON.stringify(e.quota));
|
|
46070
|
+
else console.log(formatErrorEnvelope(message2, {
|
|
46071
|
+
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
|
|
46072
|
+
}));
|
|
46073
|
+
}
|
|
46074
|
+
return failGraceful(message2);
|
|
46056
46075
|
}
|
|
46057
46076
|
}
|
|
46058
46077
|
function formatClaimHolder(holder) {
|
|
@@ -46346,9 +46365,9 @@ function registerBoardCommands(program3) {
|
|
|
46346
46365
|
}
|
|
46347
46366
|
|
|
46348
46367
|
// src/bootstrap-commands.ts
|
|
46349
|
-
var
|
|
46368
|
+
var import_node_fs40 = require("node:fs");
|
|
46350
46369
|
var import_node_os21 = require("node:os");
|
|
46351
|
-
var
|
|
46370
|
+
var import_node_path37 = require("node:path");
|
|
46352
46371
|
init_cli_shared();
|
|
46353
46372
|
init_clean_exit();
|
|
46354
46373
|
|
|
@@ -46661,10 +46680,10 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
|
|
|
46661
46680
|
}
|
|
46662
46681
|
|
|
46663
46682
|
// src/box-commands.ts
|
|
46664
|
-
var
|
|
46683
|
+
var import_node_fs39 = require("node:fs");
|
|
46665
46684
|
var import_node_child_process12 = require("node:child_process");
|
|
46666
46685
|
var import_node_os20 = require("node:os");
|
|
46667
|
-
var
|
|
46686
|
+
var import_node_path36 = require("node:path");
|
|
46668
46687
|
|
|
46669
46688
|
// src/box-control.ts
|
|
46670
46689
|
var import_node_crypto16 = require("node:crypto");
|
|
@@ -47411,16 +47430,16 @@ function parseTenantTasksVar(raw) {
|
|
|
47411
47430
|
const task = rawTask;
|
|
47412
47431
|
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
47432
|
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((
|
|
47433
|
+
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
47434
|
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
47435
|
const maxTimeout = task.secretOutput !== void 0 ? 3600 : 10800;
|
|
47417
47436
|
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
47437
|
const artifact = task.artifact ?? "none";
|
|
47419
47438
|
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((
|
|
47439
|
+
const carriesPlaceholder = task.command.some((arg2) => arg2.includes("{artifact}"));
|
|
47421
47440
|
if (artifact === "required" !== carriesPlaceholder) throw new Error(`org project set: tenantTasks.${name} must use {artifact} exactly when artifact is required`);
|
|
47422
47441
|
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((
|
|
47442
|
+
const outputPlaceholder = task.command.some((arg2) => arg2.includes("{output}"));
|
|
47424
47443
|
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
47444
|
out[name] = {
|
|
47426
47445
|
service: task.service,
|
|
@@ -48554,6 +48573,276 @@ ${formatSshRecipe(box, remoteCommand)}
|
|
|
48554
48573
|
`;
|
|
48555
48574
|
}
|
|
48556
48575
|
|
|
48576
|
+
// ../infra/hetzner/cloud-firewall.mjs
|
|
48577
|
+
var import_node_fs38 = require("node:fs");
|
|
48578
|
+
var import_node_url = require("node:url");
|
|
48579
|
+
var import_node_path35 = require("node:path");
|
|
48580
|
+
var import_meta = {};
|
|
48581
|
+
var HZ = "https://api.hetzner.cloud/v1";
|
|
48582
|
+
var APPLY = process.env.FIREWALL_APPLY === "1";
|
|
48583
|
+
var ANY = ["0.0.0.0/0", "::/0"];
|
|
48584
|
+
function arg(name) {
|
|
48585
|
+
const i = process.argv.indexOf(`--${name}`);
|
|
48586
|
+
return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : void 0;
|
|
48587
|
+
}
|
|
48588
|
+
async function hetzner(token, path2, init = {}) {
|
|
48589
|
+
if (!token) throw new Error("no Hetzner Cloud API token (inject it with mmi-cli vault secrets use ... --name HZ_TOKEN)");
|
|
48590
|
+
const res = await fetch(`${HZ}${path2}`, {
|
|
48591
|
+
...init,
|
|
48592
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
48593
|
+
signal: AbortSignal.timeout(3e4)
|
|
48594
|
+
});
|
|
48595
|
+
const body = await res.json().catch(() => ({}));
|
|
48596
|
+
if (!res.ok) throw new Error(`Hetzner ${init.method ?? "GET"} ${path2.split("?")[0]} failed: ${res.status} ${body?.error?.code ?? ""} ${body?.error?.message ?? ""}`);
|
|
48597
|
+
return body;
|
|
48598
|
+
}
|
|
48599
|
+
async function hz(path2, init = {}) {
|
|
48600
|
+
return hetzner(process.env.HZ_TOKEN, path2, init);
|
|
48601
|
+
}
|
|
48602
|
+
function buildRules(cloudflare, extras = []) {
|
|
48603
|
+
const cf = [...cloudflare.ipv4, ...cloudflare.ipv6];
|
|
48604
|
+
if (!cloudflare.ipv4.length || !cloudflare.ipv6.length) throw new Error("refusing an empty Cloudflare range list (it would block every site)");
|
|
48605
|
+
const rules = [
|
|
48606
|
+
{ direction: "in", protocol: "tcp", port: "22", source_ips: ANY, description: "SSH (deploy, reconcile, runner)" },
|
|
48607
|
+
{ direction: "in", protocol: "icmp", source_ips: ANY, description: "ICMP" },
|
|
48608
|
+
{ direction: "in", protocol: "tcp", port: "80", source_ips: cf, description: "HTTP from Cloudflare only" },
|
|
48609
|
+
{ direction: "in", protocol: "tcp", port: "443", source_ips: cf, description: "HTTPS from Cloudflare only" }
|
|
48610
|
+
];
|
|
48611
|
+
for (const e of extras) {
|
|
48612
|
+
if (!["tcp", "udp"].includes(e.protocol) || !/^\d{1,5}(-\d{1,5})?$/.test(String(e.port)) || !e.description) {
|
|
48613
|
+
throw new Error(`invalid firewall extra ${JSON.stringify(e)}: needs protocol tcp|udp, port N or N-M, description`);
|
|
48614
|
+
}
|
|
48615
|
+
rules.push({ direction: "in", protocol: e.protocol, port: String(e.port), source_ips: ANY, description: e.description.slice(0, 255) });
|
|
48616
|
+
}
|
|
48617
|
+
return rules;
|
|
48618
|
+
}
|
|
48619
|
+
async function cloudflareRanges() {
|
|
48620
|
+
const res = await fetch("https://api.cloudflare.com/client/v4/ips", { signal: AbortSignal.timeout(3e4) });
|
|
48621
|
+
const body = await res.json();
|
|
48622
|
+
if (!res.ok || !body?.success) throw new Error(`Cloudflare IP list unreadable: ${res.status}`);
|
|
48623
|
+
return { ipv4: body.result.ipv4_cidrs ?? [], ipv6: body.result.ipv6_cidrs ?? [] };
|
|
48624
|
+
}
|
|
48625
|
+
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 })));
|
|
48626
|
+
function declaredExtras(box) {
|
|
48627
|
+
const extrasFile = JSON.parse((0, import_node_fs38.readFileSync)((0, import_node_url.fileURLToPath)(new URL("./firewall-extras.json", import_meta.url)), "utf8"));
|
|
48628
|
+
return Object.prototype.hasOwnProperty.call(extrasFile, box) ? extrasFile[box] : void 0;
|
|
48629
|
+
}
|
|
48630
|
+
async function main() {
|
|
48631
|
+
const box = arg("box");
|
|
48632
|
+
if (!box || !/^[a-z0-9-]+$/.test(box)) {
|
|
48633
|
+
console.error("cloud-firewall: --box <hetzner server name> is required");
|
|
48634
|
+
process.exit(64);
|
|
48635
|
+
}
|
|
48636
|
+
const detach2 = process.argv.includes("--detach");
|
|
48637
|
+
const name = `mmi-${box}`;
|
|
48638
|
+
const servers = (await hz(`/servers?name=${encodeURIComponent(box)}`)).servers ?? [];
|
|
48639
|
+
if (servers.length !== 1) throw new Error(`expected exactly one server named ${box} in this project, found ${servers.length}`);
|
|
48640
|
+
const server = servers[0];
|
|
48641
|
+
const existing = ((await hz(`/firewalls?name=${encodeURIComponent(name)}`)).firewalls ?? [])[0];
|
|
48642
|
+
const applied = Boolean(existing?.applied_to?.some((a) => a.server?.id === server.id));
|
|
48643
|
+
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" : ""}`);
|
|
48644
|
+
if (detach2) {
|
|
48645
|
+
if (!existing || !applied) {
|
|
48646
|
+
console.log("cloud-firewall: not applied to this server \u2014 nothing to detach");
|
|
48647
|
+
return;
|
|
48648
|
+
}
|
|
48649
|
+
if (!APPLY) {
|
|
48650
|
+
console.log("cloud-firewall: dry-run (FIREWALL_APPLY=1 to detach)");
|
|
48651
|
+
return;
|
|
48652
|
+
}
|
|
48653
|
+
await hz(`/firewalls/${existing.id}/actions/remove_from_resources`, { method: "POST", body: JSON.stringify({ remove_from: [{ type: "server", server: { id: server.id } }] }) });
|
|
48654
|
+
console.log(`cloud-firewall: detached ${name} from ${box}`);
|
|
48655
|
+
return;
|
|
48656
|
+
}
|
|
48657
|
+
const extras = declaredExtras(box);
|
|
48658
|
+
if (extras === void 0) throw new Error(`${box} has no entry in firewall-extras.json; declare it (an empty list is fine) before applying`);
|
|
48659
|
+
const rules = buildRules(await cloudflareRanges(), extras);
|
|
48660
|
+
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}`);
|
|
48661
|
+
const rulesChanged = !existing || norm(existing.rules) !== norm(rules);
|
|
48662
|
+
console.log(`cloud-firewall: rules ${rulesChanged ? "would change" : "unchanged"}; apply-to-server ${applied ? "already done" : "needed"}`);
|
|
48663
|
+
if (!APPLY) {
|
|
48664
|
+
console.log("cloud-firewall: dry-run (FIREWALL_APPLY=1 to write)");
|
|
48665
|
+
return;
|
|
48666
|
+
}
|
|
48667
|
+
let id = existing?.id;
|
|
48668
|
+
if (!existing) {
|
|
48669
|
+
id = (await hz("/firewalls", { method: "POST", body: JSON.stringify({ name, labels: { managed_by: "mmi-hub", box }, rules }) })).firewall.id;
|
|
48670
|
+
console.log(`cloud-firewall: created ${name}#${id}`);
|
|
48671
|
+
} else if (rulesChanged) {
|
|
48672
|
+
await hz(`/firewalls/${id}/actions/set_rules`, { method: "POST", body: JSON.stringify({ rules }) });
|
|
48673
|
+
console.log(`cloud-firewall: updated rules on ${name}#${id}`);
|
|
48674
|
+
}
|
|
48675
|
+
if (!applied) {
|
|
48676
|
+
await hz(`/firewalls/${id}/actions/apply_to_resources`, { method: "POST", body: JSON.stringify({ apply_to: [{ type: "server", server: { id: server.id } }] }) });
|
|
48677
|
+
console.log(`cloud-firewall: applied ${name} to ${box}`);
|
|
48678
|
+
}
|
|
48679
|
+
}
|
|
48680
|
+
if (process.argv[1] && (0, import_node_path35.basename)(process.argv[1]).toLowerCase() === "cloud-firewall.mjs") {
|
|
48681
|
+
main().catch((err) => {
|
|
48682
|
+
console.error(`cloud-firewall: failed: ${err.message}`);
|
|
48683
|
+
process.exit(1);
|
|
48684
|
+
});
|
|
48685
|
+
}
|
|
48686
|
+
|
|
48687
|
+
// ../infra/hetzner/firewall-extras.json
|
|
48688
|
+
var firewall_extras_default = {
|
|
48689
|
+
$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.",
|
|
48690
|
+
"mm-work": [
|
|
48691
|
+
{ protocol: "tcp", port: "7881", description: "MM-Chat dev huddles LiveKit RTC over TCP (MMI-Hub#7353, owner-approved 2026-09-26)" },
|
|
48692
|
+
{ protocol: "udp", port: "7882", description: "MM-Chat dev huddles LiveKit RTC over UDP (MMI-Hub#7353, owner-approved 2026-09-26)" },
|
|
48693
|
+
{ protocol: "tcp", port: "7891", description: "MM-Chat main huddles LiveKit RTC over TCP (MMI-Hub#7907, owner-approved 2026-10-06)" },
|
|
48694
|
+
{ protocol: "udp", port: "7892", description: "MM-Chat main huddles LiveKit RTC over UDP (MMI-Hub#7907, owner-approved 2026-10-06)" }
|
|
48695
|
+
],
|
|
48696
|
+
"mm-central": [],
|
|
48697
|
+
"mm-recorder": [],
|
|
48698
|
+
"mmi-runner": [],
|
|
48699
|
+
"mmi-runner2": []
|
|
48700
|
+
};
|
|
48701
|
+
|
|
48702
|
+
// ../shared/runner-hosts.ts
|
|
48703
|
+
var RUNNER_HOSTS = ["mmi-runner", "mmi-runner2"];
|
|
48704
|
+
|
|
48705
|
+
// src/box-firewall.ts
|
|
48706
|
+
function declaredExtrasFromBundle(box) {
|
|
48707
|
+
const file = firewall_extras_default;
|
|
48708
|
+
return Object.prototype.hasOwnProperty.call(file, box) ? file[box] : void 0;
|
|
48709
|
+
}
|
|
48710
|
+
var FirewallRefusal = class extends Error {
|
|
48711
|
+
};
|
|
48712
|
+
var FIREWALL_NAME_PREFIX = "mmi-";
|
|
48713
|
+
var ACTION_POLL_MS = 2500;
|
|
48714
|
+
var ACTION_POLL_ATTEMPTS = 20;
|
|
48715
|
+
async function runBoxFirewall(boxName, mode, live, deps) {
|
|
48716
|
+
const runner = RUNNER_HOSTS.find((h) => h.toLowerCase() === boxName.toLowerCase());
|
|
48717
|
+
if (!runner) {
|
|
48718
|
+
throw new FirewallRefusal(
|
|
48719
|
+
`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)`
|
|
48720
|
+
);
|
|
48721
|
+
}
|
|
48722
|
+
const token = await deps.fetchToken();
|
|
48723
|
+
const firewallName = `${FIREWALL_NAME_PREFIX}${runner}`;
|
|
48724
|
+
if (live === false && mode !== "plan") {
|
|
48725
|
+
throw new FirewallRefusal(`box firewall: ${mode} is a mutation \u2014 it needs --apply`);
|
|
48726
|
+
}
|
|
48727
|
+
const servers = (await deps.hetzner(token, `/servers?name=${encodeURIComponent(runner)}`)).servers ?? [];
|
|
48728
|
+
if (servers.length !== 1) {
|
|
48729
|
+
throw new FirewallRefusal(`box firewall: expected exactly one server named ${runner} in the MM project, found ${servers.length} \u2014 refusing to guess the target`);
|
|
48730
|
+
}
|
|
48731
|
+
const server = servers[0];
|
|
48732
|
+
const observedAt = deps.now();
|
|
48733
|
+
const existing = ((await deps.hetzner(token, `/firewalls?name=${encodeURIComponent(firewallName)}`)).firewalls ?? [])[0];
|
|
48734
|
+
const applied = Boolean(existing?.applied_to?.some((a) => a.server?.id === server.id));
|
|
48735
|
+
const target = {
|
|
48736
|
+
operation: mode,
|
|
48737
|
+
live,
|
|
48738
|
+
box: runner,
|
|
48739
|
+
server: { id: server.id, name: server.name, ...server.public_net?.ipv4?.ip ? { ipv4: server.public_net.ipv4.ip } : {} },
|
|
48740
|
+
firewall: existing ? { id: existing.id, name: existing.name } : null,
|
|
48741
|
+
applied,
|
|
48742
|
+
rulesChanged: false,
|
|
48743
|
+
currentRules: existing?.rules ?? [],
|
|
48744
|
+
plannedRules: null,
|
|
48745
|
+
actions: [],
|
|
48746
|
+
readback: void 0,
|
|
48747
|
+
note: void 0,
|
|
48748
|
+
ambiguous: false,
|
|
48749
|
+
ok: false,
|
|
48750
|
+
observedAt
|
|
48751
|
+
};
|
|
48752
|
+
if (mode === "detach") {
|
|
48753
|
+
if (!existing || !applied) {
|
|
48754
|
+
return { ...target, ok: true, note: `firewall ${firewallName} is ${existing ? "not applied to" : "absent for"} this server \u2014 nothing to detach` };
|
|
48755
|
+
}
|
|
48756
|
+
const action = await actionPost(deps, token, existing.id, "remove_from_resources", { remove_from: [{ type: "server", server: { id: server.id } }] });
|
|
48757
|
+
const waited = await waitForAction(deps, token, action);
|
|
48758
|
+
target.actions.push(actionReceipt("remove_from_resources", waited));
|
|
48759
|
+
target.ambiguous = waited.ambiguous;
|
|
48760
|
+
const readback2 = await readbackState(deps, token, existing.id, server.id, null, deps.now());
|
|
48761
|
+
target.readback = readback2;
|
|
48762
|
+
target.ok = !waited.ambiguous && readback2.attached === false;
|
|
48763
|
+
if (target.ok) target.note = `detached ${firewallName} from ${runner}`;
|
|
48764
|
+
return target;
|
|
48765
|
+
}
|
|
48766
|
+
const extras = deps.declaredExtras(runner);
|
|
48767
|
+
if (extras === void 0) {
|
|
48768
|
+
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`);
|
|
48769
|
+
}
|
|
48770
|
+
const planned = buildRules(await deps.cloudflareRanges(), extras);
|
|
48771
|
+
target.plannedRules = planned;
|
|
48772
|
+
const rulesChanged = !existing || norm(existing.rules ?? []) !== norm(planned);
|
|
48773
|
+
target.rulesChanged = rulesChanged;
|
|
48774
|
+
if (mode === "plan") {
|
|
48775
|
+
target.ok = !rulesChanged && applied;
|
|
48776
|
+
if (target.ok) target.note = `firewall ${firewallName}#${existing.id} matches the reviewed policy and is applied`;
|
|
48777
|
+
return target;
|
|
48778
|
+
}
|
|
48779
|
+
let firewallId = existing?.id;
|
|
48780
|
+
if (!existing) {
|
|
48781
|
+
const created = await deps.hetzner(token, "/firewalls", {
|
|
48782
|
+
method: "POST",
|
|
48783
|
+
body: JSON.stringify({ name: firewallName, labels: { managed_by: "mmi-hub", box: runner }, rules: planned })
|
|
48784
|
+
});
|
|
48785
|
+
firewallId = created.firewall.id;
|
|
48786
|
+
target.actions.push({ id: firewallId, kind: "create", status: "created" });
|
|
48787
|
+
} else if (rulesChanged) {
|
|
48788
|
+
const action = await actionPost(deps, token, firewallId, "set_rules", { rules: planned });
|
|
48789
|
+
const waited = await waitForAction(deps, token, action);
|
|
48790
|
+
target.actions.push(actionReceipt("set_rules", waited));
|
|
48791
|
+
target.ambiguous ||= waited.ambiguous;
|
|
48792
|
+
if (waited.ambiguous || waited.action.status === "error") {
|
|
48793
|
+
const readback2 = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48794
|
+
target.readback = readback2;
|
|
48795
|
+
return target;
|
|
48796
|
+
}
|
|
48797
|
+
}
|
|
48798
|
+
if (!applied) {
|
|
48799
|
+
const action = await actionPost(deps, token, firewallId, "apply_to_resources", { apply_to: [{ type: "server", server: { id: server.id } }] });
|
|
48800
|
+
const waited = await waitForAction(deps, token, action);
|
|
48801
|
+
target.actions.push(actionReceipt("apply_to_resources", waited));
|
|
48802
|
+
target.ambiguous ||= waited.ambiguous;
|
|
48803
|
+
if (waited.action.status === "error") {
|
|
48804
|
+
const readback2 = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48805
|
+
target.readback = readback2;
|
|
48806
|
+
return target;
|
|
48807
|
+
}
|
|
48808
|
+
}
|
|
48809
|
+
const readback = await readbackState(deps, token, firewallId, server.id, planned, deps.now());
|
|
48810
|
+
target.firewall = { id: firewallId, name: firewallName };
|
|
48811
|
+
target.readback = readback;
|
|
48812
|
+
target.ok = !target.ambiguous && readback.attached && readback.rulesMatch === true;
|
|
48813
|
+
if (target.ok) target.note = `firewall ${firewallName}#${firewallId} applied to ${runner} and read back independently`;
|
|
48814
|
+
return target;
|
|
48815
|
+
}
|
|
48816
|
+
async function actionPost(deps, token, firewallId, kind, body) {
|
|
48817
|
+
const res = await deps.hetzner(token, `/firewalls/${firewallId}/actions/${kind}`, { method: "POST", body: JSON.stringify(body) });
|
|
48818
|
+
return res.action;
|
|
48819
|
+
}
|
|
48820
|
+
async function waitForAction(deps, token, accepted) {
|
|
48821
|
+
let last = accepted;
|
|
48822
|
+
for (let attempt = 0; attempt < ACTION_POLL_ATTEMPTS; attempt += 1) {
|
|
48823
|
+
const res = await deps.hetzner(token, `/firewalls/actions/${accepted.id}`);
|
|
48824
|
+
last = res.action ?? last;
|
|
48825
|
+
if (last.status === "success" || last.status === "error") return { action: last, ambiguous: false };
|
|
48826
|
+
await deps.sleep(ACTION_POLL_MS);
|
|
48827
|
+
}
|
|
48828
|
+
return { action: last, ambiguous: true };
|
|
48829
|
+
}
|
|
48830
|
+
function actionReceipt(kind, waited) {
|
|
48831
|
+
return {
|
|
48832
|
+
id: waited.action.id,
|
|
48833
|
+
kind,
|
|
48834
|
+
status: waited.ambiguous ? "running (wait bound exhausted \u2014 ambiguous)" : waited.action.status,
|
|
48835
|
+
...waited.action.error ? { error: { code: waited.action.error.code, message: waited.action.error.message } } : {}
|
|
48836
|
+
};
|
|
48837
|
+
}
|
|
48838
|
+
async function readbackState(deps, token, firewallId, serverId, planned, at) {
|
|
48839
|
+
const firewall = (await deps.hetzner(token, `/firewalls/${firewallId}`)).firewall;
|
|
48840
|
+
const server = (await deps.hetzner(token, `/servers/${serverId}`)).server;
|
|
48841
|
+
const attached = server != null && server.id === serverId && Boolean(firewall?.applied_to?.some((a) => a.server?.id === serverId));
|
|
48842
|
+
const rulesMatch = planned ? norm(firewall?.rules ?? []) === norm(planned) : null;
|
|
48843
|
+
return { observedAt: at, attached, rulesMatch };
|
|
48844
|
+
}
|
|
48845
|
+
|
|
48557
48846
|
// src/box-commands.ts
|
|
48558
48847
|
var HETZNER_API = "https://api.hetzner.cloud/v1/servers";
|
|
48559
48848
|
var HETZNER_TIMEOUT_MS = 15e3;
|
|
@@ -48664,7 +48953,7 @@ function registerBoxCommands(program3) {
|
|
|
48664
48953
|
const stdinSource = o.body ? `"${o.body}"` : "<body.sh>";
|
|
48665
48954
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
48666
48955
|
const runLine = wroteScript ? useStdin ? `bash "${wroteScript}" < ${stdinSource}` : `bash "${wroteScript}"` : void 0;
|
|
48667
|
-
if (wroteScript) (0,
|
|
48956
|
+
if (wroteScript) (0, import_node_fs39.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
48668
48957
|
if (o.json) {
|
|
48669
48958
|
console.log(JSON.stringify({
|
|
48670
48959
|
box: found,
|
|
@@ -48775,6 +49064,79 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
48775
49064
|
process.exitCode = 2;
|
|
48776
49065
|
}
|
|
48777
49066
|
});
|
|
49067
|
+
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) => {
|
|
49068
|
+
try {
|
|
49069
|
+
if (o.apply && o.plan) throw new UsageError("box firewall: --plan and --apply are exclusive");
|
|
49070
|
+
if (o.detach && !o.apply) throw new UsageError("box firewall: --detach is a mutation \u2014 it needs --apply");
|
|
49071
|
+
if (!o.apply && !o.plan) o.plan = true;
|
|
49072
|
+
const mode = o.detach ? "detach" : o.apply ? "apply" : "plan";
|
|
49073
|
+
const deps = firewallDeps();
|
|
49074
|
+
if (mode === "plan") {
|
|
49075
|
+
const receipt2 = await runBoxFirewall(name, mode, false, deps);
|
|
49076
|
+
renderFirewallReceipt(receipt2, o.json === true);
|
|
49077
|
+
process.exitCode = receipt2.ok ? 0 : 1;
|
|
49078
|
+
return;
|
|
49079
|
+
}
|
|
49080
|
+
const { boxes, incomplete } = await fetchInventory();
|
|
49081
|
+
const found = lookupBox(name, boxes, incomplete);
|
|
49082
|
+
if (found.kind !== "found") {
|
|
49083
|
+
const why = found.kind === "unknown" ? `the inventory is incomplete (${found.reasons.join("; ")})` : `no box named '${name}'`;
|
|
49084
|
+
throw new UnreadableError(`box firewall: ${why}`);
|
|
49085
|
+
}
|
|
49086
|
+
const receipt = await runBoxFirewall(found.box.name, mode, true, deps);
|
|
49087
|
+
if (!o.json) warnIncomplete(incomplete);
|
|
49088
|
+
renderFirewallReceipt(receipt, o.json === true);
|
|
49089
|
+
process.exitCode = receipt.ok ? 0 : 1;
|
|
49090
|
+
} catch (e) {
|
|
49091
|
+
const message2 = e instanceof FirewallRefusal ? e.message : e.message;
|
|
49092
|
+
if (o.json) console.log(JSON.stringify({ box: name, ok: false, refused: e instanceof FirewallRefusal, error: message2 }, null, 2));
|
|
49093
|
+
else console.error(message2);
|
|
49094
|
+
process.exitCode = 2;
|
|
49095
|
+
}
|
|
49096
|
+
});
|
|
49097
|
+
}
|
|
49098
|
+
function renderFirewallReceipt(r, json) {
|
|
49099
|
+
if (json) {
|
|
49100
|
+
console.log(JSON.stringify(r, null, 2));
|
|
49101
|
+
return;
|
|
49102
|
+
}
|
|
49103
|
+
const fw = r.firewall ? `${r.firewall.name}#${r.firewall.id}` : `mmi-${r.box} (absent)`;
|
|
49104
|
+
const lines2 = [
|
|
49105
|
+
`${r.box} \u2014 firewall ${r.operation}${r.live ? " (LIVE)" : " (dry-run)"}`,
|
|
49106
|
+
` server: #${r.server.id}${r.server.ipv4 ? ` (${r.server.ipv4})` : ""}`,
|
|
49107
|
+
` firewall: ${fw}, ${r.applied ? "attached to this server" : "not attached to this server"}`
|
|
49108
|
+
];
|
|
49109
|
+
if (r.plannedRules) {
|
|
49110
|
+
lines2.push(` rules: ${r.rulesChanged ? "WOULD CHANGE \u2014 planned set differs from the provider" : "match the reviewed policy"}`);
|
|
49111
|
+
for (const rule of r.plannedRules) {
|
|
49112
|
+
const from = rule.source_ips.length > 2 ? `${rule.source_ips.length} Cloudflare ranges` : rule.source_ips.join(", ");
|
|
49113
|
+
lines2.push(` in ${rule.protocol}${rule.port ? `/${rule.port}` : ""} from ${from} \u2014 ${rule.description}`);
|
|
49114
|
+
}
|
|
49115
|
+
}
|
|
49116
|
+
for (const a of r.actions) lines2.push(` action ${a.kind} #${a.id}: ${a.status}${a.error ? ` \u2014 ${a.error.code ?? ""} ${a.error.message ?? ""}` : ""}`);
|
|
49117
|
+
if (r.readback) {
|
|
49118
|
+
lines2.push(` readback (independent, ${r.readback.observedAt}): attached=${r.readback.attached}${r.readback.rulesMatch === null ? "" : ` rulesMatch=${r.readback.rulesMatch}`}`);
|
|
49119
|
+
}
|
|
49120
|
+
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");
|
|
49121
|
+
lines2.push(` ${r.ok ? "OK" : "NOT OK"}${r.note ? ` \u2014 ${r.note}` : ""} (observed ${r.observedAt})`);
|
|
49122
|
+
console.log(lines2.join("\n"));
|
|
49123
|
+
}
|
|
49124
|
+
function firewallDeps() {
|
|
49125
|
+
return {
|
|
49126
|
+
hetzner: (token, path2, init) => hetzner(token, path2, init),
|
|
49127
|
+
cloudflareRanges: () => cloudflareRanges(),
|
|
49128
|
+
declaredExtras: (boxName) => declaredExtrasFromBundle(boxName),
|
|
49129
|
+
fetchToken: async () => {
|
|
49130
|
+
let token = null;
|
|
49131
|
+
await withSecrets(async (secretsDeps) => {
|
|
49132
|
+
token = await fetchSecretValue(secretsDeps, PROJECT_API_TOKEN.MM, { slug: HETZNER_VAULT_SLUG }, true);
|
|
49133
|
+
});
|
|
49134
|
+
if (!token) throw new Error("box firewall: could not read the Hetzner MM API token from the vault (the value is never printed)");
|
|
49135
|
+
return token;
|
|
49136
|
+
},
|
|
49137
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
49138
|
+
now: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
49139
|
+
};
|
|
48778
49140
|
}
|
|
48779
49141
|
var UsageError = class extends Error {
|
|
48780
49142
|
};
|
|
@@ -48791,17 +49153,17 @@ function hubGitShow(ref) {
|
|
|
48791
49153
|
return (path2) => git3(["show", `${ref}:${path2}`]);
|
|
48792
49154
|
}
|
|
48793
49155
|
function runOnBox(found, script) {
|
|
48794
|
-
const dir = (0,
|
|
49156
|
+
const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os20.tmpdir)(), "mmi-box-control-"));
|
|
48795
49157
|
try {
|
|
48796
|
-
const recipe = (0,
|
|
48797
|
-
(0,
|
|
49158
|
+
const recipe = (0, import_node_path36.join)(dir, "recipe.sh");
|
|
49159
|
+
(0, import_node_fs39.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
|
|
48798
49160
|
const r = (0, import_node_child_process12.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
|
|
48799
49161
|
if (r.status !== 0) {
|
|
48800
49162
|
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
49163
|
}
|
|
48802
49164
|
return r.stdout;
|
|
48803
49165
|
} finally {
|
|
48804
|
-
(0,
|
|
49166
|
+
(0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
|
|
48805
49167
|
}
|
|
48806
49168
|
}
|
|
48807
49169
|
function formatControl(mode, box, rows) {
|
|
@@ -50302,12 +50664,12 @@ var declaredProjectAdmins = (meta) => {
|
|
|
50302
50664
|
};
|
|
50303
50665
|
var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
50304
50666
|
async function gateRenderDiff(current, rendered) {
|
|
50305
|
-
const dir = (0,
|
|
50306
|
-
const before = (0,
|
|
50307
|
-
const after = (0,
|
|
50667
|
+
const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
|
|
50668
|
+
const before = (0, import_node_path37.join)(dir, "committed-gate.yml");
|
|
50669
|
+
const after = (0, import_node_path37.join)(dir, "registry-render.yml");
|
|
50308
50670
|
try {
|
|
50309
|
-
(0,
|
|
50310
|
-
(0,
|
|
50671
|
+
(0, import_node_fs40.writeFileSync)(before, current);
|
|
50672
|
+
(0, import_node_fs40.writeFileSync)(after, rendered);
|
|
50311
50673
|
try {
|
|
50312
50674
|
return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
50313
50675
|
} catch (e) {
|
|
@@ -50316,7 +50678,7 @@ async function gateRenderDiff(current, rendered) {
|
|
|
50316
50678
|
throw e;
|
|
50317
50679
|
}
|
|
50318
50680
|
} finally {
|
|
50319
|
-
(0,
|
|
50681
|
+
(0, import_node_fs40.rmSync)(dir, { recursive: true, force: true });
|
|
50320
50682
|
}
|
|
50321
50683
|
}
|
|
50322
50684
|
function bootstrapTrainDoctorDeps() {
|
|
@@ -50337,9 +50699,9 @@ async function propagationScope(classes, what) {
|
|
|
50337
50699
|
}
|
|
50338
50700
|
const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
50339
50701
|
let independentCount = rosterRepos.length;
|
|
50340
|
-
if ((0,
|
|
50702
|
+
if ((0, import_node_fs40.existsSync)("projects.json")) {
|
|
50341
50703
|
try {
|
|
50342
|
-
const local = JSON.parse((0,
|
|
50704
|
+
const local = JSON.parse((0, import_node_fs40.readFileSync)("projects.json", "utf8"));
|
|
50343
50705
|
const localRepos = /* @__PURE__ */ new Set();
|
|
50344
50706
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
50345
50707
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -50464,13 +50826,13 @@ function registerBootstrapCommands(program3) {
|
|
|
50464
50826
|
})),
|
|
50465
50827
|
pushAllowlistOwners: pushAllowlistAuthority(verifyOwners, declaredProjectAdmins(meta)),
|
|
50466
50828
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
50467
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
50829
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null,
|
|
50468
50830
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
50469
50831
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
50470
50832
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
50471
50833
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
50472
50834
|
// sanction, which is the pre-#3664 behaviour.
|
|
50473
|
-
sanctionedAdmins: (0,
|
|
50835
|
+
sanctionedAdmins: (0, import_node_fs40.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs40.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
50474
50836
|
requiredGcpApis: (() => {
|
|
50475
50837
|
const v = meta?.requiredGcpApis;
|
|
50476
50838
|
if (Array.isArray(v)) return v;
|
|
@@ -50548,14 +50910,14 @@ function registerBootstrapCommands(program3) {
|
|
|
50548
50910
|
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
50911
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
50550
50912
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50551
|
-
if (!(0,
|
|
50913
|
+
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
50914
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50553
50915
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
50554
|
-
const manifest = loadBootstrapSeeds((0,
|
|
50916
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
50555
50917
|
const hubContents = /* @__PURE__ */ new Map();
|
|
50556
50918
|
for (const s of manifest.seeds) {
|
|
50557
50919
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
50558
|
-
hubContents.set(s.target, (0,
|
|
50920
|
+
hubContents.set(s.target, (0, import_node_fs40.existsSync)(s.target) ? (0, import_node_fs40.readFileSync)(s.target, "utf8") : null);
|
|
50559
50921
|
}
|
|
50560
50922
|
let targets;
|
|
50561
50923
|
let classOf = (_repo) => "deployable";
|
|
@@ -50811,24 +51173,24 @@ function registerBootstrapCommands(program3) {
|
|
|
50811
51173
|
return;
|
|
50812
51174
|
}
|
|
50813
51175
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
50814
|
-
if (!(0,
|
|
51176
|
+
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
51177
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
50816
51178
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
50817
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51179
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
50818
51180
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
50819
51181
|
const slug = parsedRepo.slug;
|
|
50820
51182
|
const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
|
|
50821
51183
|
const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
|
|
50822
|
-
const readFile10 = (p) => (0,
|
|
51184
|
+
const readFile10 = (p) => (0, import_node_fs40.existsSync)(p) ? (0, import_node_fs40.readFileSync)(p, "utf8") : null;
|
|
50823
51185
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
50824
51186
|
const putSeed = async (target, content, ref, sha) => {
|
|
50825
|
-
const tmp = (0,
|
|
50826
|
-
(0,
|
|
51187
|
+
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`);
|
|
51188
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
50827
51189
|
try {
|
|
50828
51190
|
await gh2(contentPutInputArgs(repo, target, tmp));
|
|
50829
51191
|
} finally {
|
|
50830
51192
|
try {
|
|
50831
|
-
(0,
|
|
51193
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
50832
51194
|
} catch {
|
|
50833
51195
|
}
|
|
50834
51196
|
}
|
|
@@ -51466,10 +51828,10 @@ LIVE apply to ${repo}:
|
|
|
51466
51828
|
if (rawFlag("--pins")) return propagatePins(rawFlag("--apply"), rawFlag("--json"));
|
|
51467
51829
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
51468
51830
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
51469
|
-
if (!(0,
|
|
51831
|
+
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
51832
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
51471
51833
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
51472
|
-
const manifest = loadBootstrapSeeds((0,
|
|
51834
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
51473
51835
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
51474
51836
|
if (!o.target) {
|
|
51475
51837
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -51478,9 +51840,9 @@ LIVE apply to ${repo}:
|
|
|
51478
51840
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
51479
51841
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
|
|
51480
51842
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
51481
|
-
if (!seed.managedBlock && !(0,
|
|
51482
|
-
const hubContent = seed.managedBlock ? null : (0,
|
|
51483
|
-
const readSeedFile2 = (path2) => (0,
|
|
51843
|
+
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`);
|
|
51844
|
+
const hubContent = seed.managedBlock ? null : (0, import_node_fs40.readFileSync)(seed.target, "utf8");
|
|
51845
|
+
const readSeedFile2 = (path2) => (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null;
|
|
51484
51846
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
51485
51847
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
51486
51848
|
const repos = inClassRepos.map((repo) => {
|
|
@@ -51599,7 +51961,7 @@ LIVE apply to ${repo}:
|
|
|
51599
51961
|
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
51962
|
const headSha = seedSource.sha;
|
|
51601
51963
|
const sourceBlob = (await execGitForSeedSource(["rev-parse", `${headSha}:${seedSourcePath(seed)}`])).trim();
|
|
51602
|
-
const runbookUnaffected = (0,
|
|
51964
|
+
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
51965
|
for (const rec of plan.records) {
|
|
51604
51966
|
if (rec.action !== "open-pr") continue;
|
|
51605
51967
|
const baseBranch = classOf(rec.repo) === "content" ? "main" : "development";
|
|
@@ -51621,15 +51983,15 @@ LIVE apply to ${repo}:
|
|
|
51621
51983
|
} catch {
|
|
51622
51984
|
existingSha = void 0;
|
|
51623
51985
|
}
|
|
51624
|
-
const tmp = (0,
|
|
51986
|
+
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
51987
|
const desiredContent = desiredByRepo.get(rec.repo);
|
|
51626
51988
|
if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
|
|
51627
|
-
(0,
|
|
51989
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha, runbookUnaffected)), "utf8");
|
|
51628
51990
|
try {
|
|
51629
51991
|
await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
51630
51992
|
} finally {
|
|
51631
51993
|
try {
|
|
51632
|
-
(0,
|
|
51994
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
51633
51995
|
} catch {
|
|
51634
51996
|
}
|
|
51635
51997
|
}
|
|
@@ -51698,10 +52060,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51698
52060
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
51699
52061
|
}
|
|
51700
52062
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
51701
|
-
if (!(0,
|
|
52063
|
+
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
52064
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
51703
52065
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
51704
|
-
const manifest = loadBootstrapSeeds((0,
|
|
52066
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
|
|
51705
52067
|
const propagatable = manifest.seeds.filter(isPropagatableSeed);
|
|
51706
52068
|
if (!o.target) {
|
|
51707
52069
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -51718,10 +52080,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51718
52080
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
51719
52081
|
let candidates;
|
|
51720
52082
|
if (o.record) {
|
|
51721
|
-
if (!(0,
|
|
52083
|
+
if (!(0, import_node_fs40.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
51722
52084
|
let parsed;
|
|
51723
52085
|
try {
|
|
51724
|
-
parsed = JSON.parse((0,
|
|
52086
|
+
parsed = JSON.parse((0, import_node_fs40.readFileSync)(o.record, "utf8"));
|
|
51725
52087
|
} catch (e) {
|
|
51726
52088
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
51727
52089
|
}
|
|
@@ -51798,13 +52160,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
51798
52160
|
} catch {
|
|
51799
52161
|
existingSha = void 0;
|
|
51800
52162
|
}
|
|
51801
|
-
const tmp = (0,
|
|
51802
|
-
(0,
|
|
52163
|
+
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`);
|
|
52164
|
+
(0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
51803
52165
|
try {
|
|
51804
52166
|
await gh2(contentPutInputArgs(repo, seed.target, tmp));
|
|
51805
52167
|
} finally {
|
|
51806
52168
|
try {
|
|
51807
|
-
(0,
|
|
52169
|
+
(0, import_node_fs40.unlinkSync)(tmp);
|
|
51808
52170
|
} catch {
|
|
51809
52171
|
}
|
|
51810
52172
|
}
|
|
@@ -52309,8 +52671,8 @@ function formatSuiteMarkdown(report) {
|
|
|
52309
52671
|
const lines2 = ["| Member | dev | rc | main | Pinned by suite |", "| --- | --- | --- | --- | --- |"];
|
|
52310
52672
|
if (report.lead) lines2.push(`| MM-Work (suite) | ${short(report.lead.dev)} | ${report.lead.rc} | ${report.lead.main} | - |`);
|
|
52311
52673
|
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(
|
|
52674
|
+
const [dev, rc, main2] = ["dev", "rc", "main"].map((s) => m.stages.find((x) => x.stage === s));
|
|
52675
|
+
lines2.push(`| ${m.repo.split("/")[1]} | ${cell(dev)} | ${cell(rc)} | ${cell(main2)} | ${[by(rc, "rc"), by(main2, "main")].filter(Boolean).join(", ") || "-"} |`);
|
|
52314
52676
|
}
|
|
52315
52677
|
return lines2.join("\n");
|
|
52316
52678
|
}
|
|
@@ -52400,8 +52762,8 @@ async function waitSuiteDev(deps, opts) {
|
|
|
52400
52762
|
}
|
|
52401
52763
|
|
|
52402
52764
|
// src/suite-train.ts
|
|
52403
|
-
var
|
|
52404
|
-
var
|
|
52765
|
+
var import_node_fs41 = require("node:fs");
|
|
52766
|
+
var import_node_path38 = require("node:path");
|
|
52405
52767
|
var import_node_crypto20 = require("node:crypto");
|
|
52406
52768
|
|
|
52407
52769
|
// src/kit-version.ts
|
|
@@ -52487,7 +52849,7 @@ function suiteReleaseTag(rc) {
|
|
|
52487
52849
|
function readSuiteCut(path2) {
|
|
52488
52850
|
let raw;
|
|
52489
52851
|
try {
|
|
52490
|
-
raw = (0,
|
|
52852
|
+
raw = (0, import_node_fs41.readFileSync)(path2, "utf8");
|
|
52491
52853
|
} catch (e) {
|
|
52492
52854
|
if (e.code === "ENOENT") return void 0;
|
|
52493
52855
|
throw e;
|
|
@@ -52503,11 +52865,11 @@ function readSuiteCut(path2) {
|
|
|
52503
52865
|
return record2;
|
|
52504
52866
|
}
|
|
52505
52867
|
function writeSuiteCut(path2, record2) {
|
|
52506
|
-
(0,
|
|
52868
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
|
|
52507
52869
|
const temp = `${path2}.${(0, import_node_crypto20.randomBytes)(8).toString("hex")}.tmp`;
|
|
52508
52870
|
try {
|
|
52509
|
-
(0,
|
|
52510
|
-
(0,
|
|
52871
|
+
(0, import_node_fs41.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
|
|
52872
|
+
(0, import_node_fs41.renameSync)(temp, path2);
|
|
52511
52873
|
} catch (e) {
|
|
52512
52874
|
throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
|
|
52513
52875
|
}
|
|
@@ -52523,15 +52885,15 @@ function pinVersion(url) {
|
|
|
52523
52885
|
}
|
|
52524
52886
|
}
|
|
52525
52887
|
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,
|
|
52888
|
+
const main2 = bundles.main;
|
|
52889
|
+
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`);
|
|
52890
|
+
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`);
|
|
52891
|
+
const mainPin = mainPinFor(new URL(main2).host, bundles, pinVersion(main2));
|
|
52892
|
+
for (const url of [bundles.rc, main2]) {
|
|
52531
52893
|
const tag = pinVersion(url);
|
|
52532
52894
|
if (!RC_TAG.test(tag) && !RELEASE_TAG.test(tag)) continue;
|
|
52533
52895
|
const sha = await deps.tagSha(member.repo, tag);
|
|
52534
|
-
if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url ===
|
|
52896
|
+
if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url === main2 ? mainPin : url, mainPin };
|
|
52535
52897
|
}
|
|
52536
52898
|
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
52899
|
}
|
|
@@ -52556,25 +52918,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
|
|
|
52556
52918
|
return tags.map((t) => `${t.repo}@${t.tag}`);
|
|
52557
52919
|
}
|
|
52558
52920
|
async function retireFailedLeadRcLedger(deps, ownerGo) {
|
|
52559
|
-
if (!(0,
|
|
52921
|
+
if (!(0, import_node_fs41.existsSync)(deps.ledgerPath)) return void 0;
|
|
52560
52922
|
return withLedgerLock(deps.ledgerPath, async (fence) => {
|
|
52561
|
-
const ledger = parseReleaseLedger((0,
|
|
52923
|
+
const ledger = parseReleaseLedger((0, import_node_fs41.readFileSync)(deps.ledgerPath, "utf8"));
|
|
52562
52924
|
const states = Object.values(ledger.phases).map((p) => p.state);
|
|
52563
52925
|
if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
|
|
52564
52926
|
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
52927
|
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,
|
|
52928
|
+
const archive = (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
|
|
52929
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
|
|
52568
52930
|
await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
|
|
52569
|
-
await fence(() => (0,
|
|
52931
|
+
await fence(() => (0, import_node_fs41.renameSync)(deps.ledgerPath, archive));
|
|
52570
52932
|
return `${ledger.tag} (ledger archived to ${archive})`;
|
|
52571
52933
|
});
|
|
52572
52934
|
}
|
|
52573
52935
|
function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
|
|
52574
52936
|
const record2 = abandonableCut(recordPath, ownerGo);
|
|
52575
52937
|
const archive = `${recordPath}.abandoned-${record2.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
|
|
52576
|
-
if ((0,
|
|
52577
|
-
(0,
|
|
52938
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
|
|
52939
|
+
(0, import_node_fs41.renameSync)(recordPath, archive);
|
|
52578
52940
|
return { archive, orphanTags: record2.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
|
|
52579
52941
|
}
|
|
52580
52942
|
async function suiteRestoreMainPins(deps) {
|
|
@@ -52661,12 +53023,12 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52661
53023
|
}
|
|
52662
53024
|
if ((record2?.announcement === "sent" || record2?.announcement === "skipped") && record2.leadReleased) {
|
|
52663
53025
|
const archive = `${deps.recordPath}.${record2.leadRcTag}.json`;
|
|
52664
|
-
if ((0,
|
|
52665
|
-
(0,
|
|
53026
|
+
if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
|
|
53027
|
+
(0, import_node_fs41.renameSync)(deps.recordPath, archive);
|
|
52666
53028
|
record2 = void 0;
|
|
52667
53029
|
}
|
|
52668
53030
|
if (record2 && !record2.pinPr && !record2.leadRcTag && record2.members.every((m) => !m.tag)) {
|
|
52669
|
-
(0,
|
|
53031
|
+
(0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52670
53032
|
record2 = void 0;
|
|
52671
53033
|
}
|
|
52672
53034
|
if (!record2) {
|
|
@@ -52720,7 +53082,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52720
53082
|
quiet.push(member);
|
|
52721
53083
|
}
|
|
52722
53084
|
if (record2.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record2.leadDevelopment, record2.leadPreviousRc)) {
|
|
52723
|
-
(0,
|
|
53085
|
+
(0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52724
53086
|
throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
|
|
52725
53087
|
}
|
|
52726
53088
|
for (const member of quiet) {
|
|
@@ -52738,7 +53100,7 @@ async function suiteRcandRun(deps, opts) {
|
|
|
52738
53100
|
}
|
|
52739
53101
|
if (refusals.length) {
|
|
52740
53102
|
const untouched = record2.members.every((m) => !m.tag) && !record2.pinPr;
|
|
52741
|
-
if (untouched) (0,
|
|
53103
|
+
if (untouched) (0, import_node_fs41.rmSync)(deps.recordPath, { force: true });
|
|
52742
53104
|
throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
|
|
52743
53105
|
${refusals.join("\n")}
|
|
52744
53106
|
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 +53119,7 @@ Each module must ship a live release that bind-mounts <deployPath>/data read-onl
|
|
|
52757
53119
|
let running = 0;
|
|
52758
53120
|
const waiting = [];
|
|
52759
53121
|
const results = await Promise.allSettled(record2.members.map(async (member) => {
|
|
52760
|
-
if (running >= 4) await new Promise((
|
|
53122
|
+
if (running >= 4) await new Promise((resolve8) => waiting.push(resolve8));
|
|
52761
53123
|
else running++;
|
|
52762
53124
|
try {
|
|
52763
53125
|
if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
|
|
@@ -52930,11 +53292,11 @@ Each member's live release must serve <data>/main/module.js at its main pin path
|
|
|
52930
53292
|
}
|
|
52931
53293
|
return record2;
|
|
52932
53294
|
}
|
|
52933
|
-
var hotfixRecordPath = (deps) => (0,
|
|
53295
|
+
var hotfixRecordPath = (deps) => (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.recordPath), "hotfix.json");
|
|
52934
53296
|
function readSuiteHotfix(path2) {
|
|
52935
53297
|
let raw;
|
|
52936
53298
|
try {
|
|
52937
|
-
raw = (0,
|
|
53299
|
+
raw = (0, import_node_fs41.readFileSync)(path2, "utf8");
|
|
52938
53300
|
} catch (e) {
|
|
52939
53301
|
if (e.code === "ENOENT") return void 0;
|
|
52940
53302
|
throw e;
|
|
@@ -52977,7 +53339,7 @@ async function suiteHotfix(deps, opts) {
|
|
|
52977
53339
|
if (completed) record2 = void 0;
|
|
52978
53340
|
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
53341
|
if (!record2?.tag) await deps.validateHotfixSummary();
|
|
52980
|
-
if (completed) (0,
|
|
53342
|
+
if (completed) (0, import_node_fs41.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
|
|
52981
53343
|
const rec = record2 ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
|
|
52982
53344
|
const save = () => writeSuiteHotfix(path2, rec);
|
|
52983
53345
|
save();
|
|
@@ -53245,9 +53607,9 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
53245
53607
|
|
|
53246
53608
|
// src/suite-train-io.ts
|
|
53247
53609
|
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
53610
|
var import_node_path51 = require("node:path");
|
|
53611
|
+
var import_node_fs55 = require("node:fs");
|
|
53612
|
+
var import_node_path52 = require("node:path");
|
|
53251
53613
|
init_cli_shared();
|
|
53252
53614
|
|
|
53253
53615
|
// src/command-register-train-operations.ts
|
|
@@ -53428,7 +53790,7 @@ function clean3(out) {
|
|
|
53428
53790
|
return out.trim();
|
|
53429
53791
|
}
|
|
53430
53792
|
function sleeper(deps) {
|
|
53431
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
53793
|
+
return deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
53432
53794
|
}
|
|
53433
53795
|
function normalizeHotfixVersion(input) {
|
|
53434
53796
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -54939,8 +55301,8 @@ function checkHotfixCarries(options) {
|
|
|
54939
55301
|
}
|
|
54940
55302
|
|
|
54941
55303
|
// src/merge-cleanup.ts
|
|
54942
|
-
var
|
|
54943
|
-
var
|
|
55304
|
+
var import_node_fs42 = require("node:fs");
|
|
55305
|
+
var import_node_path39 = require("node:path");
|
|
54944
55306
|
var import_node_os22 = require("node:os");
|
|
54945
55307
|
init_cli_shared();
|
|
54946
55308
|
init_github_client();
|
|
@@ -55348,13 +55710,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
|
|
|
55348
55710
|
return rewritten === base ? null : rewritten;
|
|
55349
55711
|
}
|
|
55350
55712
|
function writeSquashBodyFile(body) {
|
|
55351
|
-
const dir = (0,
|
|
55352
|
-
const path2 = (0,
|
|
55353
|
-
(0,
|
|
55713
|
+
const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path39.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
|
|
55714
|
+
const path2 = (0, import_node_path39.join)(dir, "body.txt");
|
|
55715
|
+
(0, import_node_fs42.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
|
|
55354
55716
|
`, "utf8");
|
|
55355
55717
|
return { path: path2, cleanup: () => {
|
|
55356
55718
|
try {
|
|
55357
|
-
(0,
|
|
55719
|
+
(0, import_node_fs42.rmSync)(dir, { recursive: true, force: true });
|
|
55358
55720
|
} catch {
|
|
55359
55721
|
}
|
|
55360
55722
|
} };
|
|
@@ -55606,8 +55968,8 @@ init_github_client();
|
|
|
55606
55968
|
|
|
55607
55969
|
// src/release-custody.ts
|
|
55608
55970
|
var import_node_crypto21 = require("node:crypto");
|
|
55609
|
-
var
|
|
55610
|
-
var
|
|
55971
|
+
var import_node_fs43 = require("node:fs");
|
|
55972
|
+
var import_node_path40 = require("node:path");
|
|
55611
55973
|
init_github_client();
|
|
55612
55974
|
|
|
55613
55975
|
// src/release-custody-bootstrap.ts
|
|
@@ -55740,9 +56102,9 @@ async function verifyCustodyBootstrap(value, api, controller, requiredContexts,
|
|
|
55740
56102
|
if (legacy.length > 20) reject("local evidence exceeds the bounded migration limit");
|
|
55741
56103
|
const root = `repos/${HUB_RELEASE_REPO}`;
|
|
55742
56104
|
const tag = await api.rest("GET", `${root}/git/ref/tags/${input.tag}`);
|
|
55743
|
-
const
|
|
56105
|
+
const main2 = await api.rest("GET", `${root}/git/ref/heads/main`);
|
|
55744
56106
|
const release = await api.rest("GET", `${root}/releases/tags/${input.tag}`);
|
|
55745
|
-
if (tag.object.sha !== input.tagSha ||
|
|
56107
|
+
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
56108
|
const observed = {};
|
|
55747
56109
|
for (const name of ["gate", "publish", "deploy", "installer", "tenant"]) {
|
|
55748
56110
|
const run = await runAttempt(api, input[name]);
|
|
@@ -56077,7 +56439,7 @@ function reclassifySupersededArchives(state) {
|
|
|
56077
56439
|
}
|
|
56078
56440
|
function localRaw(path2) {
|
|
56079
56441
|
try {
|
|
56080
|
-
return (0,
|
|
56442
|
+
return (0, import_node_fs43.readFileSync)(path2, "utf8");
|
|
56081
56443
|
} catch (e) {
|
|
56082
56444
|
if (e.code === "ENOENT") return null;
|
|
56083
56445
|
throw e;
|
|
@@ -56086,19 +56448,19 @@ function localRaw(path2) {
|
|
|
56086
56448
|
function hydrateLocal(path2, raw) {
|
|
56087
56449
|
const previous = localRaw(path2);
|
|
56088
56450
|
if (previous === raw) return;
|
|
56089
|
-
(0,
|
|
56451
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(path2), { recursive: true });
|
|
56090
56452
|
if (previous !== null) {
|
|
56091
56453
|
const ledger = parseReleaseLedger(previous);
|
|
56092
|
-
const archive = (0,
|
|
56093
|
-
if (!(0,
|
|
56094
|
-
if (raw === null) (0,
|
|
56454
|
+
const archive = (0, import_node_path40.join)((0, import_node_path40.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
|
|
56455
|
+
if (!(0, import_node_fs43.existsSync)(archive)) (0, import_node_fs43.writeFileSync)(archive, previous, { flag: "wx" });
|
|
56456
|
+
if (raw === null) (0, import_node_fs43.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
|
|
56095
56457
|
}
|
|
56096
|
-
if (raw !== null) (0,
|
|
56458
|
+
if (raw !== null) (0, import_node_fs43.writeFileSync)(path2, raw, "utf8");
|
|
56097
56459
|
}
|
|
56098
56460
|
async function commandIdentity(file, args, run, repo) {
|
|
56099
56461
|
if (file === "git" && args[0] === "push") {
|
|
56100
56462
|
const deleting = args.includes("--delete");
|
|
56101
|
-
const refs = args.slice(2).filter((
|
|
56463
|
+
const refs = args.slice(2).filter((arg2) => !arg2.startsWith("-"));
|
|
56102
56464
|
const targets = [];
|
|
56103
56465
|
for (const refspec of refs) {
|
|
56104
56466
|
const [source, destination] = refspec.split(":");
|
|
@@ -56113,7 +56475,7 @@ async function commandIdentity(file, args, run, repo) {
|
|
|
56113
56475
|
return { kind: "github-release", identity: { repo, tag, sha: (await run("git", ["rev-parse", `${tag}^{commit}`])).trim() } };
|
|
56114
56476
|
}
|
|
56115
56477
|
if (file === "gh" && args[0] === "workflow" && args[1] === "run") {
|
|
56116
|
-
const fields = Object.fromEntries(args.flatMap((
|
|
56478
|
+
const fields = Object.fromEntries(args.flatMap((arg2, i) => ["-f", "--field", "-F", "--raw-field"].includes(arg2) ? [args[i + 1].split(/=(.*)/s).slice(0, 2)] : []));
|
|
56117
56479
|
const ref = args.includes("--ref") ? args[args.indexOf("--ref") + 1] : "main";
|
|
56118
56480
|
const sha = (await run("git", ["rev-parse", `origin/${ref}`])).trim();
|
|
56119
56481
|
const before = JSON.parse(await run("gh", [
|
|
@@ -56130,7 +56492,7 @@ async function commandIdentity(file, args, run, repo) {
|
|
|
56130
56492
|
return { kind: "workflow-rerun", identity: { repo, runId, attempt: prior.run_attempt + 1 } };
|
|
56131
56493
|
}
|
|
56132
56494
|
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((
|
|
56495
|
+
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
56496
|
return write ? { kind: "github-write", identity: { repo, command: args } } : null;
|
|
56135
56497
|
}
|
|
56136
56498
|
function effectObserver(api) {
|
|
@@ -56283,8 +56645,8 @@ function withHubReleaseCustody(deps) {
|
|
|
56283
56645
|
}
|
|
56284
56646
|
|
|
56285
56647
|
// src/project-info-sync.ts
|
|
56286
|
-
var
|
|
56287
|
-
var
|
|
56648
|
+
var import_node_fs44 = require("node:fs");
|
|
56649
|
+
var import_node_path41 = require("node:path");
|
|
56288
56650
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
56289
56651
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
56290
56652
|
projectV2 { id }
|
|
@@ -56331,14 +56693,14 @@ function sharedName(entries, fallback) {
|
|
|
56331
56693
|
}
|
|
56332
56694
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
56333
56695
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
56334
|
-
const readmePath = (0,
|
|
56335
|
-
if (!(0,
|
|
56696
|
+
const readmePath = (0, import_node_path41.join)(repoRoot2, "README.md");
|
|
56697
|
+
if (!(0, import_node_fs44.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
56336
56698
|
const entries = entriesFor(project2, projects);
|
|
56337
56699
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
56338
56700
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
56339
56701
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
56340
56702
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
56341
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
56703
|
+
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
56704
|
const lines2 = [
|
|
56343
56705
|
`# ${projectName}`,
|
|
56344
56706
|
"",
|
|
@@ -56357,8 +56719,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
56357
56719
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
56358
56720
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
56359
56721
|
const orgDocs = [
|
|
56360
|
-
(0,
|
|
56361
|
-
(0,
|
|
56722
|
+
(0, import_node_fs44.existsSync)((0, import_node_path41.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
|
|
56723
|
+
(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
56724
|
].filter(Boolean);
|
|
56363
56725
|
if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
|
|
56364
56726
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
|
|
@@ -56380,8 +56742,8 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
56380
56742
|
}
|
|
56381
56743
|
|
|
56382
56744
|
// src/train-commands.ts
|
|
56383
|
-
var
|
|
56384
|
-
var
|
|
56745
|
+
var import_node_fs45 = require("node:fs");
|
|
56746
|
+
var import_node_path42 = require("node:path");
|
|
56385
56747
|
init_cli_shared();
|
|
56386
56748
|
init_clean_exit();
|
|
56387
56749
|
init_client_version();
|
|
@@ -56396,7 +56758,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
56396
56758
|
}
|
|
56397
56759
|
function readRepoVersion() {
|
|
56398
56760
|
try {
|
|
56399
|
-
return JSON.parse((0,
|
|
56761
|
+
return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
56400
56762
|
} catch {
|
|
56401
56763
|
return void 0;
|
|
56402
56764
|
}
|
|
@@ -56507,7 +56869,7 @@ function releaseProgressVerdict(status, subject = "Release") {
|
|
|
56507
56869
|
|
|
56508
56870
|
// src/suite-pin.ts
|
|
56509
56871
|
var import_promises6 = require("node:fs/promises");
|
|
56510
|
-
var
|
|
56872
|
+
var import_node_path43 = require("node:path");
|
|
56511
56873
|
var SuitePinError = class _SuitePinError extends Error {
|
|
56512
56874
|
diagnostic;
|
|
56513
56875
|
constructor(repo, tag, error) {
|
|
@@ -56575,8 +56937,8 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
56575
56937
|
if (input.carriedOnly) return { status: "unchanged" };
|
|
56576
56938
|
throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
|
|
56577
56939
|
}
|
|
56578
|
-
await (0, import_promises6.mkdir)((0,
|
|
56579
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
56940
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
56941
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56580
56942
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56581
56943
|
await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56582
56944
|
if (prs.length) {
|
|
@@ -56588,7 +56950,7 @@ async function openSuiteCutPinPr(deps, input) {
|
|
|
56588
56950
|
await run("git", ["switch", "-c", branch]);
|
|
56589
56951
|
}
|
|
56590
56952
|
for (const stage of ["rc", "main"]) {
|
|
56591
|
-
const path2 = (0,
|
|
56953
|
+
const path2 = (0, import_node_path43.join)(scratch, `${stage}-pins.json`);
|
|
56592
56954
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
|
|
56593
56955
|
await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
|
|
56594
56956
|
}
|
|
@@ -56641,12 +57003,12 @@ async function openSuiteRestorePinPr(deps, input) {
|
|
|
56641
57003
|
}
|
|
56642
57004
|
});
|
|
56643
57005
|
if (!todo.length) return void 0;
|
|
56644
|
-
await (0, import_promises6.mkdir)((0,
|
|
56645
|
-
scratch = await (0, import_promises6.mkdtemp)((0,
|
|
57006
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
57007
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56646
57008
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56647
57009
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56648
57010
|
await run("git", ["switch", "-c", branch]);
|
|
56649
|
-
const path2 = (0,
|
|
57011
|
+
const path2 = (0, import_node_path43.join)(scratch, "main-pins.json");
|
|
56650
57012
|
await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
|
|
56651
57013
|
await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
|
|
56652
57014
|
await run("git", ["add", "seed/modules.json"]);
|
|
@@ -56720,8 +57082,8 @@ async function openSuitePinPr(deps, input) {
|
|
|
56720
57082
|
if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
|
|
56721
57083
|
const open2 = onBranch.find((p) => p.state === "OPEN");
|
|
56722
57084
|
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,
|
|
57085
|
+
await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
|
|
57086
|
+
scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
|
|
56725
57087
|
const run = (file, args) => deps.run(file, args, scratch);
|
|
56726
57088
|
await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
|
|
56727
57089
|
await run("git", ["switch", "-c", branch]);
|
|
@@ -56773,7 +57135,7 @@ function prTitleRefusal(title) {
|
|
|
56773
57135
|
|
|
56774
57136
|
// src/command-register-collaboration.ts
|
|
56775
57137
|
var import_node_child_process18 = require("node:child_process");
|
|
56776
|
-
var
|
|
57138
|
+
var import_node_fs54 = require("node:fs");
|
|
56777
57139
|
var import_promises8 = require("node:fs/promises");
|
|
56778
57140
|
init_clean_exit();
|
|
56779
57141
|
init_cli_shared();
|
|
@@ -56797,7 +57159,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
|
|
|
56797
57159
|
}
|
|
56798
57160
|
|
|
56799
57161
|
// src/command-register-collaboration.ts
|
|
56800
|
-
var
|
|
57162
|
+
var import_node_path50 = require("node:path");
|
|
56801
57163
|
init_house_map();
|
|
56802
57164
|
|
|
56803
57165
|
// src/issue-body.ts
|
|
@@ -56914,7 +57276,7 @@ init_github_client();
|
|
|
56914
57276
|
init_github_quota();
|
|
56915
57277
|
|
|
56916
57278
|
// src/issue-commands.ts
|
|
56917
|
-
var
|
|
57279
|
+
var import_node_fs46 = require("node:fs");
|
|
56918
57280
|
var import_node_crypto22 = require("node:crypto");
|
|
56919
57281
|
init_cli_shared();
|
|
56920
57282
|
init_clean_exit();
|
|
@@ -57107,7 +57469,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
57107
57469
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
57108
57470
|
const patch = {};
|
|
57109
57471
|
let bodyChanged = false;
|
|
57110
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
57472
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs46.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
57111
57473
|
if (options.titleFile !== void 0) {
|
|
57112
57474
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
57113
57475
|
} else if (options.title !== void 0) {
|
|
@@ -57774,7 +58136,7 @@ function extendCreateCommand(issue, batchAttach) {
|
|
|
57774
58136
|
if (opts.batch) {
|
|
57775
58137
|
let specs;
|
|
57776
58138
|
try {
|
|
57777
|
-
const raw = (0,
|
|
58139
|
+
const raw = (0, import_node_fs46.readFileSync)(opts.batch, "utf8");
|
|
57778
58140
|
specs = JSON.parse(raw);
|
|
57779
58141
|
if (!Array.isArray(specs)) {
|
|
57780
58142
|
const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
|
|
@@ -57851,8 +58213,8 @@ ${lines2}`, {
|
|
|
57851
58213
|
}
|
|
57852
58214
|
|
|
57853
58215
|
// src/post-merge-recon.ts
|
|
57854
|
-
var
|
|
57855
|
-
var
|
|
58216
|
+
var import_node_fs47 = require("node:fs");
|
|
58217
|
+
var import_node_path44 = require("node:path");
|
|
57856
58218
|
|
|
57857
58219
|
// src/cross-repo-filing-issue.ts
|
|
57858
58220
|
init_github_client();
|
|
@@ -58022,16 +58384,16 @@ function buildPostMergeReconRecovery(input) {
|
|
|
58022
58384
|
}
|
|
58023
58385
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
58024
58386
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
58025
|
-
(0,
|
|
58026
|
-
(0,
|
|
58387
|
+
(0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(path2), { recursive: true });
|
|
58388
|
+
(0, import_node_fs47.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
58027
58389
|
`, "utf8");
|
|
58028
58390
|
return path2;
|
|
58029
58391
|
}
|
|
58030
58392
|
function clearPostMergeReconRecovery(cwd, repo, pr) {
|
|
58031
58393
|
const path2 = postMergeReconStatePath(cwd, repo, pr);
|
|
58032
|
-
if (!(0,
|
|
58394
|
+
if (!(0, import_node_fs47.existsSync)(path2)) return;
|
|
58033
58395
|
try {
|
|
58034
|
-
(0,
|
|
58396
|
+
(0, import_node_fs47.unlinkSync)(path2);
|
|
58035
58397
|
} catch {
|
|
58036
58398
|
}
|
|
58037
58399
|
}
|
|
@@ -58066,7 +58428,7 @@ var import_promises7 = require("node:fs/promises");
|
|
|
58066
58428
|
init_cli_shared();
|
|
58067
58429
|
|
|
58068
58430
|
// src/api-read.ts
|
|
58069
|
-
var
|
|
58431
|
+
var import_node_fs48 = require("node:fs");
|
|
58070
58432
|
init_cli_shared();
|
|
58071
58433
|
var API_ACCEPT_HEADERS = {
|
|
58072
58434
|
json: "application/vnd.github+json",
|
|
@@ -58331,7 +58693,7 @@ function registerApiReadCommands(program3, overrides) {
|
|
|
58331
58693
|
if (req.workflowRuns) body = sliceWorkflowRunsPage(body, req.workflowRuns);
|
|
58332
58694
|
if (o.jq !== void 0) body = await jqRun(o.jq, body, localJqEnv(), ...bindings.length ? [bindings] : []);
|
|
58333
58695
|
if (zip) {
|
|
58334
|
-
(0,
|
|
58696
|
+
(0, import_node_fs48.writeFileSync)(o.out, body, "latin1");
|
|
58335
58697
|
return;
|
|
58336
58698
|
}
|
|
58337
58699
|
} catch (e) {
|
|
@@ -59872,9 +60234,9 @@ function registerPrLifecycleCommands(program3) {
|
|
|
59872
60234
|
|
|
59873
60235
|
// src/review-verdict.ts
|
|
59874
60236
|
var import_node_child_process15 = require("node:child_process");
|
|
59875
|
-
var
|
|
60237
|
+
var import_node_fs49 = require("node:fs");
|
|
59876
60238
|
var import_node_os24 = require("node:os");
|
|
59877
|
-
var
|
|
60239
|
+
var import_node_path45 = require("node:path");
|
|
59878
60240
|
init_cli_shared();
|
|
59879
60241
|
var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
|
|
59880
60242
|
var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
|
|
@@ -59970,7 +60332,7 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
59970
60332
|
}
|
|
59971
60333
|
}
|
|
59972
60334
|
function computePrPatchId(number, repo) {
|
|
59973
|
-
return new Promise((
|
|
60335
|
+
return new Promise((resolve8, reject2) => {
|
|
59974
60336
|
const gh2 = (0, import_node_child_process15.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
59975
60337
|
const git3 = (0, import_node_child_process15.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
59976
60338
|
let out = "";
|
|
@@ -60008,7 +60370,7 @@ function computePrPatchId(number, repo) {
|
|
|
60008
60370
|
if (gitCode !== 0) return reject2(new Error(`git patch-id --stable exited ${gitCode}: ${gitErr.trim()}`));
|
|
60009
60371
|
const id = out.trim().split(/\s+/)[0];
|
|
60010
60372
|
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
|
-
|
|
60373
|
+
resolve8(id);
|
|
60012
60374
|
};
|
|
60013
60375
|
gh2.on("close", (code) => {
|
|
60014
60376
|
ghCode = code;
|
|
@@ -60039,15 +60401,15 @@ async function readPrIssueComments(number, repo) {
|
|
|
60039
60401
|
return comments;
|
|
60040
60402
|
}
|
|
60041
60403
|
async function postPrCommentFromFile(number, repo, body) {
|
|
60042
|
-
const dir = (0,
|
|
60043
|
-
const path2 = (0,
|
|
60404
|
+
const dir = (0, import_node_fs49.mkdtempSync)((0, import_node_path45.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
|
|
60405
|
+
const path2 = (0, import_node_path45.join)(dir, "body.md");
|
|
60044
60406
|
try {
|
|
60045
|
-
(0,
|
|
60407
|
+
(0, import_node_fs49.writeFileSync)(path2, body, "utf8");
|
|
60046
60408
|
const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
|
|
60047
60409
|
return stdout.trim();
|
|
60048
60410
|
} finally {
|
|
60049
60411
|
try {
|
|
60050
|
-
(0,
|
|
60412
|
+
(0, import_node_fs49.rmSync)(dir, { recursive: true, force: true });
|
|
60051
60413
|
} catch {
|
|
60052
60414
|
}
|
|
60053
60415
|
}
|
|
@@ -60202,7 +60564,7 @@ init_cli_shared();
|
|
|
60202
60564
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
60203
60565
|
function catFileBatch(root, ref, paths) {
|
|
60204
60566
|
if (paths.length === 0) return Promise.resolve([]);
|
|
60205
|
-
return new Promise((
|
|
60567
|
+
return new Promise((resolve8) => {
|
|
60206
60568
|
const chunks = [];
|
|
60207
60569
|
let settled2 = false;
|
|
60208
60570
|
const child2 = (0, import_node_child_process16.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
@@ -60210,7 +60572,7 @@ function catFileBatch(root, ref, paths) {
|
|
|
60210
60572
|
if (settled2) return;
|
|
60211
60573
|
settled2 = true;
|
|
60212
60574
|
clearTimeout(timer);
|
|
60213
|
-
|
|
60575
|
+
resolve8(ok ? parseCatFileBatch(Buffer.concat(chunks), paths.length) : []);
|
|
60214
60576
|
};
|
|
60215
60577
|
const timer = setTimeout(() => {
|
|
60216
60578
|
child2.kill("SIGKILL");
|
|
@@ -60460,18 +60822,18 @@ function createPreparationDeps(input, io = {}) {
|
|
|
60460
60822
|
}
|
|
60461
60823
|
|
|
60462
60824
|
// src/worktree-residue-sweep.ts
|
|
60463
|
-
var
|
|
60464
|
-
var
|
|
60825
|
+
var import_node_fs53 = require("node:fs");
|
|
60826
|
+
var import_node_path49 = require("node:path");
|
|
60465
60827
|
|
|
60466
60828
|
// src/worktree-merge-cleanup.ts
|
|
60467
|
-
var
|
|
60468
|
-
var
|
|
60829
|
+
var import_node_fs52 = require("node:fs");
|
|
60830
|
+
var import_node_path48 = require("node:path");
|
|
60469
60831
|
init_cli_shared();
|
|
60470
60832
|
|
|
60471
60833
|
// src/stage-runner.ts
|
|
60472
60834
|
var import_node_child_process17 = require("node:child_process");
|
|
60473
|
-
var
|
|
60474
|
-
var
|
|
60835
|
+
var import_node_fs51 = require("node:fs");
|
|
60836
|
+
var import_node_path47 = require("node:path");
|
|
60475
60837
|
var import_node_net = require("node:net");
|
|
60476
60838
|
var import_node_util5 = require("node:util");
|
|
60477
60839
|
|
|
@@ -60481,8 +60843,8 @@ function normalizeEol2(s) {
|
|
|
60481
60843
|
}
|
|
60482
60844
|
|
|
60483
60845
|
// src/env-detection.ts
|
|
60484
|
-
var
|
|
60485
|
-
var
|
|
60846
|
+
var import_node_fs50 = require("node:fs");
|
|
60847
|
+
var import_node_path46 = require("node:path");
|
|
60486
60848
|
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
|
|
60487
60849
|
function isEnvBaseName(name) {
|
|
60488
60850
|
return name.toLowerCase() === ".env";
|
|
@@ -60492,7 +60854,7 @@ function findEnvFiles(root) {
|
|
|
60492
60854
|
const walk2 = (dir, rel) => {
|
|
60493
60855
|
let entries;
|
|
60494
60856
|
try {
|
|
60495
|
-
entries = (0,
|
|
60857
|
+
entries = (0, import_node_fs50.readdirSync)(dir, { withFileTypes: true });
|
|
60496
60858
|
} catch {
|
|
60497
60859
|
return;
|
|
60498
60860
|
}
|
|
@@ -60500,13 +60862,13 @@ function findEnvFiles(root) {
|
|
|
60500
60862
|
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
60501
60863
|
if (entry.isDirectory()) {
|
|
60502
60864
|
if (SKIPPED_DIRS.has(entry.name)) continue;
|
|
60503
|
-
walk2((0,
|
|
60865
|
+
walk2((0, import_node_path46.join)(dir, entry.name), relPath);
|
|
60504
60866
|
continue;
|
|
60505
60867
|
}
|
|
60506
60868
|
if (isEnvBaseName(entry.name)) {
|
|
60507
60869
|
let isDir = false;
|
|
60508
60870
|
try {
|
|
60509
|
-
isDir = (0,
|
|
60871
|
+
isDir = (0, import_node_fs50.lstatSync)((0, import_node_path46.join)(dir, entry.name)).isDirectory();
|
|
60510
60872
|
} catch {
|
|
60511
60873
|
}
|
|
60512
60874
|
if (!isDir) found.push(relPath);
|
|
@@ -60645,12 +61007,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
|
|
|
60645
61007
|
function stageBash() {
|
|
60646
61008
|
if (process.platform !== "win32") return "bash";
|
|
60647
61009
|
const configured = process.env.SHELL;
|
|
60648
|
-
if (configured && (0,
|
|
61010
|
+
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
61011
|
const candidates = [
|
|
60650
|
-
(0,
|
|
60651
|
-
...process.env.LOCALAPPDATA ? [(0,
|
|
61012
|
+
(0, import_node_path47.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
|
|
61013
|
+
...process.env.LOCALAPPDATA ? [(0, import_node_path47.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
|
|
60652
61014
|
];
|
|
60653
|
-
const bash = candidates.find((path2) => (0,
|
|
61015
|
+
const bash = candidates.find((path2) => (0, import_node_fs51.existsSync)(path2));
|
|
60654
61016
|
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
61017
|
return bash;
|
|
60656
61018
|
}
|
|
@@ -60671,7 +61033,7 @@ function healthPollIntervalMs() {
|
|
|
60671
61033
|
return HEALTH_POLL_INTERVAL_MS;
|
|
60672
61034
|
}
|
|
60673
61035
|
function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
60674
|
-
return new Promise((
|
|
61036
|
+
return new Promise((resolve8, reject2) => {
|
|
60675
61037
|
let settled2 = false;
|
|
60676
61038
|
const finish = (fn) => {
|
|
60677
61039
|
if (settled2) return;
|
|
@@ -60681,7 +61043,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
|
60681
61043
|
child2.removeAllListeners("exit");
|
|
60682
61044
|
fn();
|
|
60683
61045
|
};
|
|
60684
|
-
const timer = setTimeout(() => finish(
|
|
61046
|
+
const timer = setTimeout(() => finish(resolve8), graceMs);
|
|
60685
61047
|
child2.on("error", (err) => finish(() => reject2(new Error(`stage process failed to start: ${err.message}`))));
|
|
60686
61048
|
child2.on("exit", (code, signal) => {
|
|
60687
61049
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -60788,7 +61150,7 @@ function appendForceRecreate(up) {
|
|
|
60788
61150
|
return `${up.trimEnd()} --force-recreate`;
|
|
60789
61151
|
}
|
|
60790
61152
|
function stageStatePath(cwd = process.cwd()) {
|
|
60791
|
-
return (0,
|
|
61153
|
+
return (0, import_node_path47.join)(cwd, "tmp", "stage", "state.json");
|
|
60792
61154
|
}
|
|
60793
61155
|
function normPath(path2) {
|
|
60794
61156
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -60874,10 +61236,10 @@ function pickStagePort(range, isFree) {
|
|
|
60874
61236
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
60875
61237
|
}
|
|
60876
61238
|
function isPortFree(port) {
|
|
60877
|
-
return new Promise((
|
|
61239
|
+
return new Promise((resolve8) => {
|
|
60878
61240
|
const srv = (0, import_node_net.createServer)();
|
|
60879
|
-
srv.once("error", () =>
|
|
60880
|
-
srv.once("listening", () => srv.close(() =>
|
|
61241
|
+
srv.once("error", () => resolve8(false));
|
|
61242
|
+
srv.once("listening", () => srv.close(() => resolve8(true)));
|
|
60881
61243
|
srv.listen(port, "127.0.0.1");
|
|
60882
61244
|
});
|
|
60883
61245
|
}
|
|
@@ -61005,8 +61367,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
61005
61367
|
}
|
|
61006
61368
|
function composeResolvesPort(cwd) {
|
|
61007
61369
|
if (process.env.PORT) return true;
|
|
61008
|
-
const envFile = (0,
|
|
61009
|
-
return (0,
|
|
61370
|
+
const envFile = (0, import_node_path47.join)(cwd, ".env");
|
|
61371
|
+
return (0, import_node_fs51.existsSync)(envFile) && envFileKeys((0, import_node_fs51.readFileSync)(envFile, "utf8")).has("PORT");
|
|
61010
61372
|
}
|
|
61011
61373
|
function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
61012
61374
|
const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
|
|
@@ -61021,13 +61383,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
|
|
|
61021
61383
|
function stageComposeFiles(cwd) {
|
|
61022
61384
|
const files = ["docker-compose.yml"];
|
|
61023
61385
|
for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
|
|
61024
|
-
if ((0,
|
|
61386
|
+
if ((0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, conventional))) files.push(conventional);
|
|
61025
61387
|
}
|
|
61026
61388
|
return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
|
|
61027
61389
|
}
|
|
61028
61390
|
async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
61029
61391
|
const keys = Object.keys(vaultEnvMerge ?? {});
|
|
61030
|
-
if (!keys.length || !(0,
|
|
61392
|
+
if (!keys.length || !(0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, "docker-compose.yml"))) return {};
|
|
61031
61393
|
let configJson;
|
|
61032
61394
|
try {
|
|
61033
61395
|
const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
|
|
@@ -61043,21 +61405,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
|
|
|
61043
61405
|
}
|
|
61044
61406
|
const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
|
|
61045
61407
|
if (!body) return {};
|
|
61046
|
-
const overridePath = (0,
|
|
61047
|
-
(0,
|
|
61048
|
-
(0,
|
|
61408
|
+
const overridePath = (0, import_node_path47.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
|
|
61409
|
+
(0, import_node_fs51.mkdirSync)((0, import_node_path47.join)(cwd, "tmp", "stage"), { recursive: true });
|
|
61410
|
+
(0, import_node_fs51.writeFileSync)(overridePath, body, "utf8");
|
|
61049
61411
|
return stageComposeFileEnv(stageComposeFiles(cwd));
|
|
61050
61412
|
}
|
|
61051
61413
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
61052
61414
|
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,
|
|
61415
|
+
const target = (0, import_node_path47.join)(cwd, config.ensureEnv.target);
|
|
61416
|
+
const example = (0, import_node_path47.join)(cwd, config.ensureEnv.example);
|
|
61417
|
+
if (!(0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
61418
|
+
(0, import_node_fs51.copyFileSync)(example, target);
|
|
61419
|
+
} else if ((0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
|
|
61420
|
+
const stale = detectStaleEnvFile((0, import_node_fs51.readFileSync)(example, "utf8"), (0, import_node_fs51.readFileSync)(target, "utf8"), {
|
|
61421
|
+
exampleMtimeMs: (0, import_node_fs51.statSync)(example).mtimeMs,
|
|
61422
|
+
targetMtimeMs: (0, import_node_fs51.statSync)(target).mtimeMs
|
|
61061
61423
|
});
|
|
61062
61424
|
if (stale) {
|
|
61063
61425
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running mmi-cli stage`;
|
|
@@ -61065,8 +61427,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
61065
61427
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
61066
61428
|
}
|
|
61067
61429
|
}
|
|
61068
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
61069
|
-
(0,
|
|
61430
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs51.existsSync)(target)) {
|
|
61431
|
+
(0, import_node_fs51.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs51.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
61070
61432
|
}
|
|
61071
61433
|
}
|
|
61072
61434
|
async function gitText(cwd, args) {
|
|
@@ -61096,20 +61458,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
61096
61458
|
return void 0;
|
|
61097
61459
|
}
|
|
61098
61460
|
function readState(path2) {
|
|
61099
|
-
if (!(0,
|
|
61461
|
+
if (!(0, import_node_fs51.existsSync)(path2)) return null;
|
|
61100
61462
|
try {
|
|
61101
|
-
return JSON.parse((0,
|
|
61463
|
+
return JSON.parse((0, import_node_fs51.readFileSync)(path2, "utf8"));
|
|
61102
61464
|
} catch {
|
|
61103
61465
|
return null;
|
|
61104
61466
|
}
|
|
61105
61467
|
}
|
|
61106
61468
|
function mkdirFor(path2) {
|
|
61107
61469
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
61108
|
-
(0,
|
|
61470
|
+
(0, import_node_fs51.mkdirSync)(dir, { recursive: true });
|
|
61109
61471
|
}
|
|
61110
61472
|
function writeState(path2, state) {
|
|
61111
61473
|
mkdirFor(path2);
|
|
61112
|
-
(0,
|
|
61474
|
+
(0, import_node_fs51.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
61113
61475
|
}
|
|
61114
61476
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
61115
61477
|
const reservation = {
|
|
@@ -61138,7 +61500,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
|
|
|
61138
61500
|
);
|
|
61139
61501
|
}
|
|
61140
61502
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
61141
|
-
(0,
|
|
61503
|
+
(0, import_node_fs51.rmSync)(path2, { force: true });
|
|
61142
61504
|
}
|
|
61143
61505
|
}
|
|
61144
61506
|
async function killTree(pid) {
|
|
@@ -61155,7 +61517,7 @@ async function killTree(pid) {
|
|
|
61155
61517
|
} catch {
|
|
61156
61518
|
}
|
|
61157
61519
|
}
|
|
61158
|
-
await new Promise((
|
|
61520
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
61159
61521
|
try {
|
|
61160
61522
|
process.kill(-pid, "SIGKILL");
|
|
61161
61523
|
} catch {
|
|
@@ -61228,7 +61590,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
61228
61590
|
} catch (e) {
|
|
61229
61591
|
last = e.message;
|
|
61230
61592
|
}
|
|
61231
|
-
await new Promise((
|
|
61593
|
+
await new Promise((resolve8) => setTimeout(resolve8, healthPollIntervalMs()));
|
|
61232
61594
|
}
|
|
61233
61595
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
61234
61596
|
}
|
|
@@ -61385,8 +61747,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
61385
61747
|
});
|
|
61386
61748
|
}
|
|
61387
61749
|
} catch (e) {
|
|
61388
|
-
(0,
|
|
61389
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
61750
|
+
(0, import_node_fs51.rmSync)(statePath, { force: true });
|
|
61751
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs51.rmSync)(globalStatePath, { force: true });
|
|
61390
61752
|
throw e;
|
|
61391
61753
|
}
|
|
61392
61754
|
const started = await startStage(config, {
|
|
@@ -61405,9 +61767,9 @@ function normPath2(p) {
|
|
|
61405
61767
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
61406
61768
|
}
|
|
61407
61769
|
function unlinkNodeModulesJunction(wtPath) {
|
|
61408
|
-
const nm = (0,
|
|
61770
|
+
const nm = (0, import_node_path48.join)(wtPath, "node_modules");
|
|
61409
61771
|
try {
|
|
61410
|
-
if ((0,
|
|
61772
|
+
if ((0, import_node_fs52.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs52.rmdirSync)(nm);
|
|
61411
61773
|
return { ok: true };
|
|
61412
61774
|
} catch (e) {
|
|
61413
61775
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -61415,11 +61777,11 @@ function unlinkNodeModulesJunction(wtPath) {
|
|
|
61415
61777
|
}
|
|
61416
61778
|
}
|
|
61417
61779
|
function defaultSleep2(ms) {
|
|
61418
|
-
return new Promise((
|
|
61780
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
61419
61781
|
}
|
|
61420
61782
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
61421
|
-
const { sleep: sleep2 = defaultSleep2, removeEmptyDir =
|
|
61422
|
-
const removeRecursive = options.removeRecursive ?? ((path2) => (0,
|
|
61783
|
+
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs52.rmdirSync, exists = import_node_fs52.existsSync } = options;
|
|
61784
|
+
const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs52.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
|
|
61423
61785
|
const junction = unlinkNodeModulesJunction(wtPath);
|
|
61424
61786
|
if (!junction.ok) return junction;
|
|
61425
61787
|
try {
|
|
@@ -61542,28 +61904,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
|
|
|
61542
61904
|
}
|
|
61543
61905
|
async function listNestedIgnoredNodeModules(wtPath, execGit) {
|
|
61544
61906
|
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,
|
|
61907
|
+
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
61908
|
}
|
|
61547
61909
|
function safeRemoveTree(path2) {
|
|
61548
|
-
const stat4 = (0,
|
|
61910
|
+
const stat4 = (0, import_node_fs52.lstatSync)(path2);
|
|
61549
61911
|
if (stat4.isSymbolicLink()) {
|
|
61550
61912
|
try {
|
|
61551
|
-
(0,
|
|
61913
|
+
(0, import_node_fs52.rmdirSync)(path2);
|
|
61552
61914
|
} catch {
|
|
61553
|
-
(0,
|
|
61915
|
+
(0, import_node_fs52.unlinkSync)(path2);
|
|
61554
61916
|
}
|
|
61555
61917
|
return;
|
|
61556
61918
|
}
|
|
61557
61919
|
if (stat4.isDirectory()) {
|
|
61558
|
-
for (const entry of (0,
|
|
61559
|
-
(0,
|
|
61920
|
+
for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0, import_node_path48.join)(path2, entry));
|
|
61921
|
+
(0, import_node_fs52.rmdirSync)(path2);
|
|
61560
61922
|
return;
|
|
61561
61923
|
}
|
|
61562
|
-
(0,
|
|
61924
|
+
(0, import_node_fs52.unlinkSync)(path2);
|
|
61563
61925
|
}
|
|
61564
61926
|
async function removeWorktreeNodeModules(wtPath) {
|
|
61565
61927
|
try {
|
|
61566
|
-
safeRemoveTree((0,
|
|
61928
|
+
safeRemoveTree((0, import_node_path48.join)(wtPath, "node_modules"));
|
|
61567
61929
|
return { ok: true };
|
|
61568
61930
|
} catch (e) {
|
|
61569
61931
|
if (e.code === "ENOENT") return { ok: true };
|
|
@@ -61575,7 +61937,7 @@ function errorMessage(e) {
|
|
|
61575
61937
|
}
|
|
61576
61938
|
function resolvedOrRaw(path2) {
|
|
61577
61939
|
try {
|
|
61578
|
-
return normPath2((0,
|
|
61940
|
+
return normPath2((0, import_node_fs52.realpathSync)(path2));
|
|
61579
61941
|
} catch {
|
|
61580
61942
|
return normPath2(path2);
|
|
61581
61943
|
}
|
|
@@ -61583,10 +61945,10 @@ function resolvedOrRaw(path2) {
|
|
|
61583
61945
|
function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
61584
61946
|
let realRoot;
|
|
61585
61947
|
try {
|
|
61586
|
-
if ((0,
|
|
61948
|
+
if ((0, import_node_fs52.lstatSync)(root).isSymbolicLink()) {
|
|
61587
61949
|
return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
|
|
61588
61950
|
}
|
|
61589
|
-
realRoot = normPath2((0,
|
|
61951
|
+
realRoot = normPath2((0, import_node_fs52.realpathSync)(root));
|
|
61590
61952
|
} catch (e) {
|
|
61591
61953
|
if (e.code === "ENOENT") return { ok: true, unlinked: [] };
|
|
61592
61954
|
return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
|
|
@@ -61602,17 +61964,17 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
|
|
|
61602
61964
|
const dir = stack.pop();
|
|
61603
61965
|
let entries;
|
|
61604
61966
|
try {
|
|
61605
|
-
entries = (0,
|
|
61967
|
+
entries = (0, import_node_fs52.readdirSync)(dir, { withFileTypes: true });
|
|
61606
61968
|
} catch (e) {
|
|
61607
61969
|
if (e.code === "ENOENT") continue;
|
|
61608
61970
|
return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
|
|
61609
61971
|
}
|
|
61610
61972
|
for (const entry of entries) {
|
|
61611
|
-
const child2 = (0,
|
|
61973
|
+
const child2 = (0, import_node_path48.join)(dir, entry.name);
|
|
61612
61974
|
if (entry.isSymbolicLink()) {
|
|
61613
61975
|
let target = "";
|
|
61614
61976
|
try {
|
|
61615
|
-
target = normPath2((0,
|
|
61977
|
+
target = normPath2((0, import_node_fs52.realpathSync)(child2));
|
|
61616
61978
|
} catch {
|
|
61617
61979
|
target = "";
|
|
61618
61980
|
}
|
|
@@ -61795,7 +62157,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61795
62157
|
return report;
|
|
61796
62158
|
}
|
|
61797
62159
|
const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
61798
|
-
const pathExists = options.pathExists ??
|
|
62160
|
+
const pathExists = options.pathExists ?? import_node_fs52.existsSync;
|
|
61799
62161
|
let afterWorktrees = [];
|
|
61800
62162
|
try {
|
|
61801
62163
|
afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
|
|
@@ -61935,7 +62297,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
61935
62297
|
const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
|
|
61936
62298
|
if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
|
|
61937
62299
|
unlinkedReparsePoints.push(...preHelperGuard.unlinked);
|
|
61938
|
-
if (pathExists((0,
|
|
62300
|
+
if (pathExists((0, import_node_path48.join)(wtPath, "node_modules"))) {
|
|
61939
62301
|
const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
|
|
61940
62302
|
if (!nmRemoved.ok) {
|
|
61941
62303
|
report.worktree = {
|
|
@@ -62071,18 +62433,18 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
62071
62433
|
// src/worktree-residue-sweep.ts
|
|
62072
62434
|
var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
|
|
62073
62435
|
function canonicalWorktreeRoot(primary) {
|
|
62074
|
-
return (0,
|
|
62436
|
+
return (0, import_node_path49.join)((0, import_node_path49.dirname)(primary), "mmi-worktrees", (0, import_node_path49.basename)(primary));
|
|
62075
62437
|
}
|
|
62076
|
-
function
|
|
62438
|
+
function norm2(p) {
|
|
62077
62439
|
const s = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
62078
62440
|
return process.platform === "win32" ? s.toLowerCase() : s;
|
|
62079
62441
|
}
|
|
62080
62442
|
function residueLedgerPath(gitCommonDir) {
|
|
62081
|
-
return (0,
|
|
62443
|
+
return (0, import_node_path49.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
|
|
62082
62444
|
}
|
|
62083
62445
|
function readPaths(file, deps) {
|
|
62084
62446
|
try {
|
|
62085
|
-
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0,
|
|
62447
|
+
const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs53.readFileSync)(f, "utf8")))(file));
|
|
62086
62448
|
return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
|
|
62087
62449
|
} catch {
|
|
62088
62450
|
return [];
|
|
@@ -62091,8 +62453,8 @@ function readPaths(file, deps) {
|
|
|
62091
62453
|
function writePaths(file, paths, deps) {
|
|
62092
62454
|
try {
|
|
62093
62455
|
const write = deps.writeLedger ?? ((f, t) => {
|
|
62094
|
-
(0,
|
|
62095
|
-
(0,
|
|
62456
|
+
(0, import_node_fs53.mkdirSync)((0, import_node_path49.dirname)(f), { recursive: true });
|
|
62457
|
+
(0, import_node_fs53.writeFileSync)(f, t, "utf8");
|
|
62096
62458
|
});
|
|
62097
62459
|
write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
|
|
62098
62460
|
`);
|
|
@@ -62102,29 +62464,29 @@ function writePaths(file, paths, deps) {
|
|
|
62102
62464
|
function recordResidue(gitCommonDir, path2, deps = {}) {
|
|
62103
62465
|
const file = residueLedgerPath(gitCommonDir);
|
|
62104
62466
|
const paths = readPaths(file, deps);
|
|
62105
|
-
if (!paths.some((p) =>
|
|
62467
|
+
if (!paths.some((p) => norm2(p) === norm2(path2))) paths.push(path2);
|
|
62106
62468
|
writePaths(file, paths, deps);
|
|
62107
62469
|
}
|
|
62108
62470
|
async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
62109
|
-
const exists = deps.exists ??
|
|
62110
|
-
const readDir = deps.readDir ?? ((p) => (0,
|
|
62471
|
+
const exists = deps.exists ?? import_node_fs53.existsSync;
|
|
62472
|
+
const readDir = deps.readDir ?? ((p) => (0, import_node_fs53.readdirSync)(p));
|
|
62111
62473
|
const isDir = deps.isDir ?? ((p) => {
|
|
62112
62474
|
try {
|
|
62113
|
-
return (0,
|
|
62475
|
+
return (0, import_node_fs53.statSync)(p).isDirectory();
|
|
62114
62476
|
} catch {
|
|
62115
62477
|
return false;
|
|
62116
62478
|
}
|
|
62117
62479
|
});
|
|
62118
62480
|
const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
|
|
62119
|
-
const removeEmpty = deps.removeEmpty ?? ((p) => (0,
|
|
62481
|
+
const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs53.rmdirSync)(p));
|
|
62120
62482
|
const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
|
|
62121
|
-
const live = new Set(registered.map(
|
|
62122
|
-
const primary = registered[0] ?
|
|
62483
|
+
const live = new Set(registered.map(norm2));
|
|
62484
|
+
const primary = registered[0] ? norm2(registered[0]) : void 0;
|
|
62123
62485
|
const file = residueLedgerPath(gitCommonDir);
|
|
62124
62486
|
const recorded = readPaths(file, deps);
|
|
62125
62487
|
const remaining = [];
|
|
62126
62488
|
for (const path2 of recorded) {
|
|
62127
|
-
const key =
|
|
62489
|
+
const key = norm2(path2);
|
|
62128
62490
|
if (live.has(key) || key === primary) {
|
|
62129
62491
|
report.dropped.push(path2);
|
|
62130
62492
|
continue;
|
|
@@ -62141,9 +62503,9 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
62141
62503
|
}
|
|
62142
62504
|
}
|
|
62143
62505
|
if (recorded.length) writePaths(file, remaining, deps);
|
|
62144
|
-
const keptKeys = new Set(remaining.map(
|
|
62506
|
+
const keptKeys = new Set(remaining.map(norm2));
|
|
62145
62507
|
const root = registered[0] ? canonicalWorktreeRoot(registered[0]) : void 0;
|
|
62146
|
-
if (root && isDir(root) && !live.has(
|
|
62508
|
+
if (root && isDir(root) && !live.has(norm2(root))) {
|
|
62147
62509
|
report.root = root;
|
|
62148
62510
|
let children = [];
|
|
62149
62511
|
try {
|
|
@@ -62151,8 +62513,8 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
|
|
|
62151
62513
|
} catch {
|
|
62152
62514
|
}
|
|
62153
62515
|
for (const name of children) {
|
|
62154
|
-
const child2 = (0,
|
|
62155
|
-
const key =
|
|
62516
|
+
const child2 = (0, import_node_path49.join)(root, name);
|
|
62517
|
+
const key = norm2(child2);
|
|
62156
62518
|
if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
|
|
62157
62519
|
let entries;
|
|
62158
62520
|
try {
|
|
@@ -62194,10 +62556,10 @@ function argvWantsJson2() {
|
|
|
62194
62556
|
return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
|
|
62195
62557
|
}
|
|
62196
62558
|
function hubRoot() {
|
|
62197
|
-
const fromPkg = (0,
|
|
62559
|
+
const fromPkg = (0, import_node_path50.join)(__dirname, "..", "..");
|
|
62198
62560
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
62199
|
-
if ((0,
|
|
62200
|
-
if ((0,
|
|
62561
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(fromPkg, marker))) return fromPkg;
|
|
62562
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(process.cwd(), marker))) return process.cwd();
|
|
62201
62563
|
return null;
|
|
62202
62564
|
}
|
|
62203
62565
|
function ciAuditDeps() {
|
|
@@ -62209,8 +62571,8 @@ function ciAuditDeps() {
|
|
|
62209
62571
|
getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
|
|
62210
62572
|
readSeedFile: (path2) => {
|
|
62211
62573
|
if (!root) return null;
|
|
62212
|
-
const fullPath = (0,
|
|
62213
|
-
return (0,
|
|
62574
|
+
const fullPath = (0, import_node_path50.join)(root, path2);
|
|
62575
|
+
return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
|
|
62214
62576
|
}
|
|
62215
62577
|
};
|
|
62216
62578
|
}
|
|
@@ -63077,11 +63439,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63077
63439
|
}
|
|
63078
63440
|
});
|
|
63079
63441
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
63080
|
-
const wfDir = (0,
|
|
63081
|
-
if (!(0,
|
|
63082
|
-
return (0,
|
|
63442
|
+
const wfDir = (0, import_node_path50.join)(cwd, ".github", "workflows");
|
|
63443
|
+
if (!(0, import_node_fs54.existsSync)(wfDir)) return [];
|
|
63444
|
+
return (0, import_node_fs54.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
63083
63445
|
try {
|
|
63084
|
-
return workflowReportsPrChecks((0,
|
|
63446
|
+
return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0, import_node_path50.join)(wfDir, name), "utf8"));
|
|
63085
63447
|
} catch {
|
|
63086
63448
|
return true;
|
|
63087
63449
|
}
|
|
@@ -63269,7 +63631,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63269
63631
|
// #5400: after grace, name "GitHub delivered zero runs" instead of burning the full budget as pending.
|
|
63270
63632
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr checks-wait", number, repo),
|
|
63271
63633
|
baseBranch,
|
|
63272
|
-
sleep: (ms) => new Promise((
|
|
63634
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63273
63635
|
log: (message2) => console.warn(message2),
|
|
63274
63636
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
63275
63637
|
// Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
|
|
@@ -63286,7 +63648,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63286
63648
|
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
63649
|
if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
|
|
63288
63650
|
const verdict = o.verdict;
|
|
63289
|
-
const findings = o.findingsFile ? (0,
|
|
63651
|
+
const findings = o.findingsFile ? (0, import_node_fs54.readFileSync)(o.findingsFile, "utf8") : void 0;
|
|
63290
63652
|
let patch;
|
|
63291
63653
|
let head;
|
|
63292
63654
|
try {
|
|
@@ -63449,7 +63811,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63449
63811
|
// #5400: same zero-runs delivery probe as checks-wait — do not burn the land budget on silence.
|
|
63450
63812
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr land", prNumber, repo),
|
|
63451
63813
|
baseBranch: "development",
|
|
63452
|
-
sleep: (ms) => new Promise((
|
|
63814
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63453
63815
|
log: (message2) => console.warn(message2),
|
|
63454
63816
|
// `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
|
|
63455
63817
|
// 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
|
|
@@ -63513,7 +63875,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63513
63875
|
} else {
|
|
63514
63876
|
lastFailure2 = void 0;
|
|
63515
63877
|
}
|
|
63516
|
-
await new Promise((
|
|
63878
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_LAND_POLL_MS));
|
|
63517
63879
|
}
|
|
63518
63880
|
if (lastFailure2) {
|
|
63519
63881
|
throw new Error(
|
|
@@ -63564,7 +63926,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63564
63926
|
const cleanupMerged = async (r) => {
|
|
63565
63927
|
if (opts.preserveWorktree) return "cleanup skipped (--preserve-worktree)";
|
|
63566
63928
|
const ran = await execFileP(process.execPath, [process.argv[1], ...canonicalArgvFor(["pr", "merge", r.pr, "--repo", r.repo])], {
|
|
63567
|
-
cwd: (0,
|
|
63929
|
+
cwd: (0, import_node_fs54.existsSync)(cwd0) ? cwd0 : primaryRoot0,
|
|
63568
63930
|
timeout: GH_MUTATION_TIMEOUT_MS * 3
|
|
63569
63931
|
}).then((x) => ({ stdout: x.stdout, stderr: x.stderr }), (e) => ({ stdout: e.stdout ?? "", stderr: e.stderr ?? "" }));
|
|
63570
63932
|
const json = ran.stdout.split("\n").reverse().find((l) => l.startsWith("{"));
|
|
@@ -63635,7 +63997,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63635
63997
|
const mergeSquashBody = squashBodyTextForMerge(
|
|
63636
63998
|
closingGuardInput,
|
|
63637
63999
|
method === "--squash",
|
|
63638
|
-
o.squashBodyFile ? (0,
|
|
64000
|
+
o.squashBodyFile ? (0, import_node_fs54.readFileSync)(o.squashBodyFile, "utf8") : void 0
|
|
63639
64001
|
);
|
|
63640
64002
|
if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
|
|
63641
64003
|
const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
|
|
@@ -63667,7 +64029,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63667
64029
|
const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
|
|
63668
64030
|
let foreignCheckout;
|
|
63669
64031
|
if (foreignCwd) {
|
|
63670
|
-
const sibling = (0,
|
|
64032
|
+
const sibling = (0, import_node_path50.join)((0, import_node_path50.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
|
|
63671
64033
|
const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
|
|
63672
64034
|
if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
|
|
63673
64035
|
const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
|
|
@@ -63705,7 +64067,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63705
64067
|
checksDiagnosticCommand: `gh pr checks ${number} --repo ${repo}`,
|
|
63706
64068
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr merge --wait", number, repo),
|
|
63707
64069
|
baseBranch,
|
|
63708
|
-
sleep: (ms) => new Promise((
|
|
64070
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
63709
64071
|
log: (message2) => console.warn(message2),
|
|
63710
64072
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
63711
64073
|
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 +64148,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63786
64148
|
}
|
|
63787
64149
|
if (!repoForPostCleanup) throw e;
|
|
63788
64150
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
63789
|
-
const commitMessage = bodyFile ? (0,
|
|
64151
|
+
const commitMessage = bodyFile ? (0, import_node_fs54.readFileSync)(bodyFile, "utf8") : void 0;
|
|
63790
64152
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
63791
64153
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
63792
64154
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -63795,7 +64157,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63795
64157
|
}
|
|
63796
64158
|
if (!o.auto && mergeRejectedBaseBranchModified(message2)) {
|
|
63797
64159
|
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((
|
|
64160
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
|
|
63799
64161
|
await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
63800
64162
|
const m2 = String(e2.message || "");
|
|
63801
64163
|
if (/already been merged/i.test(m2)) {
|
|
@@ -63901,7 +64263,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63901
64263
|
let residueSweep;
|
|
63902
64264
|
if (!(foreignCwd && !foreignCheckout)) {
|
|
63903
64265
|
try {
|
|
63904
|
-
gitCommonDir = (0,
|
|
64266
|
+
gitCommonDir = (0, import_node_path50.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
|
|
63905
64267
|
residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
|
|
63906
64268
|
} catch {
|
|
63907
64269
|
}
|
|
@@ -63919,7 +64281,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
63919
64281
|
preserveWorktree: o.preserveWorktree,
|
|
63920
64282
|
gcAcknowledged: o.gc,
|
|
63921
64283
|
expectedHeadOid: headRefOid,
|
|
63922
|
-
pathExists: (p) => (0,
|
|
64284
|
+
pathExists: (p) => (0, import_node_fs54.existsSync)(p),
|
|
63923
64285
|
// #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
|
|
63924
64286
|
// standing in is removed mid-cleanup, so a cwd-relative invocation fails with
|
|
63925
64287
|
// 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
|
|
@@ -64060,7 +64422,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
|
|
|
64060
64422
|
console.warn(`ci checks-wait: zero-runs probe FAILED (${read.error}) \u2014 not treating as delivery failure this cycle`);
|
|
64061
64423
|
return null;
|
|
64062
64424
|
},
|
|
64063
|
-
sleep: (ms) => new Promise((
|
|
64425
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
64064
64426
|
log: (message2) => console.warn(message2),
|
|
64065
64427
|
timeoutMs: timeoutMs ?? PR_CHECKS_TIMEOUT_MS,
|
|
64066
64428
|
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 +64559,14 @@ function trainApplyDeps() {
|
|
|
64197
64559
|
},
|
|
64198
64560
|
// Hub-App-authority dispatch of the central tenant deploy (#953) — the Hub fires the
|
|
64199
64561
|
// workflow_dispatch with its App token, so the caller needs no MMI-Hub Actions write.
|
|
64200
|
-
|
|
64201
|
-
|
|
64562
|
+
readDeployedSha: async (slug, stage) => (await fetchDeployFactsBySlug(slug, registryClientDeps(await loadConfig())))?.stages[stage]?.deployedTag,
|
|
64563
|
+
compareDeployRef: async (repo, base, head) => (await defaultGitHubClient().rest(
|
|
64564
|
+
"GET",
|
|
64565
|
+
`repos/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}?per_page=1`,
|
|
64566
|
+
{ timeoutMs: GH_TRAIN_TIMEOUT_MS }
|
|
64567
|
+
)).status,
|
|
64568
|
+
dispatchTenantDeploy: async ({ repo, stage, ref, rollback }) => {
|
|
64569
|
+
const res = await tenantDeploy({ repo, stage, ref, ...rollback ? { rollback } : {} }, registryClientDeps(await loadConfig()));
|
|
64202
64570
|
if (!res.ok) {
|
|
64203
64571
|
const detail = res.body?.error ?? res.error ?? `HTTP ${res.status}`;
|
|
64204
64572
|
throw new Error(`tenant deploy dispatch failed: ${detail}`);
|
|
@@ -64450,7 +64818,7 @@ function hotfixFollowUpLegs(runs2, foldPort, alignment, foldNote, deployNote) {
|
|
|
64450
64818
|
}
|
|
64451
64819
|
return legs;
|
|
64452
64820
|
}
|
|
64453
|
-
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((
|
|
64821
|
+
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))) {
|
|
64454
64822
|
for (let attempt = 0; ; attempt++) {
|
|
64455
64823
|
try {
|
|
64456
64824
|
return await cb(repo, true);
|
|
@@ -65185,7 +65553,7 @@ function rcRunCauseFromLog(log) {
|
|
|
65185
65553
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
65186
65554
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
65187
65555
|
}
|
|
65188
|
-
var suiteStateDir = (root) => (0,
|
|
65556
|
+
var suiteStateDir = (root) => (0, import_node_path52.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
65189
65557
|
async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
|
|
65190
65558
|
const deps = trainApplyDeps();
|
|
65191
65559
|
deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
|
|
@@ -65204,8 +65572,8 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
65204
65572
|
const meta = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
65205
65573
|
if (!meta.ok || meta.project?.releaseTrack !== "full" || meta.project.suite !== "mm-work")
|
|
65206
65574
|
throw new Error(`${repo}: first rc branch requires releaseTrack full and suite=mm-work in META; no branch created`);
|
|
65207
|
-
const
|
|
65208
|
-
if (
|
|
65575
|
+
const main2 = (await deps.run("git", ["rev-parse", "origin/main"])).trim();
|
|
65576
|
+
if (main2.toLowerCase() !== previousRc.toLowerCase()) throw new Error(`${repo}: main moved since the cut snapshot; no rc branch created`);
|
|
65209
65577
|
await deps.run("git", ["push", "origin", `${previousRc}:refs/heads/rc`]);
|
|
65210
65578
|
await deps.run("git", ["fetch", "origin", "rc"]);
|
|
65211
65579
|
rcNow = (await deps.run("git", ["rev-parse", "origin/rc"])).trim();
|
|
@@ -65232,16 +65600,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
|
65232
65600
|
}
|
|
65233
65601
|
}
|
|
65234
65602
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
65235
|
-
const recordPath = (0,
|
|
65236
|
-
const base = (0,
|
|
65603
|
+
const recordPath = (0, import_node_path52.join)(suiteStateDir(root), "cut.json");
|
|
65604
|
+
const base = (0, import_node_path52.join)(suiteStateDir(root), "checkouts");
|
|
65237
65605
|
const checkout = async (repo) => {
|
|
65238
65606
|
if (repo === SUITE_REPO) return root;
|
|
65239
|
-
const target = (0,
|
|
65240
|
-
if (!(0,
|
|
65241
|
-
(0,
|
|
65607
|
+
const target = (0, import_node_path52.join)(base, repo.split("/")[1]);
|
|
65608
|
+
if (!(0, import_node_fs55.existsSync)(target)) {
|
|
65609
|
+
(0, import_node_fs55.mkdirSync)(base, { recursive: true });
|
|
65242
65610
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
65243
65611
|
}
|
|
65244
|
-
if (!(0,
|
|
65612
|
+
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
65613
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
65246
65614
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
65247
65615
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -65305,7 +65673,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65305
65673
|
if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
|
|
65306
65674
|
const target = await checkout(repo);
|
|
65307
65675
|
return new Promise((resolveResult, reject2) => {
|
|
65308
|
-
const child2 = (0, import_node_child_process19.fork)((0,
|
|
65676
|
+
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
65677
|
cwd: target,
|
|
65310
65678
|
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
65311
65679
|
execArgv: []
|
|
@@ -65377,7 +65745,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65377
65745
|
if (!reuse) await gh(["workflow", "run", "module-rc-publish.yml", "--repo", "mutmutco/MMI-Hub", ...fields.flatMap((f) => ["--field", f])]);
|
|
65378
65746
|
let candidate = reuse;
|
|
65379
65747
|
for (let i = 0; !candidate && i < 25; i++) {
|
|
65380
|
-
await new Promise((
|
|
65748
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
65381
65749
|
candidate = (await rcRuns()).find((r) => r.displayTitle === title && Date.parse(r.createdAt) >= since);
|
|
65382
65750
|
}
|
|
65383
65751
|
if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
|
|
@@ -65533,7 +65901,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65533
65901
|
for (let i = 0; ; i++) {
|
|
65534
65902
|
const pin = await openSuitePinPr({ root, run: (file, args, cwd) => exec(file, args, cwd, GIT_TIMEOUT_MS) }, { repo, tag, stage: "main" });
|
|
65535
65903
|
if (pin.status !== "pending" || i >= 40) return pin;
|
|
65536
|
-
await new Promise((
|
|
65904
|
+
await new Promise((resolve8) => setTimeout(resolve8, 3e4));
|
|
65537
65905
|
}
|
|
65538
65906
|
},
|
|
65539
65907
|
suiteMapping: async (leadTag) => {
|
|
@@ -65548,9 +65916,9 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
65548
65916
|
const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
|
|
65549
65917
|
if (parseSuiteMapping(body)) return;
|
|
65550
65918
|
const dir = suiteStateDir(root);
|
|
65551
|
-
(0,
|
|
65552
|
-
const file = (0,
|
|
65553
|
-
(0,
|
|
65919
|
+
(0, import_node_fs55.mkdirSync)(dir, { recursive: true });
|
|
65920
|
+
const file = (0, import_node_path52.join)(dir, `notes-${leadTag}.md`);
|
|
65921
|
+
(0, import_node_fs55.writeFileSync)(file, `${body}
|
|
65554
65922
|
|
|
65555
65923
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
65556
65924
|
await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
|
|
@@ -65591,7 +65959,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
|
|
|
65591
65959
|
|
|
65592
65960
|
// src/suite-commands.ts
|
|
65593
65961
|
init_cli_shared();
|
|
65594
|
-
var
|
|
65962
|
+
var import_node_fs56 = require("node:fs");
|
|
65595
65963
|
function parseMemberSummaryFiles(entries) {
|
|
65596
65964
|
const memberSummaryFiles = {};
|
|
65597
65965
|
for (const entry of entries) {
|
|
@@ -65615,7 +65983,7 @@ function suiteRcDataProdDeps() {
|
|
|
65615
65983
|
const rc = facts.stages.rc;
|
|
65616
65984
|
return rc?.present ? rc.substrate ?? "an unnamed substrate" : null;
|
|
65617
65985
|
},
|
|
65618
|
-
sleep: (ms) => new Promise((
|
|
65986
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))
|
|
65619
65987
|
};
|
|
65620
65988
|
}
|
|
65621
65989
|
function registerSuiteCommands(program3) {
|
|
@@ -65685,7 +66053,7 @@ function registerSuiteCommands(program3) {
|
|
|
65685
66053
|
try {
|
|
65686
66054
|
const io = suiteTrainIo(root);
|
|
65687
66055
|
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,
|
|
66056
|
+
if (retired && !(0, import_node_fs56.existsSync)(io.recordPath)) {
|
|
65689
66057
|
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
66058
|
return;
|
|
65691
66059
|
}
|
|
@@ -65925,9 +66293,9 @@ function registerJevCommands(program3) {
|
|
|
65925
66293
|
|
|
65926
66294
|
// src/packages-adopt-export.ts
|
|
65927
66295
|
var import_node_child_process20 = require("node:child_process");
|
|
65928
|
-
var
|
|
66296
|
+
var import_node_fs57 = require("node:fs");
|
|
65929
66297
|
var import_node_os25 = require("node:os");
|
|
65930
|
-
var
|
|
66298
|
+
var import_node_path53 = require("node:path");
|
|
65931
66299
|
var import_node_util6 = require("node:util");
|
|
65932
66300
|
init_cli_shared();
|
|
65933
66301
|
|
|
@@ -66547,38 +66915,38 @@ function expectedFromRun(run, opts) {
|
|
|
66547
66915
|
}
|
|
66548
66916
|
async function adoptExport(opts, deps) {
|
|
66549
66917
|
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,
|
|
66918
|
+
const packagePath = (0, import_node_path53.join)(opts.dir, "package.json");
|
|
66919
|
+
const lockPath = (0, import_node_path53.join)(opts.dir, "package-lock.json");
|
|
66920
|
+
const packageText = (0, import_node_fs57.readFileSync)(packagePath, "utf8");
|
|
66921
|
+
const lockText = (0, import_node_fs57.readFileSync)(lockPath, "utf8");
|
|
66922
|
+
const work = (0, import_node_fs57.mkdtempSync)((0, import_node_path53.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
|
|
66555
66923
|
try {
|
|
66556
66924
|
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,
|
|
66925
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
66926
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
66927
|
+
(0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
66928
|
+
const script = (0, import_node_path53.join)(work, "package_export.py");
|
|
66929
|
+
(0, import_node_fs57.writeFileSync)(script, package_export_default);
|
|
66562
66930
|
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,
|
|
66931
|
+
const verified = (0, import_node_path53.join)(work, "verified");
|
|
66932
|
+
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
66933
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
66566
66934
|
for (const { name } of plan.result.adopted) {
|
|
66567
66935
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
66568
|
-
await deps.npm(["cache", "add", (0,
|
|
66936
|
+
await deps.npm(["cache", "add", (0, import_node_path53.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
66569
66937
|
}
|
|
66570
|
-
(0,
|
|
66571
|
-
(0,
|
|
66938
|
+
(0, import_node_fs57.writeFileSync)(packagePath, plan.packageText);
|
|
66939
|
+
(0, import_node_fs57.writeFileSync)(lockPath, plan.lockText);
|
|
66572
66940
|
try {
|
|
66573
66941
|
await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
|
|
66574
66942
|
} catch (error) {
|
|
66575
|
-
(0,
|
|
66576
|
-
(0,
|
|
66943
|
+
(0, import_node_fs57.writeFileSync)(packagePath, packageText);
|
|
66944
|
+
(0, import_node_fs57.writeFileSync)(lockPath, lockText);
|
|
66577
66945
|
throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
|
|
66578
66946
|
}
|
|
66579
66947
|
return plan.result;
|
|
66580
66948
|
} finally {
|
|
66581
|
-
(0,
|
|
66949
|
+
(0, import_node_fs57.rmSync)(work, { recursive: true, force: true });
|
|
66582
66950
|
}
|
|
66583
66951
|
}
|
|
66584
66952
|
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process20.execFile);
|
|
@@ -66682,12 +67050,12 @@ function registerPackagesCommands(program3) {
|
|
|
66682
67050
|
init_cli_shared();
|
|
66683
67051
|
|
|
66684
67052
|
// src/wave-status.ts
|
|
66685
|
-
var
|
|
67053
|
+
var import_node_fs58 = require("node:fs");
|
|
66686
67054
|
function observeStage(worktreePath) {
|
|
66687
67055
|
const statePath = stageStatePath(worktreePath);
|
|
66688
67056
|
let raw;
|
|
66689
67057
|
try {
|
|
66690
|
-
raw = (0,
|
|
67058
|
+
raw = (0, import_node_fs58.readFileSync)(statePath, "utf8");
|
|
66691
67059
|
} catch (error) {
|
|
66692
67060
|
if (error.code === "ENOENT") return { state: "ok", value: { running: false } };
|
|
66693
67061
|
return { state: "unavailable", reason: "stage state could not be read" };
|
|
@@ -66754,9 +67122,9 @@ async function collectWaveStatus(deps) {
|
|
|
66754
67122
|
// src/discovery-commands.ts
|
|
66755
67123
|
init_github_client();
|
|
66756
67124
|
init_cli_shared();
|
|
66757
|
-
var
|
|
67125
|
+
var import_node_fs59 = require("node:fs");
|
|
66758
67126
|
var import_node_os26 = require("node:os");
|
|
66759
|
-
var
|
|
67127
|
+
var import_node_path54 = require("node:path");
|
|
66760
67128
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
66761
67129
|
var defaultStatusDeps = {
|
|
66762
67130
|
repo: resolveRepo,
|
|
@@ -66977,8 +67345,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
66977
67345
|
}
|
|
66978
67346
|
const home = (0, import_node_os26.homedir)();
|
|
66979
67347
|
const plugin = onboardPluginGate({
|
|
66980
|
-
readKnown: () => readFileSyncSafe((0,
|
|
66981
|
-
readSettings: () => readFileSyncSafe((0,
|
|
67348
|
+
readKnown: () => readFileSyncSafe((0, import_node_path54.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs59.readFileSync),
|
|
67349
|
+
readSettings: () => readFileSyncSafe((0, import_node_path54.join)(home, ".claude", "settings.json"), import_node_fs59.readFileSync)
|
|
66982
67350
|
});
|
|
66983
67351
|
return {
|
|
66984
67352
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -68679,9 +69047,9 @@ function formatExplainGroup(cmd, rootName) {
|
|
|
68679
69047
|
lines2.push(...formatOwnDetail(cmd));
|
|
68680
69048
|
lines2.push("Commands:");
|
|
68681
69049
|
for (const child2 of cmd.subcommands) {
|
|
68682
|
-
const args = child2.arguments.map((
|
|
68683
|
-
const name =
|
|
68684
|
-
return
|
|
69050
|
+
const args = child2.arguments.map((arg2) => {
|
|
69051
|
+
const name = arg2.variadic ? `${arg2.name}...` : arg2.name;
|
|
69052
|
+
return arg2.required ? `<${name}>` : `[${name}]`;
|
|
68685
69053
|
}).join(" ");
|
|
68686
69054
|
lines2.push(` ${canonical(child2)}${args ? ` ${args}` : ""}${child2.description ? ` \u2014 ${child2.description}` : ""}`);
|
|
68687
69055
|
}
|
|
@@ -68702,11 +69070,11 @@ function briefCommand(cmd) {
|
|
|
68702
69070
|
return {
|
|
68703
69071
|
path: cmd.path,
|
|
68704
69072
|
...cmd.description ? { description: cmd.description } : {},
|
|
68705
|
-
arguments: cmd.arguments.map((
|
|
68706
|
-
name:
|
|
68707
|
-
required:
|
|
68708
|
-
...
|
|
68709
|
-
...
|
|
69073
|
+
arguments: cmd.arguments.map((arg2) => ({
|
|
69074
|
+
name: arg2.name,
|
|
69075
|
+
required: arg2.required,
|
|
69076
|
+
...arg2.variadic ? { variadic: true } : {},
|
|
69077
|
+
...arg2.description ? { description: arg2.description } : {}
|
|
68710
69078
|
})),
|
|
68711
69079
|
options: ownOptions(cmd).map((opt) => ({
|
|
68712
69080
|
flags: opt.flags,
|
|
@@ -68729,15 +69097,15 @@ function explainBriefManifest(manifest, command) {
|
|
|
68729
69097
|
}
|
|
68730
69098
|
function formatExplainBrief(cmd, rootName) {
|
|
68731
69099
|
const lines2 = [`${rootName} ${cmd.path}${cmd.description ? ` \u2014 ${cmd.description}` : ""}`];
|
|
68732
|
-
for (const
|
|
68733
|
-
const flag =
|
|
68734
|
-
lines2.push(` ${flag} ${
|
|
69100
|
+
for (const arg2 of cmd.arguments) {
|
|
69101
|
+
const flag = arg2.required ? `<${arg2.name}>` : `[${arg2.name}]`;
|
|
69102
|
+
lines2.push(` ${flag} ${arg2.required ? "required" : "optional"}${arg2.description ? ` ${arg2.description}` : ""}`);
|
|
68735
69103
|
}
|
|
68736
69104
|
for (const opt of ownOptions(cmd)) {
|
|
68737
69105
|
lines2.push(` ${opt.flags} ${opt.takesValue ? "takes-value" : "flag"}${opt.mandatory ? " required" : ""}${opt.description ? ` ${opt.description}` : ""}`);
|
|
68738
69106
|
}
|
|
68739
69107
|
for (const child2 of cmd.subcommands) {
|
|
68740
|
-
const args = child2.arguments.map((
|
|
69108
|
+
const args = child2.arguments.map((arg2) => arg2.required ? `<${arg2.name}>` : `[${arg2.name}]`).join(" ");
|
|
68741
69109
|
lines2.push(`${child2.path}${args ? ` ${args}` : ""}${child2.description ? ` \u2014 ${child2.description}` : ""}`);
|
|
68742
69110
|
for (const opt of ownOptions(child2)) {
|
|
68743
69111
|
lines2.push(` ${opt.flags} ${opt.takesValue ? "takes-value" : "flag"}${opt.mandatory ? " required" : ""}${opt.description ? ` ${opt.description}` : ""}`);
|
|
@@ -69644,8 +70012,8 @@ async function repoWorkflowEntries(client, repo) {
|
|
|
69644
70012
|
for (const wf of workflowsList) {
|
|
69645
70013
|
if (typeof wf?.path !== "string" || !wf.path) continue;
|
|
69646
70014
|
if (!wf.path.startsWith(".github/workflows/")) continue;
|
|
69647
|
-
const
|
|
69648
|
-
const name = `${repo}/${
|
|
70015
|
+
const basename6 = wf.path.split("/").pop() ?? wf.path;
|
|
70016
|
+
const name = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
|
|
69649
70017
|
if (wf.state !== "active") {
|
|
69650
70018
|
disabled.push(name);
|
|
69651
70019
|
continue;
|
|
@@ -70109,8 +70477,8 @@ function registerSessionReport(program3) {
|
|
|
70109
70477
|
}
|
|
70110
70478
|
|
|
70111
70479
|
// src/stage-commands.ts
|
|
70112
|
-
var
|
|
70113
|
-
var
|
|
70480
|
+
var import_node_fs60 = require("node:fs");
|
|
70481
|
+
var import_node_path55 = require("node:path");
|
|
70114
70482
|
init_cli_shared();
|
|
70115
70483
|
init_clean_exit();
|
|
70116
70484
|
|
|
@@ -70358,8 +70726,8 @@ function registerStageCommands(program3) {
|
|
|
70358
70726
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
70359
70727
|
const resolution = decideStage({
|
|
70360
70728
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
70361
|
-
hasCompose: (0,
|
|
70362
|
-
hasEnvExample: (0,
|
|
70729
|
+
hasCompose: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), "docker-compose.yml")),
|
|
70730
|
+
hasEnvExample: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), ".env.example")),
|
|
70363
70731
|
repo
|
|
70364
70732
|
});
|
|
70365
70733
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -70652,19 +71020,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
70652
71020
|
|
|
70653
71021
|
// src/tenant-artifact.ts
|
|
70654
71022
|
var import_node_crypto23 = require("node:crypto");
|
|
70655
|
-
var
|
|
71023
|
+
var import_node_fs61 = require("node:fs");
|
|
70656
71024
|
var import_promises11 = require("node:fs/promises");
|
|
70657
|
-
var
|
|
71025
|
+
var import_node_path56 = require("node:path");
|
|
70658
71026
|
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
71027
|
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
70660
71028
|
async function sha256File(path2) {
|
|
70661
71029
|
const hash = (0, import_node_crypto23.createHash)("sha256");
|
|
70662
|
-
for await (const chunk of (0,
|
|
71030
|
+
for await (const chunk of (0, import_node_fs61.createReadStream)(path2)) hash.update(chunk);
|
|
70663
71031
|
return hash.digest("hex");
|
|
70664
71032
|
}
|
|
70665
71033
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
70666
71034
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
70667
|
-
const path2 = (0,
|
|
71035
|
+
const path2 = (0, import_node_path56.resolve)(inputPath);
|
|
70668
71036
|
const info = await (0, import_promises11.stat)(path2);
|
|
70669
71037
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
70670
71038
|
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 +71050,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
70682
71050
|
return [key, value];
|
|
70683
71051
|
}));
|
|
70684
71052
|
headers["content-length"] = String(info.size);
|
|
70685
|
-
const stream = (0,
|
|
71053
|
+
const stream = (0, import_node_fs61.createReadStream)(path2);
|
|
70686
71054
|
let uploaded;
|
|
70687
71055
|
try {
|
|
70688
71056
|
uploaded = await fetch(body.uploadUrl, {
|
|
@@ -71036,7 +71404,7 @@ function renderVerifySecrets(body) {
|
|
|
71036
71404
|
}
|
|
71037
71405
|
|
|
71038
71406
|
// src/command-register-developer.ts
|
|
71039
|
-
var
|
|
71407
|
+
var import_node_fs66 = require("node:fs");
|
|
71040
71408
|
init_clean_exit();
|
|
71041
71409
|
init_cli_shared();
|
|
71042
71410
|
init_error_codes();
|
|
@@ -71069,12 +71437,13 @@ async function resolveWhoami(deps) {
|
|
|
71069
71437
|
}
|
|
71070
71438
|
|
|
71071
71439
|
// src/command-register-developer.ts
|
|
71072
|
-
var
|
|
71440
|
+
var import_node_path63 = require("node:path");
|
|
71073
71441
|
|
|
71074
71442
|
// src/doc-refs-command-source.ts
|
|
71075
71443
|
var import_node_child_process21 = require("node:child_process");
|
|
71076
|
-
var
|
|
71077
|
-
var
|
|
71444
|
+
var import_node_fs62 = require("node:fs");
|
|
71445
|
+
var import_node_path57 = require("node:path");
|
|
71446
|
+
var IN_PROCESS_SOURCE = "in-process";
|
|
71078
71447
|
var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
|
|
71079
71448
|
var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
|
|
71080
71449
|
var flagsOf = (options) => new Set((options ?? []).flatMap((option) => String(option?.flags ?? "").match(/--[\w-]+/g) ?? []));
|
|
@@ -71095,7 +71464,7 @@ function inventoryFromManifest(manifest, source) {
|
|
|
71095
71464
|
function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
|
|
71096
71465
|
try {
|
|
71097
71466
|
const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
|
|
71098
|
-
cwd: (0,
|
|
71467
|
+
cwd: (0, import_node_path57.dirname)(bundle),
|
|
71099
71468
|
encoding: "utf8",
|
|
71100
71469
|
windowsHide: true,
|
|
71101
71470
|
timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
|
|
@@ -71110,20 +71479,29 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
|
|
|
71110
71479
|
}
|
|
71111
71480
|
function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
|
|
71112
71481
|
if (!argv1) return false;
|
|
71113
|
-
const
|
|
71114
|
-
const absolute = (0,
|
|
71482
|
+
const norm3 = (path2) => {
|
|
71483
|
+
const absolute = (0, import_node_path57.resolve)(path2);
|
|
71115
71484
|
return platform2 === "win32" ? absolute.toLowerCase() : absolute;
|
|
71116
71485
|
};
|
|
71117
|
-
const running =
|
|
71118
|
-
if (!["main.cjs", "index.cjs"].includes((0,
|
|
71119
|
-
return (0,
|
|
71486
|
+
const running = norm3(argv1);
|
|
71487
|
+
if (!["main.cjs", "index.cjs"].includes((0, import_node_path57.basename)(running))) return false;
|
|
71488
|
+
return (0, import_node_path57.dirname)(running) === (0, import_node_path57.dirname)(norm3(bundle));
|
|
71489
|
+
}
|
|
71490
|
+
function nameUnknownCommandSources(findings, source, root) {
|
|
71491
|
+
const checkoutBundle = (0, import_node_path57.resolve)(root, "cli", "dist", "main.cjs");
|
|
71492
|
+
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>`" : "";
|
|
71493
|
+
for (const finding of findings) {
|
|
71494
|
+
if (finding.kind !== "unknown-command") continue;
|
|
71495
|
+
finding.commandSource = source;
|
|
71496
|
+
finding.detail += ` (source: ${source}${recovery})`;
|
|
71497
|
+
}
|
|
71120
71498
|
}
|
|
71121
71499
|
function resolveCommandInventory(deps) {
|
|
71122
|
-
const exists = deps.exists ??
|
|
71500
|
+
const exists = deps.exists ?? import_node_fs62.existsSync;
|
|
71123
71501
|
const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
|
|
71124
71502
|
const warnings = [];
|
|
71125
71503
|
if (deps.from) {
|
|
71126
|
-
const bundle = (0,
|
|
71504
|
+
const bundle = (0, import_node_path57.isAbsolute)(deps.from) ? deps.from : (0, import_node_path57.resolve)(deps.root, deps.from);
|
|
71127
71505
|
if (!exists(bundle)) {
|
|
71128
71506
|
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
71507
|
}
|
|
@@ -71133,10 +71511,13 @@ function resolveCommandInventory(deps) {
|
|
|
71133
71511
|
}
|
|
71134
71512
|
return { inventory: inventory2, warnings };
|
|
71135
71513
|
}
|
|
71136
|
-
const treeBundle = (0,
|
|
71137
|
-
if (!exists(treeBundle)
|
|
71514
|
+
const treeBundle = (0, import_node_path57.join)(deps.root, "cli", "dist", "main.cjs");
|
|
71515
|
+
if (!exists(treeBundle)) {
|
|
71138
71516
|
return { inventory: deps.inProcess(), warnings };
|
|
71139
71517
|
}
|
|
71518
|
+
if (isRunningBundle(treeBundle, deps.argv1, deps.platform)) {
|
|
71519
|
+
return { inventory: { ...deps.inProcess(), source: treeBundle }, warnings };
|
|
71520
|
+
}
|
|
71140
71521
|
const inventory = inventoryFromManifest(read(treeBundle), treeBundle);
|
|
71141
71522
|
if (!inventory) {
|
|
71142
71523
|
warnings.push({
|
|
@@ -71251,19 +71632,19 @@ async function executeWaveLand(plan, deps) {
|
|
|
71251
71632
|
// src/dist-drift.ts
|
|
71252
71633
|
var import_node_child_process22 = require("node:child_process");
|
|
71253
71634
|
var import_node_crypto25 = require("node:crypto");
|
|
71254
|
-
var
|
|
71635
|
+
var import_node_fs64 = require("node:fs");
|
|
71255
71636
|
var import_node_os27 = require("node:os");
|
|
71256
|
-
var
|
|
71637
|
+
var import_node_path59 = require("node:path");
|
|
71257
71638
|
|
|
71258
71639
|
// ../scripts/distribution-digest.mjs
|
|
71259
71640
|
var import_node_crypto24 = require("node:crypto");
|
|
71260
|
-
var
|
|
71261
|
-
var
|
|
71641
|
+
var import_node_fs63 = require("node:fs");
|
|
71642
|
+
var import_node_path58 = require("node:path");
|
|
71262
71643
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
71263
71644
|
function repoPath(root, declaredPath, label) {
|
|
71264
|
-
const absoluteRoot = (0,
|
|
71265
|
-
const target = (0,
|
|
71266
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
71645
|
+
const absoluteRoot = (0, import_node_path58.resolve)(root);
|
|
71646
|
+
const target = (0, import_node_path58.resolve)(root, declaredPath);
|
|
71647
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path58.sep}`)) {
|
|
71267
71648
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
71268
71649
|
}
|
|
71269
71650
|
return target;
|
|
@@ -71271,7 +71652,7 @@ function repoPath(root, declaredPath, label) {
|
|
|
71271
71652
|
function digestFiles(files) {
|
|
71272
71653
|
const hash = (0, import_node_crypto24.createHash)("sha256");
|
|
71273
71654
|
for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
|
|
71274
|
-
const content = file.stat.isSymbolicLink() ? Buffer.from((0,
|
|
71655
|
+
const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs63.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs63.readFileSync)(file.absolute);
|
|
71275
71656
|
hash.update(file.relative, "utf8");
|
|
71276
71657
|
hash.update("\0");
|
|
71277
71658
|
hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
|
|
@@ -71286,8 +71667,8 @@ function digestFiles(files) {
|
|
|
71286
71667
|
function digestPackedFiles(packageRoot, packedFiles) {
|
|
71287
71668
|
return digestFiles(packedFiles.map((path2) => {
|
|
71288
71669
|
const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
|
|
71289
|
-
if (!(0,
|
|
71290
|
-
return { absolute, relative: slash(path2), stat: (0,
|
|
71670
|
+
if (!(0, import_node_fs63.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
|
|
71671
|
+
return { absolute, relative: slash(path2), stat: (0, import_node_fs63.lstatSync)(absolute) };
|
|
71291
71672
|
}));
|
|
71292
71673
|
}
|
|
71293
71674
|
|
|
@@ -71372,13 +71753,13 @@ function renderDistDriftReceipt(receipt) {
|
|
|
71372
71753
|
return lines2;
|
|
71373
71754
|
}
|
|
71374
71755
|
function readOrNull(path2) {
|
|
71375
|
-
return (0,
|
|
71756
|
+
return (0, import_node_fs64.existsSync)(path2) ? (0, import_node_fs64.readFileSync)(path2) : null;
|
|
71376
71757
|
}
|
|
71377
71758
|
function walkFiles(root) {
|
|
71378
71759
|
const files = [];
|
|
71379
71760
|
const walk2 = (directory) => {
|
|
71380
|
-
for (const entry of (0,
|
|
71381
|
-
const child2 = (0,
|
|
71761
|
+
for (const entry of (0, import_node_fs64.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
71762
|
+
const child2 = (0, import_node_path59.join)(directory, entry.name);
|
|
71382
71763
|
if (entry.isDirectory()) walk2(child2);
|
|
71383
71764
|
else files.push(child2);
|
|
71384
71765
|
}
|
|
@@ -71388,10 +71769,10 @@ function walkFiles(root) {
|
|
|
71388
71769
|
}
|
|
71389
71770
|
function bomPathFor(root) {
|
|
71390
71771
|
try {
|
|
71391
|
-
const registry2 = JSON.parse((0,
|
|
71392
|
-
return (0,
|
|
71772
|
+
const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0, import_node_path59.join)(root, "surfaces.json"), "utf8"));
|
|
71773
|
+
return (0, import_node_path59.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
71393
71774
|
} catch {
|
|
71394
|
-
return (0,
|
|
71775
|
+
return (0, import_node_path59.join)(root, "distribution-bom.json");
|
|
71395
71776
|
}
|
|
71396
71777
|
}
|
|
71397
71778
|
function rebuildTo(packageRoot, outDir) {
|
|
@@ -71404,40 +71785,40 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
71404
71785
|
});
|
|
71405
71786
|
}
|
|
71406
71787
|
function runDistStatus(root) {
|
|
71407
|
-
const stage = (0,
|
|
71788
|
+
const stage = (0, import_node_fs64.mkdtempSync)((0, import_node_path59.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
|
|
71408
71789
|
let overlayCount = 0;
|
|
71409
71790
|
try {
|
|
71410
|
-
const cliOut = (0,
|
|
71411
|
-
const hubOut = (0,
|
|
71412
|
-
rebuildTo((0,
|
|
71413
|
-
rebuildTo((0,
|
|
71791
|
+
const cliOut = (0, import_node_path59.join)(stage, "cli-dist");
|
|
71792
|
+
const hubOut = (0, import_node_path59.join)(stage, "hub-dist");
|
|
71793
|
+
rebuildTo((0, import_node_path59.join)(root, "cli"), cliOut);
|
|
71794
|
+
rebuildTo((0, import_node_path59.join)(root, "updater"), hubOut);
|
|
71414
71795
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
71415
71796
|
const rebuilt = (path2) => {
|
|
71416
71797
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
71417
|
-
return spec ? readOrNull((0,
|
|
71798
|
+
return spec ? readOrNull((0, import_node_path59.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
71418
71799
|
};
|
|
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,
|
|
71800
|
+
const committed = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
|
|
71801
|
+
const tree = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
|
|
71802
|
+
const distRoot = (0, import_node_path59.join)(root, "cli", "dist");
|
|
71803
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path59.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
71804
|
+
const bom = JSON.parse((0, import_node_fs64.readFileSync)(bomPathFor(root), "utf8"));
|
|
71424
71805
|
const digest = (entries) => {
|
|
71425
|
-
const overlay = (0,
|
|
71806
|
+
const overlay = (0, import_node_path59.join)(stage, `overlay-${overlayCount++}`);
|
|
71426
71807
|
for (const entry of entries) {
|
|
71427
|
-
const target = (0,
|
|
71428
|
-
(0,
|
|
71429
|
-
(0,
|
|
71808
|
+
const target = (0, import_node_path59.join)(overlay, entry.path);
|
|
71809
|
+
(0, import_node_fs64.mkdirSync)((0, import_node_path59.dirname)(target), { recursive: true });
|
|
71810
|
+
(0, import_node_fs64.writeFileSync)(target, entry.bytes);
|
|
71430
71811
|
}
|
|
71431
71812
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
71432
71813
|
};
|
|
71433
71814
|
return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
|
|
71434
71815
|
} finally {
|
|
71435
|
-
(0,
|
|
71816
|
+
(0, import_node_fs64.rmSync)(stage, { recursive: true, force: true });
|
|
71436
71817
|
}
|
|
71437
71818
|
}
|
|
71438
71819
|
|
|
71439
71820
|
// src/doc-refs-live.ts
|
|
71440
|
-
var
|
|
71821
|
+
var import_node_path60 = require("node:path");
|
|
71441
71822
|
var concrete = (value) => !!value && !/[<>{}$]/.test(value);
|
|
71442
71823
|
var liveOption = (tokens2, flag) => {
|
|
71443
71824
|
const index = tokens2.indexOf(flag);
|
|
@@ -71510,7 +71891,7 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
71510
71891
|
const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
|
|
71511
71892
|
const read = deps.readFile ?? readFileOrNull;
|
|
71512
71893
|
const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
|
|
71513
|
-
const body = read((0,
|
|
71894
|
+
const body = read((0, import_node_path60.join)(root, doc));
|
|
71514
71895
|
return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
|
|
71515
71896
|
}));
|
|
71516
71897
|
const remote = await checkRunbookLive(docs, live);
|
|
@@ -71547,7 +71928,7 @@ function createRunbookLiveDeps(cliBin) {
|
|
|
71547
71928
|
};
|
|
71548
71929
|
const first = await probe().catch(() => 599);
|
|
71549
71930
|
if (first < 500) return first;
|
|
71550
|
-
await new Promise((
|
|
71931
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e4));
|
|
71551
71932
|
return probe();
|
|
71552
71933
|
}
|
|
71553
71934
|
};
|
|
@@ -71721,7 +72102,7 @@ async function diagnoseHub(deps) {
|
|
|
71721
72102
|
|
|
71722
72103
|
// src/schedules-lift-command.ts
|
|
71723
72104
|
var import_promises12 = require("node:fs/promises");
|
|
71724
|
-
var
|
|
72105
|
+
var import_node_path61 = require("node:path");
|
|
71725
72106
|
init_clean_exit();
|
|
71726
72107
|
init_cli_shared();
|
|
71727
72108
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -71750,7 +72131,7 @@ async function readWorkflowFiles(dir) {
|
|
|
71750
72131
|
const files = [];
|
|
71751
72132
|
for (const name of names.sort()) {
|
|
71752
72133
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
71753
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0,
|
|
72134
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path61.join)(dir, name), "utf8") });
|
|
71754
72135
|
}
|
|
71755
72136
|
return files;
|
|
71756
72137
|
}
|
|
@@ -71833,8 +72214,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
71833
72214
|
|
|
71834
72215
|
// src/spawn-policy-core.ts
|
|
71835
72216
|
var import_node_child_process24 = require("node:child_process");
|
|
71836
|
-
var
|
|
71837
|
-
var
|
|
72217
|
+
var import_node_fs65 = require("node:fs");
|
|
72218
|
+
var import_node_path62 = require("node:path");
|
|
71838
72219
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
71839
72220
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
71840
72221
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -71920,7 +72301,7 @@ function runSpawnPolicy(root) {
|
|
|
71920
72301
|
for (const file of files) {
|
|
71921
72302
|
let raw;
|
|
71922
72303
|
try {
|
|
71923
|
-
raw = (0,
|
|
72304
|
+
raw = (0, import_node_fs65.readFileSync)((0, import_node_path62.join)(root, file), "utf8");
|
|
71924
72305
|
} catch {
|
|
71925
72306
|
continue;
|
|
71926
72307
|
}
|
|
@@ -71940,19 +72321,19 @@ function runSpawnPolicy(root) {
|
|
|
71940
72321
|
function registerDeveloperCommands(program3) {
|
|
71941
72322
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
71942
72323
|
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,
|
|
72324
|
+
const path2 = (0, import_node_path63.join)(process.cwd(), ".gitignore");
|
|
72325
|
+
const current = (0, import_node_fs66.existsSync)(path2) ? (0, import_node_fs66.readFileSync)(path2, "utf8") : null;
|
|
71945
72326
|
const plan = planManagedGitignore(current);
|
|
71946
72327
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
71947
72328
|
if (opts.json) {
|
|
71948
|
-
if (opts.write && plan.changed) (0,
|
|
72329
|
+
if (opts.write && plan.changed) (0, import_node_fs66.writeFileSync)(path2, plan.content, "utf8");
|
|
71949
72330
|
console.log(JSON.stringify(plan, null, 2));
|
|
71950
72331
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
71951
72332
|
return;
|
|
71952
72333
|
}
|
|
71953
72334
|
if (opts.write) {
|
|
71954
72335
|
if (plan.changed) {
|
|
71955
|
-
(0,
|
|
72336
|
+
(0, import_node_fs66.writeFileSync)(path2, plan.content, "utf8");
|
|
71956
72337
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
71957
72338
|
} else {
|
|
71958
72339
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -72148,6 +72529,7 @@ function registerDeveloperCommands(program3) {
|
|
|
72148
72529
|
const deps = { commandPaths, commandOptions, commandSource: source, base: o.base, runbooksOnly: o.runbooksOnly };
|
|
72149
72530
|
const result = o.live ? await runLiveDocRefs(root, deps, createRunbookLiveDeps(process.argv[1])) : runDocRefs(root, deps);
|
|
72150
72531
|
result.warnings.push(...resolved.warnings);
|
|
72532
|
+
nameUnknownCommandSources(result.findings, source, root);
|
|
72151
72533
|
if (o.json) {
|
|
72152
72534
|
consoleIo.log(JSON.stringify({ ok: result.ok, docCount: result.docCount, findings: result.findings, warnings: result.warnings }, null, 2));
|
|
72153
72535
|
if (!result.ok) process.exitCode = 1;
|
|
@@ -72269,7 +72651,7 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
72269
72651
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
72270
72652
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
72271
72653
|
function envHealLockPath(home) {
|
|
72272
|
-
return (0,
|
|
72654
|
+
return (0, import_node_path64.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
72273
72655
|
}
|
|
72274
72656
|
async function withEnvHealLock(what, run) {
|
|
72275
72657
|
try {
|
|
@@ -72407,7 +72789,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72407
72789
|
);
|
|
72408
72790
|
const result = applyPluginCachePlan(
|
|
72409
72791
|
plan,
|
|
72410
|
-
(p) => (0,
|
|
72792
|
+
(p) => (0, import_node_fs67.rmSync)(p, { recursive: true }),
|
|
72411
72793
|
stagingApplyFsGuard(configRoot)
|
|
72412
72794
|
);
|
|
72413
72795
|
return {
|
|
@@ -72442,7 +72824,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72442
72824
|
// has no generated routing index would
|
|
72443
72825
|
// get a permanent — demanding an artifact it never asked for.
|
|
72444
72826
|
docsIndexState: (root) => {
|
|
72445
|
-
if (!(0,
|
|
72827
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
72446
72828
|
const real = createDocsIndexDeps(root);
|
|
72447
72829
|
let docs;
|
|
72448
72830
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -72451,7 +72833,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72451
72833
|
},
|
|
72452
72834
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
72453
72835
|
healDocsIndex: (root) => {
|
|
72454
|
-
if (!(0,
|
|
72836
|
+
if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
72455
72837
|
const real = createDocsIndexDeps(root);
|
|
72456
72838
|
let docs;
|
|
72457
72839
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -72472,8 +72854,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72472
72854
|
});
|
|
72473
72855
|
const raced = await Promise.race([
|
|
72474
72856
|
work.then((r) => ({ ...r, timedOut: false })),
|
|
72475
|
-
new Promise((
|
|
72476
|
-
ceiling = setTimeout(() =>
|
|
72857
|
+
new Promise((resolve8) => {
|
|
72858
|
+
ceiling = setTimeout(() => resolve8({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
|
|
72477
72859
|
})
|
|
72478
72860
|
]);
|
|
72479
72861
|
if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
|
|
@@ -72506,8 +72888,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
72506
72888
|
incomplete: nb.incomplete,
|
|
72507
72889
|
timedOut: false
|
|
72508
72890
|
})),
|
|
72509
|
-
new Promise((
|
|
72510
|
-
ceiling = setTimeout(() =>
|
|
72891
|
+
new Promise((resolve8) => {
|
|
72892
|
+
ceiling = setTimeout(() => resolve8({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
|
|
72511
72893
|
})
|
|
72512
72894
|
]);
|
|
72513
72895
|
if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
|
|
@@ -72991,10 +73373,10 @@ tenant.command("logs <owner/repo> <stage>").description("READ-ONLY: read a tenan
|
|
|
72991
73373
|
return failGraceful(`runtime tenant logs: ${e.message}`);
|
|
72992
73374
|
}
|
|
72993
73375
|
});
|
|
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
|
|
73376
|
+
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
73377
|
if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime tenant redeploy: <stage> must be dev, rc, or main");
|
|
72996
73378
|
try {
|
|
72997
|
-
const raw = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, watch: o.watch });
|
|
73379
|
+
const raw = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, rollback: o.rollback, watch: o.watch });
|
|
72998
73380
|
const result = raw.workflowRuns ? { ...raw, workflowRuns: raw.workflowRuns.map(workflowRunWithEvidence) } : raw;
|
|
72999
73381
|
return printLine(o.json ? JSON.stringify(result, null, 2) : renderTenantRedeploy(result));
|
|
73000
73382
|
} catch (e) {
|
|
@@ -73254,7 +73636,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
73254
73636
|
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
73637
|
if (o.secretsFile) {
|
|
73256
73638
|
try {
|
|
73257
|
-
vars.push(`secrets=${(0,
|
|
73639
|
+
vars.push(`secrets=${(0, import_node_fs67.readFileSync)(o.secretsFile, "utf8")}`);
|
|
73258
73640
|
} catch (e) {
|
|
73259
73641
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
73260
73642
|
}
|
|
@@ -73388,7 +73770,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
73388
73770
|
const cfg = await loadConfig();
|
|
73389
73771
|
const reg = registryClientDeps(cfg);
|
|
73390
73772
|
const slug = slugOf(repo);
|
|
73391
|
-
const [metaRead, deployFacts, authority, dev, rc,
|
|
73773
|
+
const [metaRead, deployFacts, authority, dev, rc, main2] = await Promise.all([
|
|
73392
73774
|
fetchProjectBySlugChecked(slug, reg),
|
|
73393
73775
|
fetchDeployFactsBySlug(slug, reg),
|
|
73394
73776
|
fetchTrainAuthority(repo, reg),
|
|
@@ -73403,7 +73785,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
73403
73785
|
meta: metaRead.project,
|
|
73404
73786
|
deployFacts,
|
|
73405
73787
|
trainAuthority: authority.ok ? authority.authority : void 0,
|
|
73406
|
-
runtime: { dev, rc, main }
|
|
73788
|
+
runtime: { dev, rc, main: main2 }
|
|
73407
73789
|
});
|
|
73408
73790
|
console.log(JSON.stringify(report, null, 2));
|
|
73409
73791
|
if (!report.rcand.canApply) process.exitCode = 1;
|
|
@@ -73646,16 +74028,16 @@ function ciAuditDeps2() {
|
|
|
73646
74028
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
73647
74029
|
readSeedFile: (path2) => {
|
|
73648
74030
|
if (!root) return null;
|
|
73649
|
-
const fullPath = (0,
|
|
73650
|
-
return (0,
|
|
74031
|
+
const fullPath = (0, import_node_path64.join)(root, path2);
|
|
74032
|
+
return (0, import_node_fs67.existsSync)(fullPath) ? (0, import_node_fs67.readFileSync)(fullPath, "utf8") : null;
|
|
73651
74033
|
}
|
|
73652
74034
|
};
|
|
73653
74035
|
}
|
|
73654
74036
|
function hubRoot2() {
|
|
73655
|
-
const fromPkg = (0,
|
|
74037
|
+
const fromPkg = (0, import_node_path64.join)(__dirname, "..", "..");
|
|
73656
74038
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
73657
|
-
if ((0,
|
|
73658
|
-
if ((0,
|
|
74039
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(fromPkg, marker))) return fromPkg;
|
|
74040
|
+
if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(process.cwd(), marker))) return process.cwd();
|
|
73659
74041
|
return null;
|
|
73660
74042
|
}
|
|
73661
74043
|
registerQueryCommands(program2);
|
|
@@ -73812,10 +74194,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
73812
74194
|
targets = resolution.targets;
|
|
73813
74195
|
}
|
|
73814
74196
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
73815
|
-
const fileMatrix = (0,
|
|
74197
|
+
const fileMatrix = (0, import_node_fs67.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs67.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73816
74198
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
73817
74199
|
const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
73818
|
-
const sanctioned = (0,
|
|
74200
|
+
const sanctioned = (0, import_node_fs67.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs67.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
73819
74201
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
73820
74202
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
73821
74203
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -73852,16 +74234,16 @@ function directoryBytes(path2) {
|
|
|
73852
74234
|
let total = 0;
|
|
73853
74235
|
let entries;
|
|
73854
74236
|
try {
|
|
73855
|
-
entries = (0,
|
|
74237
|
+
entries = (0, import_node_fs67.readdirSync)(path2, { withFileTypes: true });
|
|
73856
74238
|
} catch {
|
|
73857
74239
|
return 0;
|
|
73858
74240
|
}
|
|
73859
74241
|
for (const entry of entries) {
|
|
73860
|
-
const child2 = (0,
|
|
74242
|
+
const child2 = (0, import_node_path64.join)(path2, entry.name);
|
|
73861
74243
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
73862
74244
|
else {
|
|
73863
74245
|
try {
|
|
73864
|
-
total += (0,
|
|
74246
|
+
total += (0, import_node_fs67.statSync)(child2).size;
|
|
73865
74247
|
} catch {
|
|
73866
74248
|
}
|
|
73867
74249
|
}
|
|
@@ -73869,25 +74251,25 @@ function directoryBytes(path2) {
|
|
|
73869
74251
|
return total;
|
|
73870
74252
|
}
|
|
73871
74253
|
function listDirEntries(dir) {
|
|
73872
|
-
return (0,
|
|
74254
|
+
return (0, import_node_fs67.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
73873
74255
|
}
|
|
73874
74256
|
function readInstalledPluginRefs(configRoot) {
|
|
73875
74257
|
const p = installedPluginsPathForConfig(configRoot);
|
|
73876
|
-
if (!(0,
|
|
74258
|
+
if (!(0, import_node_fs67.existsSync)(p)) return [];
|
|
73877
74259
|
try {
|
|
73878
|
-
return installedPluginPaths((0,
|
|
74260
|
+
return installedPluginPaths((0, import_node_fs67.readFileSync)(p, "utf8"));
|
|
73879
74261
|
} catch {
|
|
73880
74262
|
return null;
|
|
73881
74263
|
}
|
|
73882
74264
|
}
|
|
73883
74265
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
73884
74266
|
return {
|
|
73885
|
-
exists: (p) => (0,
|
|
73886
|
-
listVersionDirs: (root) => (0,
|
|
74267
|
+
exists: (p) => (0, import_node_fs67.existsSync)(p),
|
|
74268
|
+
listVersionDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
73887
74269
|
dirBytes,
|
|
73888
|
-
listStagingDirs: (root) => (0,
|
|
74270
|
+
listStagingDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
73889
74271
|
try {
|
|
73890
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
74272
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path64.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs67.statSync)(p).mtimeMs) };
|
|
73891
74273
|
} catch {
|
|
73892
74274
|
return { name: d.name, mtimeMs: Date.now() };
|
|
73893
74275
|
}
|
|
@@ -73901,10 +74283,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
73901
74283
|
return {
|
|
73902
74284
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
73903
74285
|
mtimeMs: (name) => {
|
|
73904
|
-
const p = (0,
|
|
73905
|
-
if (!(0,
|
|
74286
|
+
const p = (0, import_node_path64.join)(stagingRoot, name);
|
|
74287
|
+
if (!(0, import_node_fs67.existsSync)(p)) return null;
|
|
73906
74288
|
try {
|
|
73907
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
74289
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs67.statSync)(q).mtimeMs);
|
|
73908
74290
|
} catch {
|
|
73909
74291
|
return null;
|
|
73910
74292
|
}
|
|
@@ -73930,7 +74312,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
73930
74312
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
73931
74313
|
);
|
|
73932
74314
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
73933
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
74315
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs67.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
73934
74316
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
73935
74317
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
73936
74318
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -73940,7 +74322,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
|
|
|
73940
74322
|
let primary = o.repoRoot;
|
|
73941
74323
|
if (!primary) {
|
|
73942
74324
|
const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
|
|
73943
|
-
primary = common ? (0,
|
|
74325
|
+
primary = common ? (0, import_node_path64.dirname)(common) : "";
|
|
73944
74326
|
}
|
|
73945
74327
|
if (!primary) {
|
|
73946
74328
|
const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";
|