@mutmutco/cli 4.3.68 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +45 -47
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -6380,7 +6380,7 @@ function decorateAwsModel(entries, registry2, verdicts) {
|
|
|
6380
6380
|
});
|
|
6381
6381
|
}
|
|
6382
6382
|
var DECLARED_ENTRIES = [
|
|
6383
|
-
{ name: "mmi-backup (mm-central,
|
|
6383
|
+
{ name: "mmi-backup (mm-central, mm-work, zuber, mmi-oguz)", cadence: "17 2 * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-backup \u2192 /opt/mmi-control/nightly-backup.sh (verify: mmi-cli devops runtime box get <box> --ssh)" },
|
|
6384
6384
|
// #3370/#6270: the one runner hygiene layer (runner-hygiene.yml was folded in and deleted). A workflow ran ON the runner and needed checkout +
|
|
6385
6385
|
// setup-node before it can prune, so at 100% it dies at checkout and never prunes — the cleanup is
|
|
6386
6386
|
// unavailable exactly when it is needed. This one is root cron on the box: no checkout, no node, and
|
|
@@ -9439,6 +9439,18 @@ function resolveUseEnvNames(keys, name) {
|
|
|
9439
9439
|
}
|
|
9440
9440
|
return { ok: true, entries };
|
|
9441
9441
|
}
|
|
9442
|
+
function referencesDollarEnv(arg, name) {
|
|
9443
|
+
return arg.includes(`\${${name}}`) || new RegExp(`\\$${name}(?![A-Za-z0-9_])`).test(arg);
|
|
9444
|
+
}
|
|
9445
|
+
function secretsUseDollarHint(command, envNames) {
|
|
9446
|
+
for (const arg of command) {
|
|
9447
|
+
for (const name of envNames) {
|
|
9448
|
+
if (!referencesDollarEnv(arg, name)) continue;
|
|
9449
|
+
return `secrets use: $${name} is injected into the CHILD's env, not your shell \u2014 the caller expanded it (empty) before this ran. Wrap the consumer so expansion happens inside: bash -c 'your-command "$${name}"'`;
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
return null;
|
|
9453
|
+
}
|
|
9442
9454
|
var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
9443
9455
|
async function drainHttpPoolBeforeSpawn() {
|
|
9444
9456
|
const drained = await Promise.race([
|
|
@@ -9490,6 +9502,8 @@ async function secretsUse(deps, keys, opts) {
|
|
|
9490
9502
|
deps.err(named.error);
|
|
9491
9503
|
return false;
|
|
9492
9504
|
}
|
|
9505
|
+
const dollarHint = secretsUseDollarHint(command, named.entries.map((e) => e.envName));
|
|
9506
|
+
if (dollarHint) deps.err(dollarHint);
|
|
9493
9507
|
const repo = await targetRepo(deps, opts);
|
|
9494
9508
|
const slug = opts.slug?.toLowerCase();
|
|
9495
9509
|
const env = { ...process.env };
|
|
@@ -14238,9 +14252,6 @@ function expectedHosts(cfg) {
|
|
|
14238
14252
|
for (const env of ENV_PREFIXES) out.push(env ? `${env}.${base}` : base);
|
|
14239
14253
|
}
|
|
14240
14254
|
}
|
|
14241
|
-
if (cfg.fofuSubdomain !== void 0) {
|
|
14242
|
-
out.push(cfg.fofuSubdomain ? `${cfg.fofuSubdomain}.fofu.ai` : "fofu.ai");
|
|
14243
|
-
}
|
|
14244
14255
|
return uniq([...out, ...cfg.edgeHosts ?? []]);
|
|
14245
14256
|
}
|
|
14246
14257
|
function expectedJsOrigins(cfg) {
|
|
@@ -14305,10 +14316,7 @@ function parseOauthConfig(mmiConfig, slug) {
|
|
|
14305
14316
|
);
|
|
14306
14317
|
}
|
|
14307
14318
|
const extraCallbackPaths = parseExtraCallbackPaths(raw.extraCallbackPaths);
|
|
14308
|
-
|
|
14309
|
-
const rawFofuSub = raw.fofuSubdomain;
|
|
14310
|
-
const fofuSubdomain = meta.fofuEnabled === true ? typeof rawFofuSub === "string" ? rawFofuSub : defaultSubdomain(slug) : void 0;
|
|
14311
|
-
return { subdomains, domains, callbackPath, extraCallbackPaths, fofuSubdomain, edgeHosts: Object.values(edgeDomainsByStage(mmiConfig)) };
|
|
14319
|
+
return { subdomains, domains, callbackPath, extraCallbackPaths, edgeHosts: Object.values(edgeDomainsByStage(mmiConfig)) };
|
|
14312
14320
|
}
|
|
14313
14321
|
function probeRedirectUri(callbackPath, port = 9123) {
|
|
14314
14322
|
return `http://localhost:${port}${callbackPath}`;
|
|
@@ -15862,10 +15870,10 @@ var rollout_plan_default = {
|
|
|
15862
15870
|
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)."
|
|
15863
15871
|
},
|
|
15864
15872
|
baseline: {
|
|
15865
|
-
version: "4.
|
|
15866
|
-
tag: "v4.
|
|
15867
|
-
commit: "
|
|
15868
|
-
npm: "@mutmutco/cli@4.
|
|
15873
|
+
version: "4.4.0",
|
|
15874
|
+
tag: "v4.4.0",
|
|
15875
|
+
commit: "8e1fae5ff484",
|
|
15876
|
+
npm: "@mutmutco/cli@4.4.0"
|
|
15869
15877
|
},
|
|
15870
15878
|
exitCriterion: "fleet-n-of-n",
|
|
15871
15879
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15882,14 +15890,14 @@ var rollout_plan_default = {
|
|
|
15882
15890
|
repo: "mutmutco/mmi-hub",
|
|
15883
15891
|
role: "canary",
|
|
15884
15892
|
schedule: "train",
|
|
15885
|
-
v3Target: "v4.
|
|
15893
|
+
v3Target: "v4.4.0"
|
|
15886
15894
|
}
|
|
15887
15895
|
],
|
|
15888
15896
|
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.",
|
|
15889
15897
|
rollback: {
|
|
15890
15898
|
independent: true,
|
|
15891
|
-
mechanism: "npm dist-tag latest -> 4.
|
|
15892
|
-
v3Target: "v4.
|
|
15899
|
+
mechanism: "npm dist-tag latest -> 4.4.0 and redeploy the Hub Lambda from tag v4.4.0 (8e1fae5ff484); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15900
|
+
v3Target: "v4.4.0 (@mutmutco/cli@4.4.0, tag commit 8e1fae5ff484 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15893
15901
|
}
|
|
15894
15902
|
},
|
|
15895
15903
|
{
|
|
@@ -15986,12 +15994,6 @@ var rollout_plan_default = {
|
|
|
15986
15994
|
schedule: "train",
|
|
15987
15995
|
v3Target: "v0.3.0"
|
|
15988
15996
|
},
|
|
15989
|
-
{
|
|
15990
|
-
repo: "mutmutco/mm-fofu",
|
|
15991
|
-
role: "member",
|
|
15992
|
-
schedule: "train",
|
|
15993
|
-
v3Target: "v0.1.0"
|
|
15994
|
-
},
|
|
15995
15997
|
{
|
|
15996
15998
|
repo: "mutmutco/mmi-junior",
|
|
15997
15999
|
role: "member",
|
|
@@ -29667,12 +29669,12 @@ function registerDeployCommands(program3) {
|
|
|
29667
29669
|
init_cli_shared();
|
|
29668
29670
|
|
|
29669
29671
|
// src/project-set.ts
|
|
29670
|
-
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "releaseChannel", "releaseLanguage", "dsManifestPath", "
|
|
29672
|
+
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "releaseChannel", "releaseLanguage", "dsManifestPath", "consumesDesignSystem", "ci", "requiredChecks", "requiredCheckBranches", "ciExemptReason", "gate", "seedCanary"];
|
|
29671
29673
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
29672
29674
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
29673
29675
|
var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
|
|
29674
29676
|
var SECRET_ENV_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
29675
|
-
var BUILD_SECRET_REF_RE = /^(?:[A-Z_][A-Z0-9_]*=)?(?:[A-Z_][A-Z0-9_]*|(?:
|
|
29677
|
+
var BUILD_SECRET_REF_RE = /^(?:[A-Z_][A-Z0-9_]*=)?(?:[A-Z_][A-Z0-9_]*|(?:_org\/[a-z0-9][a-z0-9-]*):[A-Z_][A-Z0-9_]*|@github-packages-token)$/;
|
|
29676
29678
|
function previewRegistryMetaMerge(existing, patch) {
|
|
29677
29679
|
const out = { ...existing ?? {} };
|
|
29678
29680
|
for (const [key, value] of Object.entries(patch)) {
|
|
@@ -29868,7 +29870,7 @@ function parseOauthVar(raw) {
|
|
|
29868
29870
|
throw new Error(`org project set: oauth must be JSON, e.g. {"subdomains":["app"],"domains":["example.co"],"callbackPath":"${DEFAULT_CALLBACK_PATH}"}`);
|
|
29869
29871
|
}
|
|
29870
29872
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
29871
|
-
throw new Error("org project set: oauth must be a {subdomains,domains,callbackPath,extraCallbackPaths
|
|
29873
|
+
throw new Error("org project set: oauth must be a {subdomains,domains,callbackPath,extraCallbackPaths} object");
|
|
29872
29874
|
}
|
|
29873
29875
|
const map = parsed;
|
|
29874
29876
|
const out = {};
|
|
@@ -29896,11 +29898,8 @@ function parseOauthVar(raw) {
|
|
|
29896
29898
|
} catch (e) {
|
|
29897
29899
|
throw new Error(`org project set: ${e.message}`);
|
|
29898
29900
|
}
|
|
29899
|
-
} else if (key === "fofuSubdomain") {
|
|
29900
|
-
if (typeof value !== "string") throw new Error('org project set: oauth.fofuSubdomain must be a string ("" selects the apex fofu.ai)');
|
|
29901
|
-
out.fofuSubdomain = value.trim();
|
|
29902
29901
|
} else {
|
|
29903
|
-
throw new Error(`org project set: oauth key "${key}" \u2014 expected only subdomains/domains/callbackPath/extraCallbackPaths
|
|
29902
|
+
throw new Error(`org project set: oauth key "${key}" \u2014 expected only subdomains/domains/callbackPath/extraCallbackPaths`);
|
|
29904
29903
|
}
|
|
29905
29904
|
}
|
|
29906
29905
|
return out;
|
|
@@ -29922,11 +29921,6 @@ function parsePublishRequiredVar(raw) {
|
|
|
29922
29921
|
if (raw === "false") return false;
|
|
29923
29922
|
throw new Error("org project set: publishRequired must be true or false");
|
|
29924
29923
|
}
|
|
29925
|
-
function parseFofuEnabledVar(raw) {
|
|
29926
|
-
if (raw === "true") return true;
|
|
29927
|
-
if (raw === "false") return false;
|
|
29928
|
-
throw new Error("org project set: fofuEnabled must be true or false");
|
|
29929
|
-
}
|
|
29930
29924
|
function parseRuntimeVaultOnlyVar(raw) {
|
|
29931
29925
|
if (raw === "true") return true;
|
|
29932
29926
|
if (raw === "false") return false;
|
|
@@ -30084,7 +30078,6 @@ var SETTABLE_VAR_KEYS = [
|
|
|
30084
30078
|
"releaseChannel",
|
|
30085
30079
|
"releaseLanguage",
|
|
30086
30080
|
"dsManifestPath",
|
|
30087
|
-
"fofuEnabled",
|
|
30088
30081
|
"consumesDesignSystem",
|
|
30089
30082
|
"runtimeVaultOnly",
|
|
30090
30083
|
"requiredGcpApis",
|
|
@@ -30113,12 +30106,11 @@ var SETTABLE_VAR_HINTS = {
|
|
|
30113
30106
|
releaseChannel: "Slack channel id, e.g. C0BE0BRGAT0",
|
|
30114
30107
|
releaseLanguage: "2-letter language code for release notes, e.g. tr (default en)",
|
|
30115
30108
|
dsManifestPath: "relative path to a package.json, e.g. web/package.json",
|
|
30116
|
-
fofuEnabled: "true|false",
|
|
30117
30109
|
consumesDesignSystem: '"fofu"',
|
|
30118
30110
|
runtimeVaultOnly: "true|false",
|
|
30119
30111
|
seedCanary: "true|false",
|
|
30120
30112
|
repos: 'JSON array, e.g. ["mutmutco/mm-foo"]',
|
|
30121
|
-
oauth: "JSON {subdomains,domains,callbackPath,extraCallbackPaths
|
|
30113
|
+
oauth: "JSON {subdomains,domains,callbackPath,extraCallbackPaths}",
|
|
30122
30114
|
requiredGcpApis: "comma-string",
|
|
30123
30115
|
requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
|
|
30124
30116
|
requiredBuildSecrets: 'JSON flat array, e.g. ["NODE_AUTH_TOKEN=@github-packages-token"]',
|
|
@@ -30214,8 +30206,6 @@ function buildProjectSetPatch(input) {
|
|
|
30214
30206
|
patch[key] = parseReposVar(raw);
|
|
30215
30207
|
} else if (key === "publishRequired") {
|
|
30216
30208
|
patch[key] = parsePublishRequiredVar(raw);
|
|
30217
|
-
} else if (key === "fofuEnabled") {
|
|
30218
|
-
patch[key] = parseFofuEnabledVar(raw);
|
|
30219
30209
|
} else if (key === "runtimeVaultOnly") {
|
|
30220
30210
|
patch[key] = parseRuntimeVaultOnlyVar(raw);
|
|
30221
30211
|
} else if (key === "seedCanary") {
|
|
@@ -36752,7 +36742,7 @@ async function runStageLiveDown(deps, t) {
|
|
|
36752
36742
|
var GITHUB_PACKAGES_TOKEN_REF = "@github-packages-token";
|
|
36753
36743
|
var ENV_ID_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
36754
36744
|
var BARE_VAULT_KEY_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
36755
|
-
var CROSS_SLUG_REF_RE = /^(?:
|
|
36745
|
+
var CROSS_SLUG_REF_RE = /^(?:_org\/[a-z0-9][a-z0-9-]*):[A-Z_][A-Z0-9_]*$/;
|
|
36756
36746
|
function parseStageBuildSecretEntry(entry) {
|
|
36757
36747
|
const trimmed = entry.trim();
|
|
36758
36748
|
if (!trimmed) return null;
|
|
@@ -36824,7 +36814,6 @@ async function fetchVaultRef(fetchVault, ref) {
|
|
|
36824
36814
|
if (colon === -1) return fetchVault(ref);
|
|
36825
36815
|
const prefix = ref.slice(0, colon);
|
|
36826
36816
|
const key = ref.slice(colon + 1);
|
|
36827
|
-
if (prefix === "mm-fofu") return fetchVault(key, { slug: "mm-fofu" });
|
|
36828
36817
|
if (prefix.startsWith("_org/")) {
|
|
36829
36818
|
const provider = prefix.slice("_org/".length);
|
|
36830
36819
|
return fetchVault(`${provider}/${key}`, { slug: "_org" });
|
|
@@ -42014,7 +42003,7 @@ function formatBoxTable(boxes) {
|
|
|
42014
42003
|
}
|
|
42015
42004
|
return lines2.join("\n");
|
|
42016
42005
|
}
|
|
42017
|
-
function formatSshRecipe(box) {
|
|
42006
|
+
function formatSshRecipe(box, remoteCommand) {
|
|
42018
42007
|
if (!box.address) {
|
|
42019
42008
|
return `box ${box.name} has no public IPv4 \u2014 it is not directly reachable over ssh.`;
|
|
42020
42009
|
}
|
|
@@ -42028,13 +42017,21 @@ function formatSshRecipe(box) {
|
|
|
42028
42017
|
" --with-decryption --query Parameter.Value --output text --region eu-central-1 \\",
|
|
42029
42018
|
` | tr -d '\\r' > "$kf"`,
|
|
42030
42019
|
'test -s "$kf" # never hand ssh an empty key file',
|
|
42031
|
-
`ssh -i "$kf" -o StrictHostKeyChecking=accept-new root@${box.address}
|
|
42020
|
+
`ssh -i "$kf" -o StrictHostKeyChecking=accept-new root@${box.address} ${shellSingleQuote(sshRemoteCommand(remoteCommand))}`
|
|
42032
42021
|
].join("\n");
|
|
42033
42022
|
}
|
|
42023
|
+
var SSH_DEFAULT_PROBE = "hostname; uptime";
|
|
42024
|
+
function sshRemoteCommand(remoteCommand) {
|
|
42025
|
+
const trimmed = remoteCommand?.trim();
|
|
42026
|
+
return trimmed ? trimmed : SSH_DEFAULT_PROBE;
|
|
42027
|
+
}
|
|
42028
|
+
function shellSingleQuote(value) {
|
|
42029
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
42030
|
+
}
|
|
42034
42031
|
var SSH_RECIPE_AGENT_NOTE = "# agent callers: this is multi-line, so running it inline is denied (#1473/#2125).\n# Use `mmi-cli devops runtime box get <name> --ssh --script <path>` to write it to a file, then `bash <path>`.";
|
|
42035
|
-
function sshRecipeScript(box) {
|
|
42032
|
+
function sshRecipeScript(box, remoteCommand) {
|
|
42036
42033
|
return `#!/usr/bin/env bash
|
|
42037
|
-
${formatSshRecipe(box)}
|
|
42034
|
+
${formatSshRecipe(box, remoteCommand)}
|
|
42038
42035
|
`;
|
|
42039
42036
|
}
|
|
42040
42037
|
|
|
@@ -42109,7 +42106,7 @@ function registerBoxCommands(program3) {
|
|
|
42109
42106
|
await failGraceful(e.message);
|
|
42110
42107
|
}
|
|
42111
42108
|
});
|
|
42112
|
-
box.command("get <name>").description("one box by name \u2014 its address and ssh key path; --ssh prints a ready-to-run connect recipe").option("--json", "machine-readable output").option("--ssh", "print a copy-paste ssh recipe (materializes the key to a temp file; never echoes it)").option("--script <path>", "with --ssh: write the recipe to <path> and print the one line to run \u2014 the runnable form for agent callers, whose transport denies a multi-line inline body (#3385)").action(async (name, o) => {
|
|
42109
|
+
box.command("get <name> [command...]").description("one box by name \u2014 its address and ssh key path; --ssh prints a ready-to-run connect recipe").option("--json", "machine-readable output").option("--ssh", "print a copy-paste ssh recipe (materializes the key to a temp file; never echoes it)").option("--script <path>", "with --ssh: write the recipe to <path> and print the one line to run \u2014 the runnable form for agent callers, whose transport denies a multi-line inline body (#3385)").action(async (name, command, o) => {
|
|
42113
42110
|
try {
|
|
42114
42111
|
const { boxes, incomplete } = await fetchInventory();
|
|
42115
42112
|
const result = lookupBox(name, boxes, incomplete);
|
|
@@ -42129,8 +42126,9 @@ function registerBoxCommands(program3) {
|
|
|
42129
42126
|
await failGraceful("runtime box get: --script requires --ssh (it writes the ssh connect recipe)");
|
|
42130
42127
|
return;
|
|
42131
42128
|
}
|
|
42129
|
+
const remote = command && command.length ? command.join(" ") : void 0;
|
|
42132
42130
|
const wroteScript = o.ssh && o.script ? o.script : null;
|
|
42133
|
-
if (wroteScript) (0, import_node_fs42.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
|
|
42131
|
+
if (wroteScript) (0, import_node_fs42.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
|
|
42134
42132
|
if (o.json) {
|
|
42135
42133
|
console.log(JSON.stringify({
|
|
42136
42134
|
box: found,
|
|
@@ -42138,7 +42136,7 @@ function registerBoxCommands(program3) {
|
|
|
42138
42136
|
...wroteScript ? { script: { path: wroteScript, run: `bash "${wroteScript}"` } } : {}
|
|
42139
42137
|
}, null, 2));
|
|
42140
42138
|
} else if (wroteScript) console.log(`wrote ${wroteScript} \u2014 run: bash "${wroteScript}"`);
|
|
42141
|
-
else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
42139
|
+
else if (o.ssh) console.log(`${formatSshRecipe(found, remote)}
|
|
42142
42140
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
42143
42141
|
else console.log(formatBoxTable([found]));
|
|
42144
42142
|
if (!o.json) warnIncomplete2(incomplete);
|
package/package.json
CHANGED