@mutmutco/cli 4.5.101 → 4.5.103

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.
Files changed (2) hide show
  1. package/dist/main.cjs +152 -25
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -6053,6 +6053,16 @@ function isRetryableGitHubWaitReadError(e) {
6053
6053
  }
6054
6054
  return /Something went wrong while executing your query/.test(text) || /^\s*unexpected end of JSON input\s*$/m.test(text);
6055
6055
  }
6056
+ function refusalWaitMs(e, nowMs) {
6057
+ const err = e;
6058
+ const text = [err?.stdout, err?.stderr, e instanceof Error ? e.message : ""].filter((t) => typeof t === "string").join("\n");
6059
+ const header = (name) => new RegExp(`^${name}:\\s*(\\d+)\\s*$`, "im").exec(text)?.[1];
6060
+ const retryAfter = header("retry-after");
6061
+ if (retryAfter !== void 0) return Number(retryAfter) * 1e3;
6062
+ const reset = header("x-ratelimit-remaining") === "0" ? Number(header("x-ratelimit-reset")) : typeof err?.rateLimitReset === "number" ? err.rateLimitReset : NaN;
6063
+ if (reset * 1e3 > nowMs) return reset * 1e3 - nowMs;
6064
+ return /secondary rate|abuse detection/i.test(text) ? 6e4 : void 0;
6065
+ }
6056
6066
  function waitReadFailureMessage(e) {
6057
6067
  const err = e;
6058
6068
  const stderr = typeof err?.stderr === "string" ? err.stderr.trim() : "";
@@ -6180,9 +6190,21 @@ async function waitForPrChecks(deps) {
6180
6190
  return { policy, status: "failure", reason: waitReadFailureMessage(e), detail: "github-read-failed", waitedMs: now() - started };
6181
6191
  }
6182
6192
  lastDetail = `github-read-transient (${waitReadFailureMessage(e)})`;
6183
- deps.log?.(`transient GitHub read failure \u2014 retrying within the wait budget: ${lastDetail}`);
6184
- report("pending");
6185
- await deps.sleep(PR_CHECKS_POLL_MS);
6193
+ const askedMs = refusalWaitMs(e, now());
6194
+ const jitter = deps.jitterMs ?? (() => Math.floor(Math.random() * PR_CHECKS_RATELIMIT_JITTER_MAX_MS));
6195
+ const waitMs = askedMs !== void 0 && askedMs > PR_CHECKS_POLL_MS ? askedMs + jitter() : PR_CHECKS_POLL_MS;
6196
+ if (askedMs !== void 0 && now() + waitMs >= deadline) {
6197
+ return {
6198
+ policy,
6199
+ status: "rate-limited",
6200
+ reason: `GitHub refused the read for rate limit and asked for ${Math.round(askedMs / 1e3)}s, past the wait deadline (${lastDetail})`,
6201
+ detail: "rate-limited",
6202
+ waitedMs: now() - started
6203
+ };
6204
+ }
6205
+ deps.log?.(`transient GitHub read failure \u2014 retrying in ${Math.round(waitMs / 1e3)}s within the wait budget: ${lastDetail}`);
6206
+ report(askedMs !== void 0 ? "rate-limited" : "pending");
6207
+ await deps.sleep(waitMs);
6186
6208
  continue;
6187
6209
  }
6188
6210
  report(state);
@@ -6300,6 +6322,7 @@ function setPollTokenProvider(provider) {
6300
6322
  pollTokenOnce = void 0;
6301
6323
  resetAppPollGuard();
6302
6324
  lastPollSource = void 0;
6325
+ observedPool.clear();
6303
6326
  }
6304
6327
  async function pollToken() {
6305
6328
  if (!pollTokenProvider) return void 0;
@@ -6327,12 +6350,43 @@ function parseRateLimitHeaders(out) {
6327
6350
  };
6328
6351
  return { remaining: first("remaining"), limit: first("limit"), resetSec: first("reset") };
6329
6352
  }
