fluncle 0.185.0 → 0.186.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.
- package/bin/fluncle.mjs +78 -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.
|
|
564
|
+
currentVersion = "0.186.0".trim() ? "0.186.0".trim() : "0.1.0";
|
|
565
565
|
});
|
|
566
566
|
|
|
567
567
|
// src/update-notifier.ts
|
|
@@ -2442,7 +2442,7 @@ function parseVersion2(version) {
|
|
|
2442
2442
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2443
2443
|
var init_version2 = __esm(() => {
|
|
2444
2444
|
init_output();
|
|
2445
|
-
currentVersion2 = "0.
|
|
2445
|
+
currentVersion2 = "0.186.0".trim() ? "0.186.0".trim() : "0.1.0";
|
|
2446
2446
|
});
|
|
2447
2447
|
|
|
2448
2448
|
// ../../packages/registry/src/index.ts
|
|
@@ -3457,6 +3457,19 @@ var init_src = __esm(() => {
|
|
|
3457
3457
|
title: "Artist edges",
|
|
3458
3458
|
weights: { status: "hidden" }
|
|
3459
3459
|
},
|
|
3460
|
+
{
|
|
3461
|
+
command: "fluncle admin backfills artist-credits",
|
|
3462
|
+
exposedContent: [
|
|
3463
|
+
"mint identity-true artists from MusicBrainz credits for slice 0's zero-matched residual → track_artists edges"
|
|
3464
|
+
],
|
|
3465
|
+
kind: "cron",
|
|
3466
|
+
name: "cron.artist-credits",
|
|
3467
|
+
operatorNotes: "every 5m, run by a rave-02 HOST systemd timer (docs/agents/hermes/artist-credits-timer/). The MB CREDIT SWEEP (RFC artist-primary-capture, slice 1b): the sibling that completes what `backfill_artist_edges` (slice 0) could not. Slice 0's name-fold left a ~14.3k ZERO-MATCHED residual (a track it stamped but wrote no edge — no credited name folded to an existing identity), and those are exactly the tracks capture-authorization (slice 1) cannot reason about, since it matches BY IDENTITY through the `track_artists` graph. This sweep picks up that residual: for each zero-matched track carrying a MusicBrainz recording identity (`mb_recording_id`, or the `mb_<recording-mbid>` PK a crawler-born row carries), ONE paced `/recording/<mbid>?inc=artist-credits` lookup through the shared MusicBrainz client names its credited artists WITH their MB artist ids, and each resolves down a three-rung ladder: an EXACT `mbid` match, else an ADOPT (the credit name folds unambiguously onto an existing artist with no mbid — the common case, since the residual is dominated by compound credit strings like 'Sub Focus & Dimension' whose members Fluncle already holds as Spotify-keyed rows; adopting `coalesce`s the mbid onto that row instead of minting a duplicate, the split-identity guard), else a MINT of a fresh identity-true row (`mintArtistByMbid` — a real MBID is identity, the licence slice 0 lacked). Fail-closed on any ambiguity (a shared fold, or a fold whose row carries a different mbid) → mint, never a wrong merge. Then the `track_artists` edges are written. A zero-matched track with NO MB identity is TERMINALLY SKIPPED (stamped, never retried). METADATA / GRAPH IDENTITY ONLY — it certifies nothing and publishes nothing (agent tier, the `backfill_recording_mbids` precedent). Worker-paced (the box holds no MusicBrainz budget): a SMALL bounded batch per tick (each row is one ~1.1s MB call), 1 req/s, circuit-broken on a throttle, with a 60s response budget that pauses mid-page and resumes on the next request. The `tracks` row carries the durable per-row reliability state — its OWN `artist_credits_backfilled_at` stamp, DISTINCT from slice 0's `artist_edges_backfilled_at` (never disturbed). The ~14.3k residual drains at ~40/tick over ~a day and a half. Zero LLM tokens. Source: docs/agents/hermes/scripts/artist-credits-sweep.*. See docs/artist-relationship.md.",
|
|
3468
|
+
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-artist-credits", kind: "cron" },
|
|
3469
|
+
statusDescription: "mints artists from MusicBrainz credits for the residual",
|
|
3470
|
+
title: "Artist credits",
|
|
3471
|
+
weights: { status: "hidden" }
|
|
3472
|
+
},
|
|
3460
3473
|
{
|
|
3461
3474
|
command: "fluncle admin backfills label-lineage",
|
|
3462
3475
|
exposedContent: [
|
|
@@ -4382,6 +4395,7 @@ __export(exports_admin_tracks, {
|
|
|
4382
4395
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
4383
4396
|
backfillDiscogsCommand: () => backfillDiscogsCommand,
|
|
4384
4397
|
backfillArtistEdgesCommand: () => backfillArtistEdgesCommand,
|
|
4398
|
+
backfillArtistCreditsCommand: () => backfillArtistCreditsCommand,
|
|
4385
4399
|
backfillAppleMusicCommand: () => backfillAppleMusicCommand,
|
|
4386
4400
|
backfillAppleCatalogueCommand: () => backfillAppleCatalogueCommand
|
|
4387
4401
|
});
|
|
@@ -4581,6 +4595,13 @@ async function backfillArtistEdgesCommand(limit, dryRun, cursor) {
|
|
|
4581
4595
|
}
|
|
4582
4596
|
return adminApiPost(`/api/admin/backfill/artist-edges?${params.toString()}`);
|
|
4583
4597
|
}
|
|
4598
|
+
async function backfillArtistCreditsCommand(limit, dryRun, cursor) {
|
|
4599
|
+
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
4600
|
+
if (cursor) {
|
|
4601
|
+
params.set("cursor", cursor);
|
|
4602
|
+
}
|
|
4603
|
+
return adminApiPost(`/api/admin/backfill/artist-credits?${params.toString()}`);
|
|
4604
|
+
}
|
|
4584
4605
|
async function vehiclesCommand(limit) {
|
|
4585
4606
|
const tracks = await fetchAdminTracks({ hasVideo: true, max: limit, order: "desc" });
|
|
4586
4607
|
return tracks.map((track) => ({
|
|
@@ -10014,6 +10035,10 @@ function addAdminCommands(program2) {
|
|
|
10014
10035
|
const { backfillArtistEdgesCommand: backfillArtistEdgesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
10015
10036
|
await runBackfillArtistEdges(options, backfillArtistEdgesCommand2);
|
|
10016
10037
|
});
|
|
10038
|
+
backfill.command("artist-credits").description("Mint identity-true artists from MusicBrainz credits for the zero-matched residual").option("--dry-run", "Report the eligible worklist without any vendor call or write", false).option("--limit <limit>", "Max worklist rows to visit", "40").option("--json", "Print JSON", false).action(async (options) => {
|
|
10039
|
+
const { backfillArtistCreditsCommand: backfillArtistCreditsCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
10040
|
+
await runBackfillArtistCredits(options, backfillArtistCreditsCommand2);
|
|
10041
|
+
});
|
|
10017
10042
|
backfill.command("cover-masters").description("Resolve owned \u22641200\xB2 cover masters (album/artist) into R2").option("--kind <kind>", "Which entity to drain: album | artist", "album").option("--dry-run", "Report the eligible worklist without any fetch or write", false).option("--retry-none", "First re-queue a bounded batch of terminal none rows to pending, then run the pass", false).option("--limit <limit>", "Max entities to process", "50").option("--json", "Print JSON", false).action(async (options) => {
|
|
10018
10043
|
const { backfillCoverMastersCommand: backfillCoverMastersCommand2 } = await Promise.resolve().then(() => (init_admin_covers(), exports_admin_covers));
|
|
10019
10044
|
await runBackfillCoverMasters(options, backfillCoverMastersCommand2);
|
|
@@ -11061,6 +11086,57 @@ async function runBackfillArtistEdges(options, backfillArtistEdgesCommand2) {
|
|
|
11061
11086
|
const verb = dryRun ? "Would write" : "Wrote";
|
|
11062
11087
|
console.log(`${verb} ${edgesWritten} track_artists edge(s) over ${scanned} track(s): ${fullyMatched.length} fully matched, ${partiallyMatched.length} partially, ${zeroMatched.length} with none; ${unmatchedNames} credited name(s) left unmatched.`);
|
|
11063
11088
|
}
|
|
11089
|
+
async function runBackfillArtistCredits(options, backfillArtistCreditsCommand2) {
|
|
11090
|
+
const limit = parseListLimit(options.limit) ?? 40;
|
|
11091
|
+
let cursor;
|
|
11092
|
+
let dryRun = options.dryRun;
|
|
11093
|
+
let scanned = 0;
|
|
11094
|
+
let mintedArtists = 0;
|
|
11095
|
+
let matchedArtists = 0;
|
|
11096
|
+
let adoptedArtists = 0;
|
|
11097
|
+
let edgesWritten = 0;
|
|
11098
|
+
let skippedNoIdentity = 0;
|
|
11099
|
+
let throttled = false;
|
|
11100
|
+
while (scanned < limit) {
|
|
11101
|
+
const remaining = limit - scanned;
|
|
11102
|
+
const result = await backfillArtistCreditsCommand2(remaining, options.dryRun, cursor);
|
|
11103
|
+
dryRun = result.dryRun;
|
|
11104
|
+
scanned += result.scanned;
|
|
11105
|
+
mintedArtists += result.mintedArtists;
|
|
11106
|
+
matchedArtists += result.matchedArtists;
|
|
11107
|
+
adoptedArtists += result.adoptedArtists;
|
|
11108
|
+
edgesWritten += result.edgesWritten;
|
|
11109
|
+
skippedNoIdentity += result.skippedNoIdentity;
|
|
11110
|
+
if (!options.json) {
|
|
11111
|
+
const verb2 = result.dryRun ? "would visit" : "visited";
|
|
11112
|
+
console.log(` \u2026${verb2} ${result.scanned}; minted ${result.mintedArtists}, adopted ${result.adoptedArtists}, matched ${result.matchedArtists} artist(s); wrote ${result.edgesWritten} edge(s); ${result.skippedNoIdentity} without a MusicBrainz identity`);
|
|
11113
|
+
}
|
|
11114
|
+
if (result.rateLimited) {
|
|
11115
|
+
throttled = true;
|
|
11116
|
+
break;
|
|
11117
|
+
}
|
|
11118
|
+
if (result.nextCursor === null) {
|
|
11119
|
+
break;
|
|
11120
|
+
}
|
|
11121
|
+
cursor = result.nextCursor;
|
|
11122
|
+
}
|
|
11123
|
+
if (options.json) {
|
|
11124
|
+
printJson({
|
|
11125
|
+
adoptedArtists,
|
|
11126
|
+
dryRun,
|
|
11127
|
+
edgesWritten,
|
|
11128
|
+
matchedArtists,
|
|
11129
|
+
mintedArtists,
|
|
11130
|
+
ok: true,
|
|
11131
|
+
rateLimited: throttled,
|
|
11132
|
+
scanned,
|
|
11133
|
+
skippedNoIdentity
|
|
11134
|
+
});
|
|
11135
|
+
return;
|
|
11136
|
+
}
|
|
11137
|
+
const verb = dryRun ? "Would visit" : "Visited";
|
|
11138
|
+
console.log(`${verb} ${scanned} track(s): minted ${mintedArtists}, adopted ${adoptedArtists}, matched ${matchedArtists} artist(s); wrote ${edgesWritten} track_artists edge(s); ${skippedNoIdentity} carried no MusicBrainz identity.`);
|
|
11139
|
+
}
|
|
11064
11140
|
async function runArtistResolveQueue(options, listArtistsCommand2) {
|
|
11065
11141
|
const limit = parseListLimit(options.limit);
|
|
11066
11142
|
const result = await listArtistsCommand2(limit ?? 50);
|
package/package.json
CHANGED