@mutmutco/codex-plugin 4.6.45 → 4.6.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/cli/dist/main.cjs +55 -16
- package/cli/package.json +1 -1
- package/package.json +1 -1
package/cli/dist/main.cjs
CHANGED
|
@@ -28556,10 +28556,10 @@ var rollout_plan_default = {
|
|
|
28556
28556
|
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)."
|
|
28557
28557
|
},
|
|
28558
28558
|
baseline: {
|
|
28559
|
-
version: "4.6.
|
|
28560
|
-
tag: "v4.6.
|
|
28561
|
-
commit: "
|
|
28562
|
-
npm: "@mutmutco/cli@4.6.
|
|
28559
|
+
version: "4.6.47",
|
|
28560
|
+
tag: "v4.6.47",
|
|
28561
|
+
commit: "4555234d7aa8",
|
|
28562
|
+
npm: "@mutmutco/cli@4.6.47"
|
|
28563
28563
|
},
|
|
28564
28564
|
exitCriterion: "fleet-n-of-n",
|
|
28565
28565
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28576,14 +28576,14 @@ var rollout_plan_default = {
|
|
|
28576
28576
|
repo: "mutmutco/mmi-hub",
|
|
28577
28577
|
role: "canary",
|
|
28578
28578
|
schedule: "train",
|
|
28579
|
-
v3Target: "v4.6.
|
|
28579
|
+
v3Target: "v4.6.47"
|
|
28580
28580
|
}
|
|
28581
28581
|
],
|
|
28582
28582
|
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.",
|
|
28583
28583
|
rollback: {
|
|
28584
28584
|
independent: true,
|
|
28585
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28586
|
-
v3Target: "v4.6.
|
|
28585
|
+
mechanism: "npm dist-tag latest -> 4.6.47 and redeploy the Hub Lambda from tag v4.6.47 (4555234d7aa8); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28586
|
+
v3Target: "v4.6.47 (@mutmutco/cli@4.6.47, tag commit 4555234d7aa8 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28587
28587
|
}
|
|
28588
28588
|
},
|
|
28589
28589
|
{
|
|
@@ -42864,7 +42864,11 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42864
42864
|
};
|
|
42865
42865
|
base.checksWait = await deps.waitForChecks(prNumber, repo);
|
|
42866
42866
|
const waitError = await checksWaitError(base.checksWait);
|
|
42867
|
-
if (waitError)
|
|
42867
|
+
if (waitError) {
|
|
42868
|
+
if (!hasQueue || base.checksWait.status === "conflicting") return { ...base, error: waitError };
|
|
42869
|
+
const noQueueEntry = /no-checks-reported/.test(base.checksWait.detail ?? "") ? `no merge-queue entry was created: workflow runs exist on the head but published no check-runs \u2014 inspect them with \`mmi-cli devops read run list --repo ${repo} --commit ${pinned ?? "<full head SHA>"}\`, then re-run \`mmi-cli devops pr land ${prNumber} --repo ${repo}\` once the required check exists` : "no merge-queue entry was created (the land stopped before enqueue)";
|
|
42870
|
+
return { ...base, error: `${waitError} \u2014 ${noQueueEntry}` };
|
|
42871
|
+
}
|
|
42868
42872
|
const driftAfterChecks = await headDrift("after checks");
|
|
42869
42873
|
if (driftAfterChecks) return driftAfterChecks;
|
|
42870
42874
|
prState = prState ?? await readPrState();
|
|
@@ -42883,7 +42887,7 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42883
42887
|
}
|
|
42884
42888
|
base.checksWait = await deps.waitForChecks(prNumber, repo);
|
|
42885
42889
|
const reWaitError = await checksWaitError(base.checksWait);
|
|
42886
|
-
if (reWaitError) return { ...base, error:
|
|
42890
|
+
if (reWaitError) return { ...base, error: reWaitError };
|
|
42887
42891
|
prState = await readPrState();
|
|
42888
42892
|
}
|
|
42889
42893
|
if (isConflicting(prState)) return { ...base, error: conflictError() };
|
|
@@ -44185,8 +44189,8 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
44185
44189
|
error: `queue submission outcome unknown: ${reason}. Never hand-edit files under the mmi-cli queue-admissions receipt store (%LOCALAPPDATA%/MMI Future/mmi-cli/queue-admissions); read the queue entry with \`mmi-cli devops read pr queue ${pr} --repo ${repo} --json\`; once the PR is verifiably OPEN at the submitted head and absent from the queue, abandon the stuck admission with \`mmi-cli devops pr land ${pr} --repo ${repo} --abandon-admission\` so a new intent needs no new head \u2014 no new request was sent after this uncertainty.`
|
|
44186
44190
|
});
|
|
44187
44191
|
const rejected = (reason) => markReenqueued(admissionRejected(reason, repo, pr));
|
|
44188
|
-
const reconcile = async (reason, recordEntry = false, resumingSaved = false) => {
|
|
44189
|
-
const maxReads = options.enqueueOnly ? 3 : 1;
|
|
44192
|
+
const reconcile = async (reason, recordEntry = false, resumingSaved = false, minReads = 1) => {
|
|
44193
|
+
const maxReads = Math.max(options.enqueueOnly ? 3 : 1, minReads);
|
|
44190
44194
|
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
44191
44195
|
for (let readAttempt = 0; readAttempt < maxReads; readAttempt++) {
|
|
44192
44196
|
if (readAttempt) await sleep3(readAttempt * 1e3);
|
|
@@ -44317,7 +44321,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
|
|
|
44317
44321
|
try {
|
|
44318
44322
|
response = admissionResponse(output);
|
|
44319
44323
|
} catch {
|
|
44320
|
-
return reconcile("the send response was lost or malformed");
|
|
44324
|
+
return reconcile("the send response was lost or malformed, and queue membership at the submitted head was re-read and not confirmed", true, false, 3);
|
|
44321
44325
|
}
|
|
44322
44326
|
const { code, body } = response;
|
|
44323
44327
|
if (code === 200 && ["enqueued", "merged"].includes(body.status ?? "")) {
|
|
@@ -48241,7 +48245,11 @@ function tenantTaskWriteRefusal(existing, patch) {
|
|
|
48241
48245
|
const sent = patch.tenantTasks;
|
|
48242
48246
|
if (sent === null || sent === void 0) return null;
|
|
48243
48247
|
const merged = /* @__PURE__ */ new Set([...Object.keys(stored), ...Object.keys(sent)]);
|
|
48244
|
-
if (merged.size > 20)
|
|
48248
|
+
if (merged.size > 20) {
|
|
48249
|
+
const repo = existing?.repos?.[0] || "<owner/repo>";
|
|
48250
|
+
const names = Object.keys(stored).sort();
|
|
48251
|
+
return `org project set: ${slug} would hold ${merged.size} tenantTasks (${names.length} declared today); the cap is 20 \u2014 retire one first with \`mmi-cli oracle org project set ${repo} --unset-task <name>\`. Declared: ${names.join(", ") || "(none)"}`;
|
|
48252
|
+
}
|
|
48245
48253
|
return null;
|
|
48246
48254
|
}
|
|
48247
48255
|
function secretRemovalRefusal(existing, patch) {
|
|
@@ -48479,6 +48487,7 @@ var DEPLOY_BUCKET_RE = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
|
|
|
48479
48487
|
var DEPLOY_DISTRIBUTION_ID_RE = /^[A-Z0-9]{5,32}$/;
|
|
48480
48488
|
var COMPOSE_PROFILE_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
48481
48489
|
var MAX_COMPOSE_PROFILES = 8;
|
|
48490
|
+
var READINESS_PATH_RE = /^\/[A-Za-z0-9/._~-]*$/;
|
|
48482
48491
|
function buildSetDeployPatch(_slug, input) {
|
|
48483
48492
|
const clean4 = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
48484
48493
|
const stage = clean4(input.stage);
|
|
@@ -48547,6 +48556,29 @@ function buildSetDeployPatch(_slug, input) {
|
|
|
48547
48556
|
}
|
|
48548
48557
|
composeProfiles = [...seen];
|
|
48549
48558
|
}
|
|
48559
|
+
const healthUrl = clean4(input.healthUrl);
|
|
48560
|
+
if (input.clearHealthUrl && healthUrl !== void 0) {
|
|
48561
|
+
throw new Error("org project set-deploy: --clear-health-url cannot be combined with --health-url (they contradict)");
|
|
48562
|
+
}
|
|
48563
|
+
if (healthUrl !== void 0) {
|
|
48564
|
+
let u;
|
|
48565
|
+
try {
|
|
48566
|
+
u = new URL(healthUrl);
|
|
48567
|
+
} catch {
|
|
48568
|
+
u = void 0;
|
|
48569
|
+
}
|
|
48570
|
+
const loopback = u?.hostname === "127.0.0.1" || u?.hostname === "localhost";
|
|
48571
|
+
if (!DEPLOY_SHELL_SAFE_RE.test(healthUrl) || !u || u.protocol !== "https:" && !(u.protocol === "http:" && loopback)) {
|
|
48572
|
+
throw new Error(`org project set-deploy: --health-url must be a shell-safe https URL (or loopback http), got ${JSON.stringify(input.healthUrl)}`);
|
|
48573
|
+
}
|
|
48574
|
+
}
|
|
48575
|
+
const readinessPath = clean4(input.readinessPath);
|
|
48576
|
+
if (input.clearReadinessPath && readinessPath !== void 0) {
|
|
48577
|
+
throw new Error("org project set-deploy: --clear-readiness-path cannot be combined with --readiness-path (they contradict)");
|
|
48578
|
+
}
|
|
48579
|
+
if (readinessPath !== void 0 && !READINESS_PATH_RE.test(readinessPath)) {
|
|
48580
|
+
throw new Error(`org project set-deploy: --readiness-path must be an absolute path of [A-Za-z0-9/._~-], got ${JSON.stringify(input.readinessPath)}`);
|
|
48581
|
+
}
|
|
48550
48582
|
return {
|
|
48551
48583
|
stage,
|
|
48552
48584
|
...substrate !== void 0 ? { substrate } : {},
|
|
@@ -48567,7 +48599,10 @@ function buildSetDeployPatch(_slug, input) {
|
|
|
48567
48599
|
...input.noEnvFile !== void 0 ? { noEnvFile: input.noEnvFile } : {},
|
|
48568
48600
|
// #7353: an explicit `[]` is what the route reads as "clear them"; omitting the key is what it reads as
|
|
48569
48601
|
// "keep them" — same patch contract as aliases (#2986/#2984).
|
|
48570
|
-
...composeProfiles !== void 0 ? { composeProfiles } : {}
|
|
48602
|
+
...composeProfiles !== void 0 ? { composeProfiles } : {},
|
|
48603
|
+
// #10096: an explicit null is what the route reads as "remove it"; omitting the key keeps it.
|
|
48604
|
+
...input.clearHealthUrl ? { healthUrl: null } : healthUrl !== void 0 ? { healthUrl } : {},
|
|
48605
|
+
...input.clearReadinessPath ? { readinessPath: null } : readinessPath !== void 0 ? { readinessPath } : {}
|
|
48571
48606
|
};
|
|
48572
48607
|
}
|
|
48573
48608
|
var DEPLOY_STAGE_BRANCH = { dev: "development", rc: "rc", main: "main" };
|
|
@@ -75246,7 +75281,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
75246
75281
|
console.log(JSON.stringify(report, null, 2));
|
|
75247
75282
|
if (!report.rcand.canApply) process.exitCode = 1;
|
|
75248
75283
|
});
|
|
75249
|
-
project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row \u2014 project-admin may set only --no-env-file/--compose-profiles on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row \u2014 `mmi-cli devops runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--redirect-from <host>", "permanent redirect host or host=target (primary or alias); repeatable", (host, hosts) => [...hosts, host], []).option("--clear-aliases", "remove EVERY serving alias from the row (#2986) \u2014 omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) \u2014 own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--compose-profiles <profiles>", "comma-separated compose profiles to enable for this stage (e.g. huddles,slack-bridge); omit to leave unchanged").option("--clear-compose-profiles", "remove EVERY compose profile from the row \u2014 omitting --compose-profiles preserves them, so clearing needs saying out loud").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
75284
|
+
project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row \u2014 project-admin may set only --no-env-file/--compose-profiles on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row \u2014 `mmi-cli devops runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--redirect-from <host>", "permanent redirect host or host=target (primary or alias); repeatable", (host, hosts) => [...hosts, host], []).option("--clear-aliases", "remove EVERY serving alias from the row (#2986) \u2014 omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) \u2014 own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--compose-profiles <profiles>", "comma-separated compose profiles to enable for this stage (e.g. huddles,slack-bridge); omit to leave unchanged").option("--clear-compose-profiles", "remove EVERY compose profile from the row \u2014 omitting --compose-profiles preserves them, so clearing needs saying out loud").option("--health-url <url>", "master-only: stage health URL (https, or loopback http) the box health gate probes; omit to leave unchanged").option("--clear-health-url", "master-only: remove the stored health URL from the row").option("--readiness-path <path>", "master-only: readiness path Caddy actively probes (health_uri, e.g. /healthz); omit to leave unchanged").option("--clear-readiness-path", "master-only: remove the stored readiness path from the row").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
75250
75285
|
const cfg = await loadConfig();
|
|
75251
75286
|
let target;
|
|
75252
75287
|
try {
|
|
@@ -75305,7 +75340,11 @@ ${filelessTransitionGuide(target, o.stage)}`
|
|
|
75305
75340
|
clearAliases: o.clearAliases,
|
|
75306
75341
|
noEnvFile,
|
|
75307
75342
|
composeProfiles,
|
|
75308
|
-
clearComposeProfiles: o.clearComposeProfiles
|
|
75343
|
+
clearComposeProfiles: o.clearComposeProfiles,
|
|
75344
|
+
healthUrl: o.healthUrl,
|
|
75345
|
+
clearHealthUrl: o.clearHealthUrl,
|
|
75346
|
+
readinessPath: o.readinessPath,
|
|
75347
|
+
clearReadinessPath: o.clearReadinessPath
|
|
75309
75348
|
});
|
|
75310
75349
|
if (target.includes("/")) body.repo = target;
|
|
75311
75350
|
} catch (e) {
|
package/cli/package.json
CHANGED