6353
+ function describeRefusal(out) {
6354
+ const header = (name) => new RegExp(`^${name}:\\s*(.+?)\\s*$`, "im").exec(out)?.[1];
6355
+ let message2 = "";
6356
+ const body = out.slice(out.search(/\r?\n\r?\n/) + 1).trim();
6357
+ try {
6358
+ message2 = String(JSON.parse(body).message ?? "");
6359
+ } catch {
6360
+ message2 = body;
6361
+ }
6362
+ const parts = [
6363
+ ["resource", header("x-ratelimit-resource")],
6364
+ ["remaining", header("x-ratelimit-remaining")],
6365
+ ["reset", header("x-ratelimit-reset")],
6366
+ ["retry-after", header("retry-after")]
6367
+ ].filter(([, v]) => v !== void 0).map(([k, v]) => `${k}=${v}`);
6368
+ const text = message2.replace(/\s+/g, " ").slice(0, 160);
6369
+ return [parts.join(" "), text ? `"${text}"` : ""].filter(Boolean).join(" ") || "no rate-limit headers or message";
6370
+ }
6330
6371
  var etagCache = /* @__PURE__ */ new Map();
6331
6372
  function appPoolDetail() {
6332
6373
  const guard = appPollGuardSnapshot();
6333
6374
  const counts = guard.remaining !== void 0 && guard.limit !== void 0 ? `App pool at ${guard.remaining}/${guard.limit}` : "App pool headroom unverified";
6334
6375
  return guard.resetAtMs !== void 0 ? `${counts}, resets ${new Date(guard.resetAtMs).toISOString()}` : counts;
6335
6376
  }
