fluncle 0.179.0 → 0.181.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 +33 -97
- 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.181.0".trim() ? "0.181.0".trim() : "0.1.0";
|
|
565
565
|
});
|
|
566
566
|
|
|
567
567
|
// src/update-notifier.ts
|
|
@@ -2260,7 +2260,7 @@ function parseVersion2(version) {
|
|
|
2260
2260
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2261
2261
|
var init_version2 = __esm(() => {
|
|
2262
2262
|
init_output();
|
|
2263
|
-
currentVersion2 = "0.
|
|
2263
|
+
currentVersion2 = "0.181.0".trim() ? "0.181.0".trim() : "0.1.0";
|
|
2264
2264
|
});
|
|
2265
2265
|
|
|
2266
2266
|
// ../../packages/registry/src/index.ts
|
|
@@ -2504,6 +2504,14 @@ var init_src = __esm(() => {
|
|
|
2504
2504
|
url: `${SITE}/privacy`,
|
|
2505
2505
|
weights: { web: "tertiary" }
|
|
2506
2506
|
},
|
|
2507
|
+
{
|
|
2508
|
+
exposedContent: ["the terms of use"],
|
|
2509
|
+
kind: "web_route",
|
|
2510
|
+
name: "web.terms",
|
|
2511
|
+
route: "/terms",
|
|
2512
|
+
url: `${SITE}/terms`,
|
|
2513
|
+
weights: { web: "tertiary" }
|
|
2514
|
+
},
|
|
2507
2515
|
{
|
|
2508
2516
|
exposedContent: ["the Galaxy game's front door (root rewrites to /galaxy)"],
|
|
2509
2517
|
kind: "subdomain",
|
|
@@ -3121,13 +3129,12 @@ var init_src = __esm(() => {
|
|
|
3121
3129
|
weights: { status: "hidden" }
|
|
3122
3130
|
},
|
|
3123
3131
|
{
|
|
3124
|
-
command: "fluncle admin backfills label-releases",
|
|
3125
3132
|
exposedContent: [
|
|
3126
|
-
"tap
|
|
3133
|
+
"tap day-one fresh releases for the operator's enabled seed labels → uncertified catalogue rows"
|
|
3127
3134
|
],
|
|
3128
3135
|
kind: "cron",
|
|
3129
3136
|
name: "cron.label-releases",
|
|
3130
|
-
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
|
|
3137
|
+
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 mints METADATA-ONLY catalogue rows (a `tracks` row with no `findings` row) for each ENABLED seed label's fresh releases with their real dates — closing the /fresh lag cliff. Runs OFF the official (dev-mode) Spotify app, which is rate-limited to death at its tier and must stay for user paths: the box reads the due seed labels (`list_label_releases_work`), runs an Apify Spotify-scraper actor per label (`label:\"<name>\" tag:new`), maps each result album (inline tracks[] + ISRCs + artists[]) to a candidate, and POSTs them to the agent-tier `backfill_label_releases` op — where the WORKER re-runs the FULL gate (the box's verdict is never trusted) and mints. The gate: artist-grounding (an album's Spotify artist already in `artists.spotify_artist_id` — the PRIMARY anchor that stops cross-genre homonym junk, always required) AND, when the actor gives a label signal (album_label/copyright — both null in albums-search mode, so grounding-only there), an exact fold-match to the seed name. It certifies nothing, publishes nothing, 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). Reuses the anchor sweep's APIFY_API_TOKEN (no new secret). Calls the oRPC HTTP endpoints directly (no CLI command the pinned box CLI would lack). Zero LLM tokens. Source: docs/agents/hermes/scripts/label-releases-sweep.*. See docs/catalogue-crawler.md.",
|
|
3131
3138
|
probeConfig: {
|
|
3132
3139
|
cadenceMs: 24 * 60 * MINUTE_MS,
|
|
3133
3140
|
cronName: "fluncle-label-releases",
|
|
@@ -4128,7 +4135,6 @@ __export(exports_admin_tracks, {
|
|
|
4128
4135
|
captureQueueCommand: () => captureQueueCommand,
|
|
4129
4136
|
backfillRecordingMbidsCommand: () => backfillRecordingMbidsCommand,
|
|
4130
4137
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
4131
|
-
backfillLabelReleasesCommand: () => backfillLabelReleasesCommand,
|
|
4132
4138
|
backfillDiscogsCommand: () => backfillDiscogsCommand,
|
|
4133
4139
|
backfillAppleMusicCommand: () => backfillAppleMusicCommand,
|
|
4134
4140
|
backfillAppleCatalogueCommand: () => backfillAppleCatalogueCommand
|
|
@@ -4315,10 +4321,6 @@ async function backfillAppleCatalogueCommand(limit, dryRun) {
|
|
|
4315
4321
|
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
4316
4322
|
return adminApiPost(`/api/admin/backfill/apple-catalogue?${params.toString()}`);
|
|
4317
4323
|
}
|
|
4318
|
-
async function backfillLabelReleasesCommand(limit, dryRun) {
|
|
4319
|
-
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
4320
|
-
return adminApiPost(`/api/admin/backfill/label-releases?${params.toString()}`);
|
|
4321
|
-
}
|
|
4322
4324
|
async function backfillRecordingMbidsCommand(limit, dryRun, cursor) {
|
|
4323
4325
|
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
4324
4326
|
if (cursor) {
|
|
@@ -6114,6 +6116,23 @@ var init_mixtape_mixcloud2 = __esm(() => {
|
|
|
6114
6116
|
PICTURE_MAX_BYTES2 = 10 * 1024 * 1024;
|
|
6115
6117
|
});
|
|
6116
6118
|
|
|
6119
|
+
// src/commands/auth-tiktok.ts
|
|
6120
|
+
var exports_auth_tiktok = {};
|
|
6121
|
+
__export(exports_auth_tiktok, {
|
|
6122
|
+
authTikTokCommand: () => authTikTokCommand
|
|
6123
|
+
});
|
|
6124
|
+
async function authTikTokCommand() {
|
|
6125
|
+
const response = await adminApiGet("/api/admin/tiktok/auth/start");
|
|
6126
|
+
console.log(`Open this TikTok authorization URL:
|
|
6127
|
+
|
|
6128
|
+
${response.authUrl}
|
|
6129
|
+
|
|
6130
|
+
After approving access, TikTok returns to the Fluncle admin callback and stores the refresh token server-side.`);
|
|
6131
|
+
}
|
|
6132
|
+
var init_auth_tiktok = __esm(() => {
|
|
6133
|
+
init_api();
|
|
6134
|
+
});
|
|
6135
|
+
|
|
6117
6136
|
// src/commands/auth-lastfm.ts
|
|
6118
6137
|
var exports_auth_lastfm = {};
|
|
6119
6138
|
__export(exports_auth_lastfm, {
|
|
@@ -9439,6 +9458,10 @@ function addAdminCommands(program2) {
|
|
|
9439
9458
|
const { authMixcloudCommand: authMixcloudCommand3 } = await Promise.resolve().then(() => (init_mixtape_mixcloud2(), exports_mixtape_mixcloud2));
|
|
9440
9459
|
await authMixcloudCommand3();
|
|
9441
9460
|
});
|
|
9461
|
+
auth.command("tiktok").description("Authorize TikTok access (Display API per-video metrics)").action(async () => {
|
|
9462
|
+
const { authTikTokCommand: authTikTokCommand2 } = await Promise.resolve().then(() => (init_auth_tiktok(), exports_auth_tiktok));
|
|
9463
|
+
await authTikTokCommand2();
|
|
9464
|
+
});
|
|
9442
9465
|
auth.command("lastfm").description("Authorize Last.fm access (love-on-add). Run once for the URL, then again with --token").option("--token <token>", "The approved request token, to mint the session key").action(async (options) => {
|
|
9443
9466
|
const { authLastfmCommand: authLastfmCommand2 } = await Promise.resolve().then(() => (init_auth_lastfm(), exports_auth_lastfm));
|
|
9444
9467
|
await authLastfmCommand2(options);
|
|
@@ -9698,10 +9721,6 @@ function addAdminCommands(program2) {
|
|
|
9698
9721
|
const { backfillAppleCatalogueCommand: backfillAppleCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
9699
9722
|
await runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2);
|
|
9700
9723
|
});
|
|
9701
|
-
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) => {
|
|
9702
|
-
const { backfillLabelReleasesCommand: backfillLabelReleasesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
9703
|
-
await runBackfillLabelReleases(options, backfillLabelReleasesCommand2);
|
|
9704
|
-
});
|
|
9705
9724
|
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) => {
|
|
9706
9725
|
const { backfillArtistsCommand: backfillArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
9707
9726
|
await runBackfillArtists(options, backfillArtistsCommand2);
|
|
@@ -10395,89 +10414,6 @@ async function runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2
|
|
|
10395
10414
|
process.exitCode = 1;
|
|
10396
10415
|
}
|
|
10397
10416
|
}
|
|
10398
|
-
async function runBackfillLabelReleases(options, backfillLabelReleasesCommand2) {
|
|
10399
|
-
const perPass = parseListLimit(options.limit);
|
|
10400
|
-
const newTrackIds = [];
|
|
10401
|
-
const labelSlugs = [];
|
|
10402
|
-
const failedLabels = [];
|
|
10403
|
-
let dryRun = options.dryRun;
|
|
10404
|
-
let throttled = false;
|
|
10405
|
-
let configured = true;
|
|
10406
|
-
let labelsProbed = 0;
|
|
10407
|
-
let albumsSeen = 0;
|
|
10408
|
-
let albumsMatched = 0;
|
|
10409
|
-
let newRows = 0;
|
|
10410
|
-
let skippedKnown = 0;
|
|
10411
|
-
let skippedUngrounded = 0;
|
|
10412
|
-
let failedFetches = 0;
|
|
10413
|
-
let fetchCeilingHit = false;
|
|
10414
|
-
const MAX_PASSES = 100;
|
|
10415
|
-
for (let pass = 0;pass < MAX_PASSES; pass += 1) {
|
|
10416
|
-
const result = await backfillLabelReleasesCommand2(perPass, options.dryRun);
|
|
10417
|
-
dryRun = result.dryRun;
|
|
10418
|
-
configured = result.configured;
|
|
10419
|
-
labelsProbed += result.labelsProbed;
|
|
10420
|
-
albumsSeen += result.albumsSeen;
|
|
10421
|
-
albumsMatched += result.albumsMatched;
|
|
10422
|
-
newRows += result.newRows;
|
|
10423
|
-
skippedKnown += result.skippedKnown;
|
|
10424
|
-
skippedUngrounded += result.skippedUngrounded;
|
|
10425
|
-
failedFetches += result.failedFetches;
|
|
10426
|
-
fetchCeilingHit = fetchCeilingHit || result.fetchCeilingHit;
|
|
10427
|
-
newTrackIds.push(...result.newTrackIds);
|
|
10428
|
-
labelSlugs.push(...result.labelSlugs);
|
|
10429
|
-
failedLabels.push(...result.failedLabels);
|
|
10430
|
-
if (!options.json) {
|
|
10431
|
-
const verb2 = result.dryRun ? "would probe" : "probed";
|
|
10432
|
-
console.log(` \u2026${verb2} ${result.labelSlugs.length} label(s); ${result.albumsMatched} matched album(s); ${result.newRows} new; ${result.skippedKnown} known; ${result.skippedUngrounded} ungrounded; ${result.failedFetches} fetch-fail; ${result.failedLabels.length} search-fail`);
|
|
10433
|
-
}
|
|
10434
|
-
if (!result.configured) {
|
|
10435
|
-
break;
|
|
10436
|
-
}
|
|
10437
|
-
if (result.rateLimited) {
|
|
10438
|
-
throttled = true;
|
|
10439
|
-
break;
|
|
10440
|
-
}
|
|
10441
|
-
if (result.dryRun) {
|
|
10442
|
-
break;
|
|
10443
|
-
}
|
|
10444
|
-
if (result.labelsProbed === 0) {
|
|
10445
|
-
break;
|
|
10446
|
-
}
|
|
10447
|
-
}
|
|
10448
|
-
if (options.json) {
|
|
10449
|
-
printSweepJson({
|
|
10450
|
-
albumsMatched,
|
|
10451
|
-
albumsSeen,
|
|
10452
|
-
configured,
|
|
10453
|
-
dryRun,
|
|
10454
|
-
failedFetches,
|
|
10455
|
-
failedLabels,
|
|
10456
|
-
fetchCeilingHit,
|
|
10457
|
-
labelSlugs,
|
|
10458
|
-
labelsProbed,
|
|
10459
|
-
newRows,
|
|
10460
|
-
newTrackIds,
|
|
10461
|
-
rateLimited: throttled,
|
|
10462
|
-
skippedKnown,
|
|
10463
|
-
skippedUngrounded
|
|
10464
|
-
}, 0);
|
|
10465
|
-
return;
|
|
10466
|
-
}
|
|
10467
|
-
if (!configured) {
|
|
10468
|
-
console.log("Label-releases tap is a no-op \u2014 the Worker's Spotify grant is gone; reconnect Spotify.");
|
|
10469
|
-
return;
|
|
10470
|
-
}
|
|
10471
|
-
const verb = dryRun ? "Would probe" : "Probed";
|
|
10472
|
-
const probedCount = dryRun ? labelSlugs.length : labelsProbed;
|
|
10473
|
-
console.log(`${verb} ${probedCount} enabled seed label(s); ${albumsMatched} matched album(s); ${newRows} new catalogue row(s); ${skippedKnown} already known; ${skippedUngrounded} dropped for no known artist; ${failedFetches} fetch-fail; ${failedLabels.length} search-fail.`);
|
|
10474
|
-
if (fetchCeilingHit) {
|
|
10475
|
-
console.log(" (a pass ended early on the single-fetch ceiling \u2014 resumes next tick)");
|
|
10476
|
-
}
|
|
10477
|
-
for (const slug of failedLabels) {
|
|
10478
|
-
console.log(` search error: ${slug}`);
|
|
10479
|
-
}
|
|
10480
|
-
}
|
|
10481
10417
|
async function runBackfillArtists(options, backfillArtistsCommand2) {
|
|
10482
10418
|
const limit = parseListLimit(options.limit);
|
|
10483
10419
|
const upserted = [];
|
package/package.json
CHANGED