@mutmutco/cli 4.3.8 → 4.3.10

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 +75 -30
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -5226,7 +5226,7 @@ function unknownCommandDomainGuide(parentPath, token) {
5226
5226
  }
5227
5227
 
5228
5228
  // src/command-composition.ts
5229
- var import_node_os20 = require("node:os");
5229
+ var import_node_os21 = require("node:os");
5230
5230
  var import_node_path46 = require("node:path");
5231
5231
 
5232
5232
  // src/board-read.ts
@@ -15450,10 +15450,10 @@ var rollout_plan_default = {
15450
15450
  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)."
15451
15451
  },
15452
15452
  baseline: {
15453
- version: "4.3.8",
15454
- tag: "v4.3.8",
15455
- commit: "b109b86b97d1",
15456
- npm: "@mutmutco/cli@4.3.8"
15453
+ version: "4.3.10",
15454
+ tag: "v4.3.10",
15455
+ commit: "6fe2720dc6ef",
15456
+ npm: "@mutmutco/cli@4.3.10"
15457
15457
  },
15458
15458
  exitCriterion: "fleet-n-of-n",
15459
15459
  hubOnlyShortcut: "forbidden",
@@ -15470,14 +15470,14 @@ var rollout_plan_default = {
15470
15470
  repo: "mutmutco/mmi-hub",
15471
15471
  role: "canary",
15472
15472
  schedule: "train",
15473
- v3Target: "v4.3.8"
15473
+ v3Target: "v4.3.10"
15474
15474
  }
15475
15475
  ],
15476
15476
  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.",
15477
15477
  rollback: {
15478
15478
  independent: true,
15479
- mechanism: "npm dist-tag latest -> 4.3.8 and redeploy the Hub Lambda from tag v4.3.8 (b109b86b97d1); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15480
- v3Target: "v4.3.8 (@mutmutco/cli@4.3.8, tag commit b109b86b97d1 \u2014 last known-good release carrying the repo-index v4-only contract)"
15479
+ mechanism: "npm dist-tag latest -> 4.3.10 and redeploy the Hub Lambda from tag v4.3.10 (6fe2720dc6ef); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15480
+ v3Target: "v4.3.10 (@mutmutco/cli@4.3.10, tag commit 6fe2720dc6ef \u2014 last known-good release carrying the repo-index v4-only contract)"
15481
15481
  }
15482
15482
  },
15483
15483
  {
@@ -19012,6 +19012,7 @@ Resume it: ${resumeCommand}
19012
19012
 
19013
19013
  // src/train-doctor.ts
19014
19014
  var import_node_fs23 = require("node:fs");
19015
+ var import_node_os13 = require("node:os");
19015
19016
  var import_node_path22 = require("node:path");
19016
19017
 
19017
19018
  // src/doctor-io.ts
@@ -21119,6 +21120,30 @@ function readLocalWorkflowsDefault(root) {
21119
21120
  }
21120
21121
  return files;
21121
21122
  }