6377
+ var observedPool = /* @__PURE__ */ new Map();
6378
+ var OBSERVED_POOL_MAX_AGE_MS = 3 * 6e4;
6379
+ function notePoolHeaders(source, out, nowMs) {
6380
+ const { remaining, resetSec } = parseRateLimitHeaders(out);
6381
+ const resource = /^x-ratelimit-resource:\s*(\S+)\s*$/im.exec(out)?.[1];
6382
+ if (remaining !== void 0 && resetSec !== void 0 && (resource ?? "core") === "core") {
6383
+ observedPool.set(source, { remaining, reset: resetSec, atMs: nowMs });
6384
+ }
6385
+ }
6386
+ function observedCorePool(source, nowMs) {
6387
+ const seen = source ? observedPool.get(source) : void 0;
6388
+ return seen && nowMs - seen.atMs <= OBSERVED_POOL_MAX_AGE_MS && seen.reset * 1e3 > nowMs ? { remaining: seen.remaining, reset: seen.reset } : void 0;
6389
+ }
6336
6390
  async function defaultGhApi(args) {
6337
6391
  const appToken = await pollToken();
6338
6392
  let source = appToken && appPollAllowsApp(Date.now()) ? "app" : "user";
@@ -6354,13 +6408,14 @@ async function defaultGhApi(args) {
6354
6408
  const out = e.stdout;
6355
6409
  const errOut = typeof out === "string" ? out : void 0;
6356
6410
  const status = errOut !== void 0 ? parseIncludedOutput(errOut).status : void 0;
6411
+ if (errOut !== void 0) notePoolHeaders(source, errOut, Date.now());
6357
6412
  if (cached && status === 304) {
6358
6413
  lastPollSource = source;
6359
6414
  return cached.body;
6360
6415
  }
6361
6416
  if (source === "app" && appToken && (status === 403 || status === 429)) {
6362
6417
  noteAppPollResponse(parseRateLimitHeaders(errOut), true, Date.now());
6363
- console.warn(`poll credential: App pool refused the read (HTTP ${status}, ${appPoolDetail()}) \u2014 REST poll reads switch to the ambient user token until the App pool resets (#7843)`);
6418
+ console.warn(`poll credential: App pool refused the read (HTTP ${status}, ${appPoolDetail()}; response: ${describeRefusal(errOut)}) \u2014 REST poll reads switch to the ambient user token until the App pool resets (#7843)`);
6364
6419
  lastPollSource = "user";
6365
6420
  source = "user";
6366
6421
  continue;
@@ -6368,6 +6423,7 @@ async function defaultGhApi(args) {
6368
6423
  throw e;
6369
6424
  }
6370
6425
  const { body, etag, pages } = parseIncludedOutput(stdout);
6426
+ notePoolHeaders(source, stdout, Date.now());
6371
6427
  if (source === "app") noteAppPollResponse(parseRateLimitHeaders(stdout), false, Date.now());
6372
6428
  lastPollSource = source;
6373
6429
  if (etag && pages === 1) etagCache.set(key, { etag, body });
@@ -6840,7 +6896,9 @@ async function fetchRequiredCheckContexts(repo, branch, gh2 = defaultGhApi) {
6840
6896
  }
6841
6897
  return contexts;
6842
6898
  }
6843
- async function fetchRestCorePool(gh2 = defaultGhApi) {
6899
+ async function fetchRestCorePool(gh2 = defaultGhApi, nowMs = Date.now()) {
6900
+ const seen = gh2 === defaultGhApi ? observedCorePool(await pollToken() && appPollAllowsApp(nowMs) ? "app" : "user", nowMs) : void 0;
6901
+ if (seen) return { state: "ok", ...seen };
6844
6902
  let core;
6845
6903
  try {
6846
6904
  core = JSON.parse(await gh2(["rate_limit"])).resources?.core;
@@ -16108,6 +16166,39 @@ function gateConfigToVars(gate) {
16108
16166
  if (extras.steps?.length) out.GATE_EXTRA_STEPS_YAML = gateExtraStepsYaml(extras.steps);
16109
16167
  return out;
16110
16168
  }
16169
+ function suiteModuleSeeds(seeds, suite, deployModel) {
16170
+ if (suite !== "mm-work" || deployModel !== "tenant-container") return seeds;
16171
+ const replacements = {
16172
+ Dockerfile: "suite-Dockerfile.template",
16173
+ "docker-compose.yml": "suite-compose.template.yml",
16174
+ ".github/workflows/gate.yml": "suite-gate.template.yml"
16175
+ };
16176
+ const additions = {
16177
+ "src/index.ts": "suite-server.template.ts",
16178
+ "package.json": "suite-package.template.json",
16179
+ "package-lock.json": "suite-package-lock.template.json",
16180
+ "tsconfig.json": "suite-tsconfig.template.json",
16181
+ "module/package.json": "suite-module-package.template.json",
16182
+ "module/package-lock.json": "suite-module-package-lock.template.json",
16183
+ "module/.npmrc": "suite-npmrc.template",
16184
+ "module/src/module.jsx": "suite-module.template.jsx",
16185
+ "module/vite.config.mjs": "suite-vite.template.mjs",
16186
+ "module/screens/screens.json": "suite-screens.template.json",
16187
+ "module/screens/files.json": "suite-files.template.json",
16188
+ ".github/workflows/screens.yml": "suite-screens.template.yml",
16189
+ "docs/Guides/mm-work-module.md": "suite-guide.template.md"
16190
+ };
16191
+ return [
16192
+ ...seeds.filter((s) => s.target !== "docs/Guides/google-login.md").map((s) => replacements[s.target] ? { ...s, source: `seed:${replacements[s.target]}` } : s),
16193
+ ...Object.entries(additions).map(([target, source]) => ({
16194
+ target,
16195
+ source: `seed:${source}`,
16196
+ ownership: "repo",
16197
+ classes: ["deployable"],
16198
+ deployModels: ["tenant-container"]
16199
+ }))
16200
+ ];
16201
+ }
16111
16202
  function seedMatchesDeployModel(seed, deployModel) {
16112
16203
  if (!seed.deployModels?.length) return true;
16113
16204
  return deployModel != null && seed.deployModels.includes(deployModel);
@@ -16289,7 +16380,11 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
16289
16380
  // Identity. name/division default off the repo name when the skill didn't pass them.
16290
16381
  name: vars.NAME || parsedRepo.name,
16291
16382
  division: vars.DIVISION || parsedRepo.name.split("-")[0] || void 0,
16292
- suite: vars.SUITE || void 0,
16383
+ suite,
16384
+ ...suite === "mm-work" && deployModel === "tenant-container" ? {
16385
+ requiredBuildSecrets: ["NODE_AUTH_TOKEN=@github-packages-token"],
16386
+ requiredChecks: ["gate", "screens / screens"]
16387
+ } : {},
16293
16388
  repos: [`${parsedRepo.owner}/${slug}`],
16294
16389
  branch: vars.BRANCH || (cls === "content" ? "main" : "development"),
16295
16390
  class: cls,
@@ -17604,10 +17699,10 @@ var rollout_plan_default = {
17604
17699
  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)."
17605
17700
  },
17606
17701
  baseline: {
17607
- version: "4.5.101",
17608
- tag: "v4.5.101",
17609
- commit: "a3c82f0f7aa3",
17610
- npm: "@mutmutco/cli@4.5.101"
17702
+ version: "4.5.103",
17703
+ tag: "v4.5.103",
17704
+ commit: "c2dd99cf0d72",
17705
+ npm: "@mutmutco/cli@4.5.103"
17611
17706
  },
17612
17707
  exitCriterion: "fleet-n-of-n",
17613
17708
  hubOnlyShortcut: "forbidden",
@@ -17624,14 +17719,14 @@ var rollout_plan_default = {
17624
17719
  repo: "mutmutco/mmi-hub",
17625
17720
  role: "canary",
17626
17721
  schedule: "train",
17627
- v3Target: "v4.5.101"
17722
+ v3Target: "v4.5.103"
17628
17723
  }
17629
17724
  ],
17630
17725
  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.",
17631
17726
  rollback: {
17632
17727
  independent: true,
17633
- mechanism: "npm dist-tag latest -> 4.5.101 and redeploy the Hub Lambda from tag v4.5.101 (a3c82f0f7aa3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17634
- v3Target: "v4.5.101 (@mutmutco/cli@4.5.101, tag commit a3c82f0f7aa3 \u2014 last known-good release carrying the repo-index v4-only contract)"
17728
+ mechanism: "npm dist-tag latest -> 4.5.103 and redeploy the Hub Lambda from tag v4.5.103 (c2dd99cf0d72); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17729
+ v3Target: "v4.5.103 (@mutmutco/cli@4.5.103, tag commit c2dd99cf0d72 \u2014 last known-good release carrying the repo-index v4-only contract)"
17635
17730
  }
17636
17731
  },
17637
17732
  {
@@ -33919,14 +34014,15 @@ function deployRowChecks(repo, deployModel, releaseTrack, facts, factsRead, suit
33919
34014
  };
33920
34015
  });
33921
34016
  }
33922
- function edgeCertificateChecks(results) {
34017
+ function edgeCertificateChecks(results, repo) {
33923
34018
  return results.flatMap((result) => {
33924
34019
  if (!result) return [];
33925
34020
  const { stage, status, error } = result;
34021
+ const fix = status?.missing && !status.pending ? ` \u2014 run: mmi-cli devops runtime edge cert ensure ${repo} ${stage}` : "";
33926
34022
  return [{
33927
34023
  ok: !error && (status?.required === false || status?.required === true && status.missing === false),
33928
34024
  label: `Cloudflare edge certificate: ${stage}`,
33929
- detail: error ? `certificate coverage unreadable (${error}); cannot verify TLS` : status?.hint
34025
+ detail: error ? `certificate coverage unreadable (${error}); cannot verify TLS` : status?.hint && `${status.hint}${fix}`
33930
34026
  }];
33931
34027
  });
33932
34028
  }
@@ -33990,6 +34086,8 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
33990
34086
  detail: exactAllowlist ? void 0 : ownersUnverified ? "org owner baseline is unreadable or empty; the exact restriction is unverified" : deps.pushAllowlistOwnersRead === true ? "restriction must contain exactly the org owners + mmi-github-app and no teams" : "restrictions.users is empty or unset"
33991
34087
  });
