@mutmutco/codex-plugin 4.5.99 → 4.5.100
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/cli/dist/main.cjs
CHANGED
|
@@ -8288,6 +8288,28 @@ async function postJson(pathSuffix, payload, deps, method = "POST", opts = {}) {
|
|
|
8288
8288
|
return { ok: false, status: 0, body: null, error: e.message, unreachable: "transport" };
|
|
8289
8289
|
}
|
|
8290
8290
|
}
|
|
8291
|
+
async function edgeDnsForStage(slug, stage, deps, ensure = false) {
|
|
8292
|
+
if (!deps.baseUrl) throw new Error("Hub API URL unavailable");
|
|
8293
|
+
const token = await deps.token();
|
|
8294
|
+
if (!token) throw new Error("Hub session token unavailable");
|
|
8295
|
+
const path2 = `/projects/${encodeURIComponent(slug)}/edge-dns/${stage}`;
|
|
8296
|
+
let body;
|
|
8297
|
+
if (ensure) {
|
|
8298
|
+
const write = await postJson(path2, {}, deps, "POST", { noRetry: true });
|
|
8299
|
+
if (!write.ok) throw new Error(`Hub edge dns ensure failed: ${write.error ?? `HTTP ${write.status}`}`);
|
|
8300
|
+
body = write.body;
|
|
8301
|
+
} else {
|
|
8302
|
+
const res = await retriedFetch(
|
|
8303
|
+
deps,
|
|
8304
|
+
`${deps.baseUrl.replace(/\/$/, "")}${path2}`,
|
|
8305
|
+
{ method: "GET", headers: { Authorization: `Bearer ${token}` } }
|
|
8306
|
+
);
|
|
8307
|
+
if (!res.ok) throw new Error(`Hub edge dns read failed: HTTP ${res.status}`);
|
|
8308
|
+
body = await res.json();
|
|
8309
|
+
}
|
|
8310
|
+
if (typeof body?.required !== "boolean") throw new Error("Hub edge dns response invalid");
|
|
8311
|
+
return body;
|
|
8312
|
+
}
|
|
8291
8313
|
async function edgeCertificateForStage(slug, stage, deps, ensure = false) {
|
|
8292
8314
|
if (!deps.baseUrl) throw new Error("Hub API URL unavailable");
|
|
8293
8315
|
const token = await deps.token();
|
|
@@ -15861,8 +15883,11 @@ function edgeDomainsByStage(meta) {
|
|
|
15861
15883
|
}
|
|
15862
15884
|
return out;
|
|
15863
15885
|
}
|
|
15864
|
-
function inferEdgeDomainsForWrite(slug) {
|
|
15886
|
+
function inferEdgeDomainsForWrite(slug, suite) {
|
|
15865
15887
|
const subdomain = defaultSubdomain(slug);
|
|
15888
|
+
if (suite === "mm-work" && slug !== suite) {
|
|
15889
|
+
return { dev: `dev.${subdomain}.mutmut.work`, main: `${subdomain}.mutmut.work` };
|
|
15890
|
+
}
|
|
15866
15891
|
return {
|
|
15867
15892
|
dev: `dev.${subdomain}.mutatismutandis.co`,
|
|
15868
15893
|
rc: `rc.${subdomain}.mutatismutandis.co`,
|
|
@@ -16236,10 +16261,11 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
|
|
|
16236
16261
|
}
|
|
16237
16262
|
const deployModel = inferDeployModelForWrite({ ...shape, projectType }, parsedRepo.fullName);
|
|
16238
16263
|
const releaseTrack = (isReleaseTrack(options.releaseTrack) ? options.releaseTrack : void 0) ?? (isReleaseTrack(options.existing?.releaseTrack) ? options.existing.releaseTrack : void 0) ?? resolveBootstrapReleaseTrack(cls, void 0);
|
|
16239
|
-
const
|
|
16240
|
-
const
|
|
16264
|
+
const suite = vars.SUITE || options.existing?.suite || void 0;
|
|
16265
|
+
const inferredDomains = inferEdgeDomainsForWrite(slug, suite);
|
|
16266
|
+
const derivedDomains = Object.fromEntries(branchesForTrack(releaseTrack).flatMap((branch) => {
|
|
16241
16267
|
const stage = branch === "development" ? "dev" : branch;
|
|
16242
|
-
return [stage, inferredDomains[stage]];
|
|
16268
|
+
return inferredDomains[stage] ? [[stage, inferredDomains[stage]]] : [];
|
|
16243
16269
|
}));
|
|
16244
16270
|
const num = (v) => {
|
|
16245
16271
|
if (v == null || v === "") return void 0;
|
|
@@ -16263,6 +16289,7 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
|
|
|
16263
16289
|
// Identity. name/division default off the repo name when the skill didn't pass them.
|
|
16264
16290
|
name: vars.NAME || parsedRepo.name,
|
|
16265
16291
|
division: vars.DIVISION || parsedRepo.name.split("-")[0] || void 0,
|
|
16292
|
+
suite: vars.SUITE || void 0,
|
|
16266
16293
|
repos: [`${parsedRepo.owner}/${slug}`],
|
|
16267
16294
|
branch: vars.BRANCH || (cls === "content" ? "main" : "development"),
|
|
16268
16295
|
class: cls,
|
|
@@ -16362,6 +16389,11 @@ function decideSeedPrAction(openPrs) {
|
|
|
16362
16389
|
}
|
|
16363
16390
|
return { action: "create" };
|
|
16364
16391
|
}
|
|
16392
|
+
function decideFirstDevDeploy(input) {
|
|
16393
|
+
if (!input.devRowWritten) return "skip";
|
|
16394
|
+
if (input.seedMode === "direct") return "dispatch";
|
|
16395
|
+
return (input.seedPrs ?? []).some((pr) => typeof pr?.mergedAt === "string" && pr.mergedAt !== "") ? "dispatch" : "defer";
|
|
16396
|
+
}
|
|
16365
16397
|
function decideSeedDelivery(branchRules) {
|
|
16366
16398
|
const rules = Array.isArray(branchRules) ? branchRules : [];
|
|
16367
16399
|
const types = new Set(
|
|
@@ -17572,10 +17604,10 @@ var rollout_plan_default = {
|
|
|
17572
17604
|
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)."
|
|
17573
17605
|
},
|
|
17574
17606
|
baseline: {
|
|
17575
|
-
version: "4.5.
|
|
17576
|
-
tag: "v4.5.
|
|
17577
|
-
commit: "
|
|
17578
|
-
npm: "@mutmutco/cli@4.5.
|
|
17607
|
+
version: "4.5.100",
|
|
17608
|
+
tag: "v4.5.100",
|
|
17609
|
+
commit: "ba367dd0d814",
|
|
17610
|
+
npm: "@mutmutco/cli@4.5.100"
|
|
17579
17611
|
},
|
|
17580
17612
|
exitCriterion: "fleet-n-of-n",
|
|
17581
17613
|
hubOnlyShortcut: "forbidden",
|
|
@@ -17592,14 +17624,14 @@ var rollout_plan_default = {
|
|
|
17592
17624
|
repo: "mutmutco/mmi-hub",
|
|
17593
17625
|
role: "canary",
|
|
17594
17626
|
schedule: "train",
|
|
17595
|
-
v3Target: "v4.5.
|
|
17627
|
+
v3Target: "v4.5.100"
|
|
17596
17628
|
}
|
|
17597
17629
|
],
|
|
17598
17630
|
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.",
|
|
17599
17631
|
rollback: {
|
|
17600
17632
|
independent: true,
|
|
17601
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
17602
|
-
v3Target: "v4.5.
|
|
17633
|
+
mechanism: "npm dist-tag latest -> 4.5.100 and redeploy the Hub Lambda from tag v4.5.100 (ba367dd0d814); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
17634
|
+
v3Target: "v4.5.100 (@mutmutco/cli@4.5.100, tag commit ba367dd0d814 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
17603
17635
|
}
|
|
17604
17636
|
},
|
|
17605
17637
|
{
|
|
@@ -33890,6 +33922,28 @@ function edgeCertificateChecks(results) {
|
|
|
33890
33922
|
}];
|
|
33891
33923
|
});
|
|
33892
33924
|
}
|
|
33925
|
+
function edgeDnsChecks(results, repo) {
|
|
33926
|
+
return results.flatMap(({ stage, status, error }) => {
|
|
33927
|
+
if (!error && status?.required === false) return [];
|
|
33928
|
+
const bad = (status?.hosts ?? []).filter((h) => h.status === "missing" || h.status === "conflict");
|
|
33929
|
+
const ok = !error && status?.complete !== false && bad.length === 0;
|
|
33930
|
+
return [{
|
|
33931
|
+
ok,
|
|
33932
|
+
label: `Cloudflare edge DNS: ${stage}`,
|
|
33933
|
+
detail: ok ? void 0 : error ? `DNS records unreadable (${error})` : `${bad.map((h) => `${h.host} ${h.status}${h.hint ? ` (${h.hint})` : ""}`).join("; ")} \u2014 run: mmi-cli devops runtime edge dns ensure ${repo} ${stage}`
|
|
33934
|
+
}];
|
|
33935
|
+
});
|
|
33936
|
+
}
|
|
33937
|
+
function firstDevDeployCheck(repo, facts, factsRead) {
|
|
33938
|
+
const dev = facts?.stages?.dev;
|
|
33939
|
+
if (!factsRead || dev?.present !== true) return [];
|
|
33940
|
+
const ok = Boolean(dev.deployedAt || dev.deployedTag);
|
|
33941
|
+
return [{
|
|
33942
|
+
ok,
|
|
33943
|
+
label: "first dev deploy happened",
|
|
33944
|
+
detail: ok ? void 0 : `DEPLOY#dev has never deployed \u2014 run: mmi-cli devops runtime tenant redeploy ${repo} dev --watch`
|
|
33945
|
+
}];
|
|
33946
|
+
}
|
|
33893
33947
|
async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
33894
33948
|
const branchesWanted = expectedBranches(repoClass, releaseTrack);
|
|
33895
33949
|
const baseBranch = releaseTrack === "trunk" || repoClass === "content" ? "main" : "development";
|
|
@@ -34012,6 +34066,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
34012
34066
|
if (check) checks.push(check);
|
|
34013
34067
|
}
|
|
34014
34068
|
checks.push(...edgeCertificateChecks(deps.edgeCertificates ?? []));
|
|
34069
|
+
checks.push(...edgeDnsChecks(deps.edgeDns ?? [], repo));
|
|
34015
34070
|
const portRangeCheck = centralContainerPortRangeCheck(deps.deployModel, deps.projectMeta?.portRange, repo);
|
|
34016
34071
|
if (portRangeCheck) checks.push(portRangeCheck);
|
|
34017
34072
|
checks.push(...deployRowChecks(
|
|
@@ -34022,6 +34077,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
34022
34077
|
deps.deployFactsRead === true,
|
|
34023
34078
|
deps.projectMeta?.suite
|
|
34024
34079
|
));
|
|
34080
|
+
checks.push(...firstDevDeployCheck(repo, deps.deployFacts, deps.deployFactsRead === true));
|
|
34025
34081
|
const readme = await contentText(deps, repo, baseBranch, "README.md");
|
|
34026
34082
|
checks.push({
|
|
34027
34083
|
ok: readme !== null && readme.includes("## Agent context"),
|
|
@@ -34460,6 +34516,13 @@ function registerBootstrapCommands(program3) {
|
|
|
34460
34516
|
return { stage, error: e.message };
|
|
34461
34517
|
}
|
|
34462
34518
|
})),
|
|
34519
|
+
edgeDns: await Promise.all(["dev", "rc", "main"].filter((stage) => deployFacts?.stages?.[stage]?.present).map(async (stage) => {
|
|
34520
|
+
try {
|
|
34521
|
+
return { stage, status: await edgeDnsForStage(slug, stage, reg) };
|
|
34522
|
+
} catch (e) {
|
|
34523
|
+
return { stage, error: e.message };
|
|
34524
|
+
}
|
|
34525
|
+
})),
|
|
34463
34526
|
pushAllowlistOwners: verifyOwners,
|
|
34464
34527
|
pushAllowlistOwnersRead: verifyOwnersRead,
|
|
34465
34528
|
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs28.existsSync)(path2) ? (0, import_node_fs28.readFileSync)(path2, "utf8") : null,
|
|
@@ -34857,7 +34920,7 @@ function registerBootstrapCommands(program3) {
|
|
|
34857
34920
|
github,
|
|
34858
34921
|
deployFacts: controlDeployFacts,
|
|
34859
34922
|
deployFactsRead: controlDeployFacts !== null,
|
|
34860
|
-
plannedMainDomain: registryMeta?.edgeDomains?.main ?? inferEdgeDomainsForWrite(slug).main
|
|
34923
|
+
plannedMainDomain: registryMeta?.edgeDomains?.main ?? inferEdgeDomainsForWrite(slug, suiteSlug || vars.SUITE).main
|
|
34861
34924
|
});
|
|
34862
34925
|
if (o.box) {
|
|
34863
34926
|
if (applyDeployModel !== "tenant-container" && applyDeployModel !== "solo-container")
|
|
@@ -34878,7 +34941,7 @@ function registerBootstrapCommands(program3) {
|
|
|
34878
34941
|
host: found.box.address,
|
|
34879
34942
|
box: found.box.name,
|
|
34880
34943
|
owners: scan.rows,
|
|
34881
|
-
domains: { ...inferEdgeDomainsForWrite(slug), ...domains, ...boxDomains },
|
|
34944
|
+
domains: { ...inferEdgeDomainsForWrite(slug, suiteSlug || vars.SUITE), ...domains, ...boxDomains },
|
|
34882
34945
|
fileless: applyDeployModel === "tenant-container"
|
|
34883
34946
|
});
|
|
34884
34947
|
controlRows = planned.rows;
|
|
@@ -35309,6 +35372,42 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
|
|
|
35309
35372
|
ddbWrites.push({ slug, action: `deploy-${coord.stage}`, record: payload });
|
|
35310
35373
|
applied.push(`DEPLOY#${coord.stage} ${coord.host}:${coord.port}${coord.domain ? ` ${coord.domain}` : ""}`);
|
|
35311
35374
|
}
|
|
35375
|
+
for (const coord of boxDeployPlan) {
|
|
35376
|
+
try {
|
|
35377
|
+
const dns = await edgeDnsForStage(slug, coord.stage, registryClientDeps(await loadConfig()), true);
|
|
35378
|
+
const bad = (dns.hosts ?? []).filter((h) => h.status === "conflict");
|
|
35379
|
+
applied.push(`edge dns ${coord.stage}: ${dns.required ? (dns.hosts ?? []).map((h) => `${h.host} ${h.status}`).join(", ") : "not required"}`);
|
|
35380
|
+
for (const h of bad) deferredFailures.push(`edge dns ${coord.stage} ${h.host}: ${h.hint}`);
|
|
35381
|
+
} catch (e) {
|
|
35382
|
+
applied.push(`edge dns ${coord.stage} (failed: ${e.message})`);
|
|
35383
|
+
deferredFailures.push(`edge dns ${coord.stage} not ensured: ${e.message}; re-run: mmi-cli devops runtime edge dns ensure ${repo} ${coord.stage}`);
|
|
35384
|
+
}
|
|
35385
|
+
}
|
|
35386
|
+
let seedPrs = [];
|
|
35387
|
+
if (seedPlan.mode === "pr" && seedPlan.branch) {
|
|
35388
|
+
try {
|
|
35389
|
+
seedPrs = JSON.parse((await gh2(["pr", "list", "--repo", repo, "--head", seedPlan.branch, "--base", baseBranch, "--state", "all", "--json", "number,url,state,mergedAt"])).stdout || "[]");
|
|
35390
|
+
} catch {
|
|
35391
|
+
seedPrs = [];
|
|
35392
|
+
}
|
|
35393
|
+
}
|
|
35394
|
+
const firstDeploy = decideFirstDevDeploy({ devRowWritten: boxDeployPlan.some((c) => c.stage === "dev"), seedMode: seedPlan.mode, seedPrs });
|
|
35395
|
+
if (firstDeploy === "defer") {
|
|
35396
|
+
applied.push("first dev deploy: deferred \u2014 the seed PR is not merged; the Hub dev auto-deploy runs on its merge (DEPLOY#dev now exists)");
|
|
35397
|
+
} else if (firstDeploy === "dispatch") {
|
|
35398
|
+
try {
|
|
35399
|
+
const out = await execFileP(process.execPath, [process.argv[1], ...canonicalArgvFor(["runtime", "tenant", "redeploy", repo, "dev", "--json"])], { timeout: 12e4 });
|
|
35400
|
+
let run = {};
|
|
35401
|
+
try {
|
|
35402
|
+
run = JSON.parse(out.stdout);
|
|
35403
|
+
} catch {
|
|
35404
|
+
}
|
|
35405
|
+
applied.push(`first dev deploy dispatched at development: ${run.runUrl ?? run.dispatch ?? "run not yet correlated"}`);
|
|
35406
|
+
} catch (e) {
|
|
35407
|
+
applied.push(`first dev deploy (failed: ${e.message.split("\n")[0]})`);
|
|
35408
|
+
deferredFailures.push(`first dev deploy not dispatched; run: mmi-cli devops runtime tenant redeploy ${repo} dev --watch`);
|
|
35409
|
+
}
|
|
35410
|
+
}
|
|
35312
35411
|
controlRows = controlRows.map((row) => {
|
|
35313
35412
|
const coord = boxDeployPlan.find((p) => row.target === `DEPLOY#${p.stage}`);
|
|
35314
35413
|
return coord ? {
|
|
@@ -53143,6 +53242,17 @@ function registerEdgeCommands(program3) {
|
|
|
53143
53242
|
process.exitCode = 1;
|
|
53144
53243
|
}
|
|
53145
53244
|
});
|
|
53245
|
+
edge.command("dns <action> <owner/repo> <stage>").description("read (`status`) or create-when-missing (`ensure`, master-only) the proxied Cloudflare A records for a registered DEPLOY# row; existing records are never modified").action(async (action, repo, stage) => {
|
|
53246
|
+
if (action !== "status" && action !== "ensure") return fail("runtime edge dns: <action> must be status or ensure");
|
|
53247
|
+
if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime edge dns: <stage> must be dev, rc, or main");
|
|
53248
|
+
try {
|
|
53249
|
+
const result = await edgeDnsForStage(slugOf(repo), stage, registryClientDeps(await loadConfig()), action === "ensure");
|
|
53250
|
+
console.log(JSON.stringify(result, null, 2));
|
|
53251
|
+
if (result.required && result.complete === false) process.exitCode = 1;
|
|
53252
|
+
} catch (e) {
|
|
53253
|
+
fail(`runtime edge dns: ${e.message}`);
|
|
53254
|
+
}
|
|
53255
|
+
});
|
|
53146
53256
|
}
|
|
53147
53257
|
|
|
53148
53258
|
// src/command-register-developer.ts
|
package/cli/package.json
CHANGED
package/package.json
CHANGED
|
@@ -481,6 +481,9 @@ web tenants, `--project-type desktop-game --deploy-model none --clear-web-profil
|
|
|
481
481
|
`--project-type mobile-app --deploy-model none --clear-web-profile` for a phone app distributed through the
|
|
482
482
|
app stores, and
|
|
483
483
|
`--class content --project-type content --deploy-model content --clear-web-profile` for a content/KB repo.
|
|
484
|
+
An **MM-Work module** adds `--var SUITE=mm-work` (#7944): it registers `suite: mm-work` and derives its
|
|
485
|
+
own `dev.<sub>.mutmut.work` + `<sub>.mutmut.work` hosts, with no rc host (the suite serves its rc from the
|
|
486
|
+
live host). Without it, apply derives the `*.mutatismutandis.co` web-app hosts, which a module never uses.
|
|
484
487
|
Run the apply path with the board variables discovered above, or register the same values with
|
|
485
488
|
`mmi-cli oracle org project set` from the Hub or from the target project checkout:
|
|
486
489
|
|
|
@@ -4,9 +4,13 @@ MMI owns the central project registry and provisioning workflows. Read
|
|
|
4
4
|
`mmi-cli oracle org project get <owner/repo> --json` before changing hostnames or login settings.
|
|
5
5
|
Use the declared edge domains for each stage; do not guess hostnames from a slug or create a
|
|
6
6
|
second DNS/deployment control plane in a product repository. New project setup goes through
|
|
7
|
-
[bootstrap](../../bootstrap/SKILL.md).
|
|
7
|
+
[bootstrap](../../bootstrap/SKILL.md). An MM-Work suite module serves from its own
|
|
8
|
+
`dev.<name>.mutmut.work` and `<name>.mutmut.work` hosts, with no rc host; bootstrap declares them
|
|
9
|
+
with `--var SUITE=mm-work`.
|
|
8
10
|
|
|
9
11
|
Cloudflare owns public DNS and edge TLS. Preserve unrelated records during an authorized change.
|
|
12
|
+
The Hub creates each registered host's proxied record during bootstrap and never edits an existing
|
|
13
|
+
one; check or re-run it with `mmi-cli devops runtime edge dns status|ensure <owner/repo> <stage>`.
|
|
10
14
|
A dev stage is private only after its independent edge-block check succeeds. A configured record
|
|
11
15
|
or successful deployment alone does not prove access control. Use [stage](../../stage/SKILL.md)
|
|
12
16
|
for the supported private live workflow.
|