21123
+ var NPM_REGISTRY = "https://registry.npmjs.org";
21124
+ async function probeRegistryWhoami(train) {
21125
+ const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path22.join)((0, import_node_os13.tmpdir)(), "mmi-npmrc-"));
21126
+ const rc = (0, import_node_path22.join)(dir, "npmrc");
21127
+ try {
21128
+ (0, import_node_fs23.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
21129
+ await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
21130
+ return { kind: "ok", detail: "the registry answered a login" };
21131
+ } catch (e) {
21132
+ const err = e;
21133
+ const stderr = typeof err.stderr === "string" ? err.stderr : "";
21134
+ const first = (clean2(stderr) || message(e)).split("\n")[0] ?? "";
21135
+ if (err.code === SECRETS_USE_WRAPPER_EXIT_CODE) return { kind: "unprobed", detail: first };
21136
+ if (/\bE401\b|401 Unauthorized/.test(stderr)) return { kind: "rejected", detail: "npm whoami answered 401 Unauthorized" };
21137
+ return { kind: "unverified", detail: first };
21138
+ } finally {
21139
+ (0, import_node_fs23.rmSync)(dir, { recursive: true, force: true });
21140
+ }
21141
+ }
21142
+ var REGISTRY_TOKEN_ENV = /\b(NODE_AUTH_TOKEN|NPM_TOKEN)\b/;
21143
+ function publishesViaTrustedPublisher(workflows) {
21144
+ if (!workflows?.length) return false;
21145
+ return workflows.some((f) => /\bid-token:\s*write\b/.test(f.body)) && !workflows.some((f) => REGISTRY_TOKEN_ENV.test(f.body));
21146
+ }
21122
21147
  var defaultDeps2 = {
21123
21148
  cliVersion: resolveClientVersion,
21124
21149
  fetchReleasedVersion: fetchNpmReleasedVersion,
@@ -21136,6 +21161,14 @@ var defaultDeps2 = {
21136
21161
  }
21137
21162
  },
21138
21163
  healAlignmentLeg: healStaleAlignmentLeg,
21164
+ readSurfacesRaw: (cwd) => {
21165
+ try {
21166
+ return (0, import_node_fs23.readFileSync)((0, import_node_path22.join)(cwd, "surfaces.json"), "utf8");
21167
+ } catch {
21168
+ return void 0;
21169
+ }
21170
+ },
21171
+ registryWhoami: probeRegistryWhoami,
21139
21172
  env: process.env
21140
21173
  };
21141
21174
  function message(e) {
@@ -21413,6 +21446,18 @@ async function runTrainDoctor(input) {
21413
21446
  add({ code: "npm-major-mismatch", severity: "blocker", source: "local", title: "local npm major differs from the npm CI declares (#5666)", remedy: message(e) });
21414
21447
  }
21415
21448
  }
