fluncle 0.237.0 → 0.239.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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +42 -15
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -622,7 +622,7 @@ function parseVersion(version) {
622
622
  var currentVersion;
623
623
  var init_version = __esm(() => {
624
624
  init_output();
625
- currentVersion = "0.237.0".trim() ? "0.237.0".trim() : "0.1.0";
625
+ currentVersion = "0.239.0".trim() ? "0.239.0".trim() : "0.1.0";
626
626
  });
627
627
 
628
628
  // src/update-notifier.ts
@@ -2335,7 +2335,7 @@ function parseVersion2(version) {
2335
2335
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2336
2336
  var init_version2 = __esm(() => {
2337
2337
  init_output();
2338
- currentVersion2 = "0.237.0".trim() ? "0.237.0".trim() : "0.1.0";
2338
+ currentVersion2 = "0.239.0".trim() ? "0.239.0".trim() : "0.1.0";
2339
2339
  });
2340
2340
 
2341
2341
  // ../../packages/registry/src/index.ts
@@ -10415,11 +10415,12 @@ var init_admin_artifacts = __esm(() => {
10415
10415
  });
10416
10416
 
10417
10417
  // ../../packages/contracts/src/orpc/admin-artist-rules.ts
10418
- var ArtistRuleVerdictSchema, ArtistRuleSourceSchema, ArtistMbidSchema, ArtistNameSchema, ArtistRuleSchema, ArtistRuleInputSchema, AddArtistRuleInputSchema, listArtistRules, addArtistRule, updateArtistRule, removeArtistRule, adminArtistRulesContract;
10418
+ var ArtistRuleVerdictSchema, LabelArtistRuleVerdictSchema, ArtistRuleSourceSchema, ArtistMbidSchema, ArtistNameSchema, ArtistRuleSchema, ArtistRuleInputSchema, AddArtistRuleInputSchema, listArtistRules, addArtistRule, updateArtistRule, removeArtistRule, adminArtistRulesContract;
10419
10419
  var init_admin_artist_rules = __esm(() => {
10420
10420
  init_dist3();
10421
10421
  init_zod();
10422
- ArtistRuleVerdictSchema = _enum(["allow", "block"]).meta({ id: "ArtistRuleVerdict" });
10422
+ ArtistRuleVerdictSchema = _enum(["allow", "block", "unlisted"]).meta({ id: "ArtistRuleVerdict" });
10423
+ LabelArtistRuleVerdictSchema = _enum(["allow", "block"]).meta({ id: "LabelArtistRuleVerdict" });
10423
10424
  ArtistRuleSourceSchema = _enum(["operator", "triage"]).meta({ id: "ArtistRuleSource" });
10424
10425
  ArtistMbidSchema = string2().uuid();
10425
10426
  ArtistNameSchema = string2().trim().min(1, "Artist name cannot be blank");
@@ -10438,10 +10439,11 @@ var init_admin_artist_rules = __esm(() => {
10438
10439
  ArtistRuleInputSchema = object({
10439
10440
  artistMbid: ArtistMbidSchema,
10440
10441
  artistName: ArtistNameSchema,
10441
- verdict: ArtistRuleVerdictSchema
10442
+ verdict: LabelArtistRuleVerdictSchema
10442
10443
  }).meta({ id: "ArtistRuleInput" });
10443
10444
  AddArtistRuleInputSchema = ArtistRuleInputSchema.extend({
10444
- artistName: ArtistNameSchema.optional()
10445
+ artistName: ArtistNameSchema.optional(),
10446
+ verdict: ArtistRuleVerdictSchema
10445
10447
  }).meta({ id: "AddArtistRuleInput" });
10446
10448
  listArtistRules = oc.route({
10447
10449
  method: "GET",
@@ -11433,7 +11435,7 @@ var init_admin_bios = __esm(() => {
11433
11435
  });
11434
11436
 
11435
11437
  // ../../packages/contracts/src/orpc/admin-catalogue.ts
11436
- var CatalogueLensSchema, CapturePriorityReasonSchema, CatalogueMatchSchema, CatalogueTrackItemSchema, CatalogueSummarySchema, listCatalogueTracks, rankCatalogue, recordDemand, clearWrongAudio, requeueUnmatchedCaptures, requeueAnchor, requeueIsrcRecovery, flagWrongAudio, forceCapture, certifyTrack, setTrackDismissed, CaptureVerifyItemSchema, listUnverifiedCaptures, verifyCapture, CrawlPassSchema, CrawlPhaseInitializationSchema, MAX_CRAWL_PREPARE_LIMIT = 6, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, crawlCatalogue, getCrawlStatus, ANCHOR_CANDIDATE_LIMIT = 100, ANCHOR_ARTIST_LIMIT = 20, ANCHOR_TEXT_MAX = 300, ANCHOR_ISRC_MAX = 64, ANCHOR_ID_MAX = 64, ANCHOR_URL_MAX = 2048, AnchorCandidateSchema, anchorTrack, DEEZER_CANDIDATE_LIMIT = 5, DEEZER_TEXT_MAX = 300, DEEZER_ISRC_MAX = 64, DEEZER_TRACK_ID_MAX = 32, DeezerIsrcCandidateSchema, resolveAnchor, resolveAnchorReview, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, SpotifyAnchorBreakerStateSchema, getSpotifyAnchorBreaker, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
11438
+ var CatalogueLensSchema, CapturePriorityReasonSchema, CatalogueMatchSchema, CatalogueTrackItemSchema, CatalogueSummarySchema, listCatalogueTracks, rankCatalogue, recordDemand, clearWrongAudio, requeueUnmatchedCaptures, requeueAnchor, requeueIsrcRecovery, flagWrongAudio, forceCapture, certifyTrack, setTrackDismissed, CaptureVerifyItemSchema, listUnverifiedCaptures, verifyCapture, CrawlPassSchema, CrawlPhaseInitializationSchema, MAX_CRAWL_PREPARE_LIMIT = 6, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, crawlCatalogue, getCrawlStatus, ANCHOR_CANDIDATE_LIMIT = 100, ANCHOR_ARTIST_LIMIT = 20, ANCHOR_TEXT_MAX = 300, ANCHOR_ISRC_MAX = 64, ANCHOR_ID_MAX = 64, ANCHOR_URL_MAX = 2048, AnchorCandidateSchema, anchorTrack, DEEZER_CANDIDATE_LIMIT = 5, DEEZER_TEXT_MAX = 300, DEEZER_ISRC_MAX = 64, DEEZER_TRACK_ID_MAX = 32, DeezerIsrcCandidateSchema, resolveAnchor, resolveAnchorReview, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, SpotifyAnchorBreakerStateSchema, AnchorRungFlagsSchema, getSpotifyAnchorBreaker, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
11437
11439
  var init_admin_catalogue = __esm(() => {
11438
11440
  init_dist3();
11439
11441
  init_zod();
@@ -11839,7 +11841,9 @@ var init_admin_catalogue = __esm(() => {
11839
11841
  source: _enum(["listenbrainz", "spotify-isrc", "spotify-search"]).nullable(),
11840
11842
  spotifyIsrcAsked: boolean2(),
11841
11843
  spotifySearchDone: boolean2(),
11844
+ spotifySearchEnabled: boolean2(),
11842
11845
  spotifyThrottled: boolean2(),
11846
+ stamped: boolean2(),
11843
11847
  verifiedBy: _enum(["isrc", "search", "search-subset"]).nullable()
11844
11848
  }));
11845
11849
  resolveAnchorReview = oc.route({
@@ -11908,13 +11912,17 @@ var init_admin_catalogue = __esm(() => {
11908
11912
  tripped: boolean2(),
11909
11913
  trippedAt: string2().nullable()
11910
11914
  }).meta({ id: "SpotifyAnchorBreakerState" });
11915
+ AnchorRungFlagsSchema = object({
11916
+ apifyEnabled: boolean2(),
11917
+ spotifySearchEnabled: boolean2()
11918
+ }).meta({ id: "AnchorRungFlags" });
11911
11919
  getSpotifyAnchorBreaker = oc.route({
11912
11920
  method: "GET",
11913
11921
  operationId: "getSpotifyAnchorBreaker",
11914
11922
  path: "/admin/catalogue/anchor/breaker",
11915
- summary: "The Spotify anchor-search throttle breaker: tripped, why, and how long left",
11923
+ summary: "The Spotify anchor-search throttle breaker: tripped, why, how long left, and the rungs",
11916
11924
  tags: ["Admin"]
11917
- }).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({ ok: literal(true) }));
11925
+ }).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({ ok: literal(true), rungs: AnchorRungFlagsSchema }));
11918
11926
  resetSpotifyAnchorBreaker = oc.route({
11919
11927
  method: "POST",
11920
11928
  operationId: "resetSpotifyAnchorBreaker",
@@ -18530,6 +18538,9 @@ async function requeueAnchorCommand(trackIds) {
18530
18538
  async function requeueIsrcRecoveryCommand(input) {
18531
18539
  return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
18532
18540
  }
18541
+ async function anchorBreakerCommand() {
18542
+ return adminApiGet("/api/v1/admin/catalogue/anchor/breaker");
18543
+ }
18533
18544
  async function forceCaptureCommand(trackId) {
18534
18545
  return adminApiPost("/api/v1/admin/catalogue/force-capture", {
18535
18546
  trackId
@@ -18598,8 +18609,8 @@ function artistRuleInput(artistMbid, verdict, artistName) {
18598
18609
  if (!MBID_PATTERN.test(cleanMbid)) {
18599
18610
  throw new Error("Artist MBID must be a MusicBrainz artist MBID");
18600
18611
  }
18601
- if (verdict !== "allow" && verdict !== "block") {
18602
- throw new Error("Pass --verdict allow|block");
18612
+ if (verdict !== "allow" && verdict !== "block" && verdict !== "unlisted") {
18613
+ throw new Error("Pass --verdict allow|block|unlisted");
18603
18614
  }
18604
18615
  const cleanName = artistName?.trim();
18605
18616
  if (artistName !== undefined && !cleanName) {
@@ -21230,8 +21241,9 @@ function labelUpdateLines(label, options) {
21230
21241
  return lines;
21231
21242
  }
21232
21243
  var ARTIST_RULE_BOUNDARY = "Rules change what the next crawl takes. Everything already here stays.";
21233
- function artistRuleWriteLine(prefix) {
21234
- return `${prefix} \u2014 ${ARTIST_RULE_BOUNDARY.replace(/^R/, "r")}`;
21244
+ var UNLISTED_RULE_BOUNDARY = "Unlisted takes the artist page down. The crawl and everything already here stay.";
21245
+ function artistRuleWriteLine(prefix, boundary = ARTIST_RULE_BOUNDARY) {
21246
+ return `${prefix} \u2014 ${boundary.charAt(0).toLowerCase()}${boundary.slice(1)}`;
21235
21247
  }
21236
21248
  function artistRuleLines(rules) {
21237
21249
  if (rules.length === 0) {
@@ -22186,6 +22198,20 @@ JSON field reference:
22186
22198
  }
22187
22199
  console.log(`Re-queued ${requeued} of ${trackIds.length} row(s) \u2014 the next anchor tick picks them up at their real priority.`);
22188
22200
  });
22201
+ catalogue.command("anchor-breaker").description("Why the anchor rungs are quiet: the throttle breaker, and which rungs are armed").option("--json", "Print JSON", false).action(async (options) => {
22202
+ await Promise.resolve().then(() => init_admin_catalogue2());
22203
+ const state = await anchorBreakerCommand();
22204
+ if (options.json) {
22205
+ printJson({ ...state, ok: true });
22206
+ return;
22207
+ }
22208
+ const minutes = Math.ceil(state.cooldownRemainingMs / 60000);
22209
+ console.log(state.tripped ? `BREAKER TRIPPED \u2014 ${state.reason ?? "unknown"}, ~${minutes}m left (${state.throttlesInWindow} throttle(s) in window)` : `BREAKER CLEAR \u2014 ${state.throttlesInWindow} throttle(s) in window`);
22210
+ console.log(`RUNGS \u2014 spotify search ${state.rungs.spotifySearchEnabled ? "ARMED" : "OFF"} \xB7 apify fallback ${state.rungs.apifyEnabled ? "ARMED" : "OFF"}`);
22211
+ if (!state.rungs.spotifySearchEnabled && !state.rungs.apifyEnabled) {
22212
+ console.log("No rung can conclude \u2014 ListenBrainz still runs and can win, but its miss settles nothing.");
22213
+ }
22214
+ });
22189
22215
  catalogue.command("requeue-isrc-recovery").description("Clear Deezer-empty ISRC-recovery stamps from a window (dry-run; --apply to take)").requiredOption("--since <iso>", "Inclusive lower bound on the stamp (2026-09-09)").option("--apply", "Actually clear the stamps (default is a dry-run count)", false).option("--json", "Print JSON", false).action(async (options) => {
22190
22216
  await Promise.resolve().then(() => init_admin_catalogue2());
22191
22217
  const result = await requeueIsrcRecoveryCommand({
@@ -22406,14 +22432,14 @@ JSON field reference:
22406
22432
  await Promise.resolve().then(() => init_admin_artists2());
22407
22433
  await runArtistsRank(options, rankArtistsCommand);
22408
22434
  });
22409
- artists.command("rule").description("Always or never take one MusicBrainz artist's records (operator)").argument("<artist-mbid>", "The MusicBrainz artist MBID").requiredOption("--verdict <verdict>", "The ruling: allow or block").option("--name <name>", "Artist name (optional; the server resolves it when omitted)").option("--json", "Print JSON", false).action(async (artistMbid, options) => {
22435
+ artists.command("rule").description("Rule one MusicBrainz artist: what the crawl takes, or whether they get a page").argument("<artist-mbid>", "The MusicBrainz artist MBID").requiredOption("--verdict <verdict>", "The ruling: allow, block, or unlisted (no public artist page)").option("--name <name>", "Artist name (optional; the server resolves it when omitted)").option("--json", "Print JSON", false).action(async (artistMbid, options) => {
22410
22436
  await Promise.resolve().then(() => init_admin_artists2());
22411
22437
  const rule = await addArtistRuleCommand(artistRuleInput(artistMbid, options.verdict, options.name));
22412
22438
  if (options.json) {
22413
22439
  printJson({ ok: true, rule });
22414
22440
  return;
22415
22441
  }
22416
- console.log(artistRuleWriteLine(`Rule set for ${rule.resolvedName ?? rule.artistName}: ${rule.verdict.toUpperCase()}`));
22442
+ console.log(artistRuleWriteLine(`Rule set for ${rule.resolvedName ?? rule.artistName}: ${rule.verdict.toUpperCase()}`, rule.verdict === "unlisted" ? UNLISTED_RULE_BOUNDARY : ARTIST_RULE_BOUNDARY));
22417
22443
  });
22418
22444
  artists.command("rules").description("List the global artist crawl rules").option("--json", "Print JSON", false).action(async (options) => {
22419
22445
  await Promise.resolve().then(() => init_admin_artists2());
@@ -25548,6 +25574,7 @@ if (__require.main == __require.module) {
25548
25574
  }
25549
25575
  export {
25550
25576
  ARTIST_RULE_BOUNDARY,
25577
+ UNLISTED_RULE_BOUNDARY,
25551
25578
  artistRuleLines,
25552
25579
  createProgram,
25553
25580
  labelUpdateLines,
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.237.0"
34
+ "version": "0.239.0"
35
35
  }