33992
34088
  }
34089
+ const packageAccess = suiteModulePackageAccessCheck(deps.projectMeta?.suite, deps.deployModel);
34090
+ if (packageAccess) checks.push(packageAccess);
33993
34091
  let environmentDrift2 = [];
33994
34092
  try {
33995
34093
  environmentDrift2 = await verifyRepoEnvironments(repo, effectiveTrack, deps.client, deps.projectMeta?.suite);
@@ -34073,7 +34171,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
34073
34171
  const check = npmrcGitHubPackagesAuthCheck(path2, await contentText(deps, repo, baseBranch, path2));
34074
34172
  if (check) checks.push(check);
34075
34173
  }
34076
- checks.push(...edgeCertificateChecks(deps.edgeCertificates ?? []));
34174
+ checks.push(...edgeCertificateChecks(deps.edgeCertificates ?? [], repo));
34077
34175
  checks.push(...edgeDnsChecks(deps.edgeDns ?? [], repo));
34078
34176
  const portRangeCheck = centralContainerPortRangeCheck(deps.deployModel, deps.projectMeta?.portRange, repo);
34079
34177
  if (portRangeCheck) checks.push(portRangeCheck);
@@ -34331,6 +34429,14 @@ function laneNotEvaluatedFromThisBranch(doctor) {
34331
34429
  const only = reasons.length === 1 ? reasons[0] : void 0;
34332
34430
  return only?.code === "branch-mismatch" ? only.title : void 0;
34333
34431
  }