21449
+ if (/"publishVisibility"\s*:\s*"private"/.test(deps.readSurfacesRaw(cwd) ?? "")) {
21450
+ const probe = await deps.registryWhoami(train);
21451
+ if (probe.kind === "rejected" && publishesViaTrustedPublisher(workflows)) {
21452
+ add({ code: "npm-token-rejected", severity: "info", source: "origin", title: `the vault npm token (_org npm/NPM_TOKEN) is REJECTED by registry.npmjs.org \u2014 ${probe.detail}; no lane of this repo consumes it (its workflows publish through OIDC Trusted Publishing and name no registry token)`, remedy: "nothing on the release path. It affects only a manual `npm view` of a restricted package made through the `_org` hop, which 404s exactly like an absent one until the token is rotated (`mmi-cli vault secrets set npm/NPM_TOKEN --slug _org`). If this repo verifies through a different vault entry, this row does not describe that hop (#6187)" });
21453
+ } else if (probe.kind === "rejected") {
21454
+ add({ code: "npm-token-rejected", severity: "warning", source: "origin", title: `the vault npm token (_org npm/NPM_TOKEN) is REJECTED by registry.npmjs.org \u2014 ${probe.detail}; every authenticated \`npm view\` of a restricted package will 404 exactly like an absent one`, remedy: 'rotate the org token: mint a fresh npm automation token, store it with `mmi-cli vault secrets set npm/NPM_TOKEN --slug _org` (master or an exact grant), and rerun. Until whoami answers a login, a restricted-package 404 proves nothing \u2014 never read it as "not published" (#6184)' });
21455
+ } else if (probe.kind === "unprobed") {
21456
+ add({ code: "npm-token-rejected", severity: "info", source: "origin", title: `the vault npm token was not probed (no _org npm/NPM_TOKEN grant, or a vault error): ${probe.detail}`, remedy: "nothing here \u2014 the release does not depend on it (CI publishes through OIDC Trusted Publishing). Before verifying a private publish by hand, run the whoami probe from an account holding the `_org` npm/NPM_TOKEN grant" });
21457
+ } else if (probe.kind === "unverified") {
21458
+ unverified("the vault npm token (`npm whoami` through the `_org` npm/NPM_TOKEN hop)", probe.detail);
21459
+ }
21460
+ }
21416
21461
  try {
21417
21462
  await train.runSelf(["secrets", "preflight", "--stage", stage, "--repo", repo, ...lane === "hotfix" ? ["--lane", "hotfix"] : []]);
21418
21463
  } catch (e) {
@@ -25576,7 +25621,7 @@ function registerBoardCommands(program3) {
25576
25621
 
25577
25622
  // src/bootstrap-commands.ts
25578
25623
  var import_node_fs26 = require("node:fs");
25579
- var import_node_os13 = require("node:os");
25624
+ var import_node_os14 = require("node:os");
25580
25625
  var import_node_path25 = require("node:path");
25581
25626
 
25582
25627
  // src/command-plans.ts
@@ -27428,7 +27473,7 @@ function registerBootstrapCommands(program3) {
27428
27473
  const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
27429
27474
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
27430
27475
  const putSeed = async (target, content, ref, sha) => {
27431
- const tmp = (0, import_node_path25.join)((0, import_node_os13.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
27476
+ const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
27432
27477
  (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
27433
27478
  try {
27434
27479
  await gh(contentPutInputArgs(repo, target, tmp));
@@ -28027,7 +28072,7 @@ LIVE apply to ${repo}:
28027
28072
  } catch {
28028
28073
  existingSha = void 0;
28029
28074
  }
28030
- const tmp = (0, import_node_path25.join)((0, import_node_os13.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28075
+ const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28031
28076
  const desiredContent = desiredByRepo.get(rec.repo);
28032
28077
  if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
28033
28078
  (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
@@ -28202,7 +28247,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
28202
28247
  } catch {
28203
28248
  existingSha = void 0;
28204
28249
  }
28205
- const tmp = (0, import_node_path25.join)((0, import_node_os13.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28250
+ const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28206
28251
  (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
28207
28252
  try {
28208
28253
  await gh(contentPutInputArgs(repo, seed.target, tmp));
@@ -29946,7 +29991,7 @@ async function collectWaveStatus(deps) {
29946
29991
 
29947
29992
  // src/discovery-commands.ts
29948
29993
  var import_node_fs30 = require("node:fs");
29949
- var import_node_os14 = require("node:os");
29994
+ var import_node_os15 = require("node:os");
29950
29995
  var import_node_path28 = require("node:path");
29951
29996
  var GC_GH_TIMEOUT_MS = 2e4;
29952
29997
  async function collectStatus() {
@@ -30135,7 +30180,7 @@ async function collectOnboardStatus(opts = {}) {
30135
30180
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
30136
30181
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
30137
30182
  }
30138
- const home = (0, import_node_os14.homedir)();
30183
+ const home = (0, import_node_os15.homedir)();
30139
30184
  const plugin = onboardPluginGate({
30140
30185
  readKnown: () => readFileSyncSafe((0, import_node_path28.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs30.readFileSync),
30141
30186
  readSettings: () => readFileSyncSafe((0, import_node_path28.join)(home, ".claude", "settings.json"), import_node_fs30.readFileSync)
@@ -32084,7 +32129,7 @@ var import_node_fs31 = require("node:fs");
32084
32129
  var import_node_crypto9 = require("node:crypto");
32085
32130
 
32086
32131
  // src/issue-body.ts
32087
- var import_node_os15 = require("node:os");
32132
+ var import_node_os16 = require("node:os");
32088
32133
  var TextArgError = class extends Error {
32089
32134
  constructor(message2, code, offendingFlag) {
32090
32135
  super(message2);
@@ -32096,7 +32141,7 @@ var TextArgError = class extends Error {
32096
32141
  offendingFlag;
32097
32142
  };
32098
32143
  function emptyStdinMessage(fileFlag) {
32099
- if ((0, import_node_os15.platform)() === "win32") {
32144
+ if ((0, import_node_os16.platform)() === "win32") {
32100
32145
  return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
32101
32146
  }
32102
32147
  return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
@@ -34593,7 +34638,7 @@ function registerSchedulesCommands(program3) {
34593
34638
  // src/secrets-commands.ts
34594
34639
  var import_node_fs33 = require("node:fs");
34595
34640
  var import_node_path30 = require("node:path");
34596
- var import_node_os16 = require("node:os");
34641
+ var import_node_os17 = require("node:os");
34597
34642
 
34598
34643
  // src/secrets-diff.ts
34599
34644
  var TIMEOUT_MS2 = 8e3;
@@ -34716,7 +34761,7 @@ async function decryptRailsCredentials(input) {
34716
34761
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
34717
34762
  "puts JSON.generate(config.config)"
34718
34763
  ].join("\n");
34719
- const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path30.join)((0, import_node_os16.tmpdir)(), "mmi-rails-decrypt-"));
34764
+ const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path30.join)((0, import_node_os17.tmpdir)(), "mmi-rails-decrypt-"));
34720
34765
  const scriptPath = (0, import_node_path30.join)(scriptDir, "decrypt.rb");
34721
34766
  (0, import_node_fs33.writeFileSync)(scriptPath, script, "utf8");
34722
34767
  try {
@@ -36007,7 +36052,7 @@ async function runPrLand(prNumber, options, deps) {
36007
36052
  // src/merge-cleanup.ts
36008
36053
  var import_node_fs37 = require("node:fs");
36009
36054
  var import_node_path34 = require("node:path");
36010
- var import_node_os17 = require("node:os");
36055
+ var import_node_os18 = require("node:os");
36011
36056
 
36012
36057
  // src/board-advance.ts
36013
36058
  function repoOf2(ref) {
@@ -37045,7 +37090,7 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
37045
37090
  return rewritten === base ? null : rewritten;
37046
37091
  }
37047
37092
  function writeSquashBodyFile(body) {
37048
- const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path34.join)((0, import_node_os17.tmpdir)(), "mmi-squash-body-"));
37093
+ const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path34.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
37049
37094
  const path2 = (0, import_node_path34.join)(dir, "body.txt");
37050
37095
  (0, import_node_fs37.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
37051
37096
  `, "utf8");
@@ -37611,13 +37656,13 @@ var import_node_path38 = require("node:path");
37611
37656
 
37612
37657
  // src/jervcode-node-modules-cleanup.ts
37613
37658
  var import_node_fs39 = require("node:fs");
37614
- var import_node_os18 = require("node:os");
37659
+ var import_node_os19 = require("node:os");
37615
37660
  var import_node_path36 = require("node:path");
37616
37661
  var JERVCODE_PACKAGE_ENTRY = (0, import_node_path36.join)("node_modules", "@jervaise", "jervcode", "dist", "launcher-entry.js");
37617
37662
  var WIN_NAMES2 = ["jervcode.cmd", "jervcode"];
37618
37663
  var POSIX_NAMES2 = ["jervcode"];
37619
37664
  var NODE_MODULES_CLEANUP_TIMEOUT_MS = 3e5;
37620
- function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os18.homedir)(), platform2 = process.platform) {
37665
+ function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os19.homedir)(), platform2 = process.platform) {
37621
37666
  const names = platform2 === "win32" ? WIN_NAMES2 : POSIX_NAMES2;
37622
37667
  const out = [];
37623
37668
  for (const dir of jervCliCandidateDirs(env, home, platform2)) {
@@ -37625,7 +37670,7 @@ function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os18.h
37625
37670
  }
37626
37671
  return out;
37627
37672
  }
37628
- function resolveJervcodePath(env = process.env, home = (0, import_node_os18.homedir)(), platform2 = process.platform, exists = import_node_fs39.existsSync) {
37673
+ function resolveJervcodePath(env = process.env, home = (0, import_node_os19.homedir)(), platform2 = process.platform, exists = import_node_fs39.existsSync) {
37629
37674
  for (const candidate of jervcodeCandidatePaths(env, home, platform2)) {
37630
37675
  if (exists(candidate)) return candidate;
37631
37676
  }
@@ -37634,7 +37679,7 @@ function resolveJervcodePath(env = process.env, home = (0, import_node_os18.home
37634
37679
  function jervcodeExecFileArgs(args, opts = {}) {
37635
37680
  const platform2 = opts.platform ?? process.platform;
37636
37681
  const exists = opts.exists ?? import_node_fs39.existsSync;
37637
- const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os18.homedir)(), platform2, exists);
37682
+ const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os19.homedir)(), platform2, exists);
37638
37683
  if (resolved) {
37639
37684
  const entry = (0, import_node_path36.join)((0, import_node_path36.join)(resolved, ".."), JERVCODE_PACKAGE_ENTRY);
37640
37685
  if (exists(entry)) {
@@ -40223,7 +40268,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
40223
40268
  var import_node_child_process16 = require("node:child_process");
40224
40269
  var import_node_crypto12 = require("node:crypto");
40225
40270
  var import_node_fs45 = require("node:fs");
40226
- var import_node_os19 = require("node:os");
40271
+ var import_node_os20 = require("node:os");
40227
40272
  var import_node_path41 = require("node:path");
40228
40273
 
40229
40274
  // ../scripts/distribution-digest.mjs
@@ -40374,7 +40419,7 @@ function rebuildTo(packageRoot, outDir) {
40374
40419
  });
40375
40420
  }
40376
40421
  function runDistStatus(root) {
40377
- const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path41.join)((0, import_node_os19.tmpdir)(), "mmi-dist-drift-"));
40422
+ const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path41.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
40378
40423
  let overlayCount = 0;
40379
40424
  try {
40380
40425
  const cliOut = (0, import_node_path41.join)(stage, "cli-dist");
@@ -43058,7 +43103,7 @@ function envHealLockPath(home) {
43058
43103
  async function withEnvHealLock(what, run) {
43059
43104
  try {
43060
43105
  return await withFileLock(
43061
- envHealLockPath((0, import_node_os20.homedir)()),
43106
+ envHealLockPath((0, import_node_os21.homedir)()),
43062
43107
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
43063
43108
  run
43064
43109
  );
@@ -43154,7 +43199,7 @@ function mmiDoctorDeps(opts = {}) {
43154
43199
  const configRoot = surfaceConfigRoot(surface);
43155
43200
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43156
43201
  const plan = buildPluginCachePlan(
43157
- (0, import_node_os20.homedir)(),
43202
+ (0, import_node_os21.homedir)(),
43158
43203
  running,
43159
43204
  pluginCacheFsDeps(configRoot, () => 0),
43160
43205
  { configRoot, includeStaging: surface !== "codex" }
@@ -43182,7 +43227,7 @@ function mmiDoctorDeps(opts = {}) {
43182
43227
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43183
43228
  const installed = installedActivePluginVersion(surface);
43184
43229
  const plan = buildPluginCachePlan(
43185
- (0, import_node_os20.homedir)(),
43230
+ (0, import_node_os21.homedir)(),
43186
43231
  running,
43187
43232
  pluginCacheFsDeps(configRoot, () => 0),
43188
43233
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
@@ -44320,7 +44365,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
44320
44365
  return;
44321
44366
  }
44322
44367
  const plan = buildPluginCachePlan(
44323
- (0, import_node_os20.homedir)(),
44368
+ (0, import_node_os21.homedir)(),
44324
44369
  running,
44325
44370
  pluginCacheFsDeps(configRoot, directoryBytes),
44326
44371
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.8",
3
+ "version": "4.3.10",
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",