fluncle 0.174.0 → 0.175.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 +32 -35
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -561,7 +561,7 @@ function parseVersion(version) {
561
561
  var currentVersion;
562
562
  var init_version = __esm(() => {
563
563
  init_output();
564
- currentVersion = "0.174.0".trim() ? "0.174.0".trim() : "0.1.0";
564
+ currentVersion = "0.175.0".trim() ? "0.175.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2259,7 +2259,7 @@ function parseVersion2(version) {
2259
2259
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2260
2260
  var init_version2 = __esm(() => {
2261
2261
  init_output();
2262
- currentVersion2 = "0.174.0".trim() ? "0.174.0".trim() : "0.1.0";
2262
+ currentVersion2 = "0.175.0".trim() ? "0.175.0".trim() : "0.1.0";
2263
2263
  });
2264
2264
 
2265
2265
  // ../../packages/registry/src/index.ts
@@ -3119,16 +3119,16 @@ var init_src = __esm(() => {
3119
3119
  weights: { status: "hidden" }
3120
3120
  },
3121
3121
  {
3122
- command: "fluncle admin backfills apple-releases",
3122
+ command: "fluncle admin backfills label-releases",
3123
3123
  exposedContent: [
3124
- "tap Apple Music's latest releases for the operator's enabled seed labels → day-one uncertified catalogue rows"
3124
+ "tap Spotify's fresh releases for the operator's enabled seed labels → day-one uncertified catalogue rows"
3125
3125
  ],
3126
3126
  kind: "cron",
3127
- name: "cron.apple-releases",
3128
- operatorNotes: "every 24h, run by a rave-02 HOST systemd timer (docs/agents/hermes/apple-releases-timer/). The MusicKit FRESHNESS TAP (D8): MusicBrainz WALKS the graph (cron.crawl) but lags a release ~2 weeks; Apple has it day one, so this taps each ENABLED seed label's latest releases and mints METADATA-ONLY catalogue rows (a `tracks` row with no `findings` row) with their real release dates — closing the /fresh lag cliff. It certifies nothing, publishes nothing, and never widens the graph (no new labels, no artist hops). Deduped against the MB crawl from both directions (ISRC + same-album title fold). Worker-paced (the three APPLE_MUSIC_* secrets live on the Worker); a NO-OP until they are provisioned. Zero LLM tokens. Source: docs/agents/hermes/scripts/apple-releases-sweep.*. See docs/catalogue-crawler.md.",
3127
+ name: "cron.label-releases",
3128
+ operatorNotes: "every 24h, run by a rave-02 HOST systemd timer (docs/agents/hermes/label-releases-timer/). The FRESHNESS TAP (D8): MusicBrainz WALKS the graph (cron.crawl) but lags a release ~2 weeks; Spotify has it day one, so this searches each ENABLED seed label's fresh releases (`label:\"<name>\" tag:new`, copyright-filtered — the fuzzy `label:` search is post-filtered on the album's ℗/© strings) and mints METADATA-ONLY catalogue rows (a `tracks` row with no `findings` row) with their real release dates — closing the /fresh lag cliff. It certifies nothing, publishes nothing, and never widens the graph (no new labels, no artist hops). Deduped against the MB crawl from both directions (Spotify id/uri/ISRC + same-album title fold). Worker-paced, reusing the publish path's Spotify OAuth (no new secret; Apple was a measured 2/99 seed-label dead end). Zero LLM tokens. Source: docs/agents/hermes/scripts/label-releases-sweep.*. See docs/catalogue-crawler.md.",
3129
3129
  probeConfig: {
3130
3130
  cadenceMs: 24 * 60 * MINUTE_MS,
3131
- cronName: "fluncle-apple-releases",
3131
+ cronName: "fluncle-label-releases",
3132
3132
  kind: "cron"
3133
3133
  },
3134
3134
  statusDescription: "taps day-one releases from the enabled labels",
@@ -4109,8 +4109,8 @@ __export(exports_admin_tracks, {
4109
4109
  captureQueueCommand: () => captureQueueCommand,
4110
4110
  backfillRecordingMbidsCommand: () => backfillRecordingMbidsCommand,
4111
4111
  backfillLastfmCommand: () => backfillLastfmCommand,
4112
+ backfillLabelReleasesCommand: () => backfillLabelReleasesCommand,
4112
4113
  backfillDiscogsCommand: () => backfillDiscogsCommand,
4113
- backfillAppleReleasesCommand: () => backfillAppleReleasesCommand,
4114
4114
  backfillAppleMusicCommand: () => backfillAppleMusicCommand,
4115
4115
  backfillAppleCatalogueCommand: () => backfillAppleCatalogueCommand
4116
4116
  });
@@ -4296,9 +4296,9 @@ async function backfillAppleCatalogueCommand(limit, dryRun) {
4296
4296
  const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
4297
4297
  return adminApiPost(`/api/admin/backfill/apple-catalogue?${params.toString()}`);
4298
4298
  }
4299
- async function backfillAppleReleasesCommand(limit, dryRun) {
4299
+ async function backfillLabelReleasesCommand(limit, dryRun) {
4300
4300
  const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
4301
- return adminApiPost(`/api/admin/backfill/apple-releases?${params.toString()}`);
4301
+ return adminApiPost(`/api/admin/backfill/label-releases?${params.toString()}`);
4302
4302
  }
4303
4303
  async function backfillRecordingMbidsCommand(limit, dryRun, cursor) {
4304
4304
  const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
@@ -9679,9 +9679,9 @@ function addAdminCommands(program2) {
9679
9679
  const { backfillAppleCatalogueCommand: backfillAppleCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
9680
9680
  await runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2);
9681
9681
  });
9682
- backfill.command("apple-releases").description("Tap Apple's latest releases for enabled seed labels into catalogue rows").option("--dry-run", "Report the labels that would be probed without any Apple call", false).option("--limit <limit>", "Max enabled seed labels to probe per pass", "5").option("--json", "Print JSON", false).action(async (options) => {
9683
- const { backfillAppleReleasesCommand: backfillAppleReleasesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
9684
- await runBackfillAppleReleases(options, backfillAppleReleasesCommand2);
9682
+ backfill.command("label-releases").description("Tap Spotify's fresh releases for enabled seed labels into catalogue rows").option("--dry-run", "Report the labels that would be probed without any Spotify call", false).option("--limit <limit>", "Max enabled seed labels to probe per pass", "5").option("--json", "Print JSON", false).action(async (options) => {
9683
+ const { backfillLabelReleasesCommand: backfillLabelReleasesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
9684
+ await runBackfillLabelReleases(options, backfillLabelReleasesCommand2);
9685
9685
  });
9686
9686
  backfill.command("artists").description("Back-fill the artist entity (artists + track_artists) for existing findings").option("--dry-run", "Report which findings would be upserted without touching the DB", false).option("--limit <limit>", "Max findings to process", "50").option("--json", "Print JSON", false).action(async (options) => {
9687
9687
  const { backfillArtistsCommand: backfillArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
@@ -10376,42 +10376,39 @@ async function runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2
10376
10376
  process.exitCode = 1;
10377
10377
  }
10378
10378
  }
10379
- async function runBackfillAppleReleases(options, backfillAppleReleasesCommand2) {
10379
+ async function runBackfillLabelReleases(options, backfillLabelReleasesCommand2) {
10380
10380
  const perPass = parseListLimit(options.limit);
10381
10381
  const newTrackIds = [];
10382
- const resolvedLabels = [];
10383
- const unresolvedLabels = [];
10382
+ const labelSlugs = [];
10383
+ const failedLabels = [];
10384
10384
  let dryRun = options.dryRun;
10385
10385
  let throttled = false;
10386
- let breakerTripped = false;
10387
10386
  let configured = true;
10388
10387
  let labelsProbed = 0;
10389
10388
  let albumsSeen = 0;
10389
+ let albumsMatched = 0;
10390
10390
  let newRows = 0;
10391
10391
  let skippedKnown = 0;
10392
10392
  const MAX_PASSES = 100;
10393
10393
  for (let pass = 0;pass < MAX_PASSES; pass += 1) {
10394
- const result = await backfillAppleReleasesCommand2(perPass, options.dryRun);
10394
+ const result = await backfillLabelReleasesCommand2(perPass, options.dryRun);
10395
10395
  dryRun = result.dryRun;
10396
10396
  configured = result.configured;
10397
10397
  labelsProbed += result.labelsProbed;
10398
10398
  albumsSeen += result.albumsSeen;
10399
+ albumsMatched += result.albumsMatched;
10399
10400
  newRows += result.newRows;
10400
10401
  skippedKnown += result.skippedKnown;
10401
10402
  newTrackIds.push(...result.newTrackIds);
10402
- resolvedLabels.push(...result.resolvedLabels);
10403
- unresolvedLabels.push(...result.unresolvedLabels);
10403
+ labelSlugs.push(...result.labelSlugs);
10404
+ failedLabels.push(...result.failedLabels);
10404
10405
  if (!options.json) {
10405
10406
  const verb2 = result.dryRun ? "would probe" : "probed";
10406
- console.log(` \u2026${verb2} ${result.dryRun ? result.resolvedLabels.length : result.labelsProbed} label(s); ${result.newRows} new; ${result.skippedKnown} known; ${result.unresolvedLabels.length} unresolved`);
10407
+ console.log(` \u2026${verb2} ${result.labelSlugs.length} label(s); ${result.albumsMatched} matched album(s); ${result.newRows} new; ${result.skippedKnown} known; ${result.failedLabels.length} failed`);
10407
10408
  }
10408
10409
  if (!result.configured) {
10409
10410
  break;
10410
10411
  }
10411
- if (result.breakerTripped) {
10412
- breakerTripped = true;
10413
- break;
10414
- }
10415
10412
  if (result.rateLimited) {
10416
10413
  throttled = true;
10417
10414
  break;
@@ -10419,35 +10416,35 @@ async function runBackfillAppleReleases(options, backfillAppleReleasesCommand2)
10419
10416
  if (result.dryRun) {
10420
10417
  break;
10421
10418
  }
10422
- if (result.labelsProbed === 0 && result.resolvedLabels.length === 0) {
10419
+ if (result.labelsProbed === 0) {
10423
10420
  break;
10424
10421
  }
10425
10422
  }
10426
10423
  if (options.json) {
10427
10424
  printSweepJson({
10425
+ albumsMatched,
10428
10426
  albumsSeen,
10429
- breakerTripped,
10430
10427
  configured,
10431
10428
  dryRun,
10429
+ failedLabels,
10430
+ labelSlugs,
10432
10431
  labelsProbed,
10433
10432
  newRows,
10434
10433
  newTrackIds,
10435
10434
  rateLimited: throttled,
10436
- resolvedLabels,
10437
- skippedKnown,
10438
- unresolvedLabels
10435
+ skippedKnown
10439
10436
  }, 0);
10440
10437
  return;
10441
10438
  }
10442
10439
  if (!configured) {
10443
- console.log("Apple releases tap is not configured (the Worker's MusicKit secrets are unset) \u2014 nothing probed.");
10440
+ console.log("Label-releases tap is a no-op \u2014 the Worker's Spotify grant is gone; reconnect Spotify.");
10444
10441
  return;
10445
10442
  }
10446
10443
  const verb = dryRun ? "Would probe" : "Probed";
10447
- const probedCount = dryRun ? resolvedLabels.length : labelsProbed;
10448
- console.log(`${verb} ${probedCount} enabled seed label(s); ${newRows} new catalogue row(s); ${skippedKnown} already known; ${resolvedLabels.length} resolved; ${unresolvedLabels.length} unresolved.`);
10449
- for (const slug of unresolvedLabels) {
10450
- console.log(` no exact Apple label match: ${slug}`);
10444
+ const probedCount = dryRun ? labelSlugs.length : labelsProbed;
10445
+ console.log(`${verb} ${probedCount} enabled seed label(s); ${albumsMatched} matched album(s); ${newRows} new catalogue row(s); ${skippedKnown} already known; ${failedLabels.length} failed.`);
10446
+ for (const slug of failedLabels) {
10447
+ console.log(` transient Spotify error: ${slug}`);
10451
10448
  }
10452
10449
  }
10453
10450
  async function runBackfillArtists(options, backfillArtistsCommand2) {
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.174.0"
34
+ "version": "0.175.0"
35
35
  }