fluncle 0.136.0 → 0.137.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 +81 -4
  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.136.0".trim() ? "0.136.0".trim() : "0.1.0";
564
+ currentVersion = "0.137.0".trim() ? "0.137.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2189,7 +2189,7 @@ function parseVersion2(version) {
2189
2189
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2190
2190
  var init_version2 = __esm(() => {
2191
2191
  init_output();
2192
- currentVersion2 = "0.136.0".trim() ? "0.136.0".trim() : "0.1.0";
2192
+ currentVersion2 = "0.137.0".trim() ? "0.137.0".trim() : "0.1.0";
2193
2193
  });
2194
2194
 
2195
2195
  // ../../packages/registry/src/index.ts
@@ -2247,11 +2247,11 @@ var init_src = __esm(() => {
2247
2247
  },
2248
2248
  {
2249
2249
  exposedContent: [
2250
- "the mixability set-builderchain a set from Fluncle's findings that mixes clean, share it as a link"
2250
+ "the free mixing tool name artists you like, and Fluncle ranks what mixes in clean next from the whole archive; chain a set and share it as a link"
2251
2251
  ],
2252
2252
  kind: "web_route",
2253
2253
  name: "web.mix",
2254
- operatorNotes: "ADMIN-GATED at launch (RFC mixability-engine, Decision 1): the full /mix set-builder ships behind admin auth until the archive clears the ~250-finding floor. Registered here as `pending` (dark to every public consumer — the dev-row, llms.txt, the sitemap, /status) so going public is one flip: remove `pending` + set a web weight + announce. The `list_mixable_tracks` API op already ships at its final public tier, so the flip needs no rebuild. No probeConfig — the admin gate 302s a bare GET to /admin/login, which would read as a false 'down'.",
2254
+ operatorNotes: "PUBLIC-CAPABLE, gated by a SELF-LIFTING DEPTH MEASUREMENT (not admin auth). The old ~250-finding / admin gate is gone: `/mix` now measures the live archive on every load (`getMixChainDepth` — can the median track reach a full set + rail by a named harmonic move?) and opens to the world on its own the day the catalogue lands enough keyed depth. Until then a stranger is redirected home and the operator still gets in to dogfood. This entry stays `pending` (dark to the dev-row, llms.txt, the sitemap, /status) ONLY so we do not advertise a URL that still redirects the flip is: confirm the depth gate has opened in prod, remove `pending`, announce. The web weight is pre-set, so the flip needs no other change. No probeConfig — a closed gate 302s a bare GET, which would read as a false 'down'.",
2255
2255
  pending: true,
2256
2256
  route: "/mix",
2257
2257
  url: `${SITE}/mix`,
@@ -5746,6 +5746,22 @@ var init_admin_artists = __esm(() => {
5746
5746
  init_api();
5747
5747
  });
5748
5748
 
5749
+ // src/commands/admin-labels.ts
5750
+ var exports_admin_labels = {};
5751
+ __export(exports_admin_labels, {
5752
+ backfillLabelImagesCommand: () => backfillLabelImagesCommand
5753
+ });
5754
+ async function backfillLabelImagesCommand(limit, dryRun, cursor) {
5755
+ const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
5756
+ if (cursor) {
5757
+ params.set("cursor", cursor);
5758
+ }
5759
+ return adminApiPost(`/api/admin/backfill/label-images?${params.toString()}`);
5760
+ }
5761
+ var init_admin_labels = __esm(() => {
5762
+ init_api();
5763
+ });
5764
+
5749
5765
  // src/commands/preview-migration.ts
5750
5766
  var exports_preview_migration = {};
5751
5767
  __export(exports_preview_migration, {
@@ -8762,6 +8778,10 @@ function addAdminCommands(program2) {
8762
8778
  const { backfillArtistImagesCommand: backfillArtistImagesCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
8763
8779
  await runBackfillArtistImages(options, backfillArtistImagesCommand2);
8764
8780
  });
8781
+ backfill.command("label-images").description("Resolve label logos (Discogs \u2192 Wikidata) into R2 for existing labels").option("--dry-run", "Report the eligible worklist without any vendor call or write", false).option("--limit <limit>", "Max labels to process", "50").option("--json", "Print JSON", false).action(async (options) => {
8782
+ const { backfillLabelImagesCommand: backfillLabelImagesCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
8783
+ await runBackfillLabelImages(options, backfillLabelImagesCommand2);
8784
+ });
8765
8785
  artists.command("resolve").description("Resolve an artist's social identity (MB url-rels + Firecrawl gap-fill)").argument("[artistId]").option("--queue", "Show the resolve worklist (artists awaiting resolution), oldest first", false).option("--limit <limit>", "Number of artists to show with --queue", "50").option("--json", "Print JSON", false).allowExcessArguments().action(async (artistId, options) => {
8766
8786
  if (options.queue) {
8767
8787
  const { listArtistsCommand: listArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
@@ -9253,6 +9273,63 @@ async function runBackfillArtistImages(options, backfillArtistImagesCommand2) {
9253
9273
  process.exitCode = 1;
9254
9274
  }
9255
9275
  }
9276
+ async function runBackfillLabelImages(options, backfillLabelImagesCommand2) {
9277
+ const limit = parseListLimit(options.limit);
9278
+ const resolved = [];
9279
+ const none = [];
9280
+ const failed = [];
9281
+ let cursor;
9282
+ let dryRun = options.dryRun;
9283
+ let throttled = false;
9284
+ while (resolved.length + none.length + failed.length < limit) {
9285
+ const remaining = limit - (resolved.length + none.length + failed.length);
9286
+ const result = await backfillLabelImagesCommand2(remaining, options.dryRun, cursor);
9287
+ dryRun = result.dryRun;
9288
+ resolved.push(...result.resolved);
9289
+ none.push(...result.none);
9290
+ failed.push(...result.failed);
9291
+ if (!options.json) {
9292
+ const verb2 = result.dryRun ? "would resolve" : "resolved";
9293
+ console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.noneCount} without an image; ${result.failedCount} failed`);
9294
+ }
9295
+ if (result.rateLimited) {
9296
+ throttled = true;
9297
+ break;
9298
+ }
9299
+ if (result.nextCursor === null) {
9300
+ break;
9301
+ }
9302
+ cursor = result.nextCursor;
9303
+ }
9304
+ if (options.json) {
9305
+ printJson({
9306
+ dryRun,
9307
+ failed,
9308
+ failedCount: failed.length,
9309
+ none,
9310
+ noneCount: none.length,
9311
+ ok: true,
9312
+ rateLimited: throttled,
9313
+ resolved,
9314
+ resolvedCount: resolved.length
9315
+ });
9316
+ if (failed.length > 0) {
9317
+ process.exitCode = 1;
9318
+ }
9319
+ return;
9320
+ }
9321
+ const verb = dryRun ? "Would resolve" : "Resolved";
9322
+ console.log(`${verb} ${resolved.length} label logo(s); ${none.length} without an image; ${failed.length} failed.`);
9323
+ for (const slug of resolved) {
9324
+ console.log(` ${slug}`);
9325
+ }
9326
+ for (const item of failed) {
9327
+ console.log(` ${item.slug}: ${item.error}`);
9328
+ }
9329
+ if (failed.length > 0) {
9330
+ process.exitCode = 1;
9331
+ }
9332
+ }
9256
9333
  async function runArtistResolveQueue(options, listArtistsCommand2) {
9257
9334
  const limit = parseListLimit(options.limit);
9258
9335
  const result = await listArtistsCommand2(limit ?? 50);
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.136.0"
34
+ "version": "0.137.0"
35
35
  }