34432
+ function suiteModulePackageAccessCheck(suite, deployModel) {
34433
+ if (suite !== "mm-work" || deployModel !== "tenant-container") return void 0;
34434
+ return {
34435
+ ok: true,
34436
+ label: "mmwork-ds package access (manual org-admin step; unverified)",
34437
+ detail: "An org admin must add this repository with Read under @mutmutco/mmwork-ds \u2192 Package settings \u2192 Manage Actions access. No GitHub REST API can verify or grant this; without it CI fails with 403 read_package."
34438
+ };
34439
+ }
34334
34440
  function attachTrainDoctorVerdicts(report, trainDoctors) {
34335
34441
  const marked = trainDoctors.map((doctor) => {
34336
34442
  const notEvaluatedFromThisBranch = laneNotEvaluatedFromThisBranch(doctor);
@@ -34819,13 +34925,6 @@ function registerBootstrapCommands(program3) {
34819
34925
  const baseBranch = o.class === "content" ? "main" : "development";
34820
34926
  const slug = parsedRepo.slug;
34821
34927
  const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
34822
- const seedsToApply = onlyTarget ? manifest.seeds.filter((s) => s.target === onlyTarget || s.target.replace("{{REPO_SLUG}}", slug) === onlyTarget) : manifest.seeds;
34823
- if (onlyTarget && !seedsToApply.length) {
34824
- const known = manifest.seeds.map((s) => s.target).join("\n ");
34825
- return fail(`bootstrap apply: --only '${onlyTarget}' names no seed in ${manifestPath}. Declared targets:
34826
- ${known}`);
34827
- }
34828
- const onlyManagedBlock = onlyTarget ? seedsToApply[0]?.managedBlock != null : false;
34829
34928
  const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
34830
34929
  const readFile10 = (p) => (0, import_node_fs28.existsSync)(p) ? (0, import_node_fs28.readFileSync)(p, "utf8") : null;
34831
34930
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
@@ -34895,6 +34994,20 @@ function registerBootstrapCommands(program3) {
34895
34994
  applyProjectType = o.projectType || registryMeta?.projectType || payload.projectType;
34896
34995
  } catch {
34897
34996
  }
34997
+ const availableSeeds = suiteModuleSeeds(manifest.seeds, vars.SUITE || registryMeta?.suite, applyDeployModel);
34998
+ const seedsToApply = onlyTarget ? availableSeeds.filter((s) => s.target === onlyTarget || s.target.replace("{{REPO_SLUG}}", slug) === onlyTarget) : availableSeeds;
34999
+ if (onlyTarget && !seedsToApply.length) {
35000
+ return fail(`bootstrap apply: --only '${onlyTarget}' names no seed for this project. Declared targets:
35001
+ ${availableSeeds.map((s) => s.target).join("\n ")}`);
35002
+ }
35003
+ const onlyManagedBlock = onlyTarget ? seedsToApply[0]?.managedBlock != null : false;
35004
+ if (vars.SUITE === "mm-work" || registryMeta?.suite === "mm-work") {
35005
+ if (applyDeployModel === "tenant-container") {
35006
+ const moduleGate = withDerivedRepoVars({}, parsedRepo, o.class, effectiveTrack, registryRequiredCheckBranches, ["gate", "screens / screens"]);
35007
+ vars.GATE_RULESET_CONTEXTS_JSON = moduleGate.GATE_RULESET_CONTEXTS_JSON;
35008
+ if (registryMeta) registryMeta = { ...registryMeta, requiredChecks: ["gate", "screens / screens"] };
35009
+ }
35010
+ }
34898
35011
  const controlClient = defaultGitHubClient();
34899
35012
  let controlOwners = [];
34900
35013
  let controlDeployFacts = null;
@@ -46588,8 +46701,11 @@ function trainApplyDeps() {
46588
46701
  // Hub-App-authority dispatch of the central tenant-control.yml (#1717) — the Hub fires the
46589
46702
  // workflow_dispatch with its App token. Never throws for an expected rejection: it returns the dispatch
46590
46703
  // outcome so runTenantControl can map a 5xx (transport-failed, retryable) vs a 4xx (rejected) vs ok.
46591
- dispatchTenantControl: async ({ repo, stage, action, lines: lines2, since, until, task, artifact, confirmDataLoss }) => {
46592
- const res = await tenantControl({ repo, stage, action, ...lines2 != null ? { lines: lines2 } : {}, ...since ? { since } : {}, ...until ? { until } : {}, ...task ? { task } : {}, ...artifact ? { artifact } : {}, ...confirmDataLoss ? { confirmDataLoss } : {} }, registryClientDeps(await loadConfig()));
46704
+ // #7961: forward every SET field rather than re-listing them, so a new option (e.g. `edge`) cannot be
46705
+ // dropped silently. Unset/false fields are omitted: the route rejects e.g. `edge` on a non-logs action.
46706
+ dispatchTenantControl: async (input) => {
46707
+ const payload = Object.fromEntries(Object.entries(input).filter(([, v]) => v != null && v !== "" && v !== false));
46708
+ const res = await tenantControl(payload, registryClientDeps(await loadConfig()));
46593
46709
  if (res.ok) return { ok: true };
46594
46710
  const body = res.body;
46595
46711
  return { ok: false, category: body?.category, error: body?.error ?? res.error };
@@ -53271,6 +53387,17 @@ function registerEdgeCommands(program3) {
53271
53387
  fail(`runtime edge dns: ${e.message}`);
53272
53388
  }
53273
53389
  });
53390
+ edge.command("cert <action> <owner/repo> <stage>").description("read (`status`) or order-when-missing (`ensure`, master-only) the Cloudflare advanced edge certificate pack (<x> + *.<x>) a dev/rc host needs").action(async (action, repo, stage) => {
53391
+ if (action !== "status" && action !== "ensure") return fail("runtime edge cert: <action> must be status or ensure");
53392
+ if (stage !== "dev" && stage !== "rc") return fail("runtime edge cert: <stage> must be dev or rc (main is covered by the zone certificate)");
53393
+ try {
53394
+ const result = await edgeCertificateForStage(slugOf(repo), stage, registryClientDeps(await loadConfig()), action === "ensure");
53395
+ console.log(JSON.stringify(result, null, 2));
53396
+ if (result.required && result.missing) process.exitCode = 1;
53397
+ } catch (e) {
53398
+ fail(`runtime edge cert: ${e.message}`);
53399
+ }
53400
+ });
53274
53401
  }
53275
53402
 
53276
53403
  // src/command-register-developer.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.101",
3
+ "version": "4.5.103",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",