fluncle 0.261.0 → 0.262.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 +79 -10
- 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.
|
|
625
|
+
currentVersion = "0.262.0".trim() ? "0.262.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.
|
|
2338
|
+
currentVersion2 = "0.262.0".trim() ? "0.262.0".trim() : "0.1.0";
|
|
2339
2339
|
});
|
|
2340
2340
|
|
|
2341
2341
|
// ../../packages/registry/src/index.ts
|
|
@@ -3459,12 +3459,12 @@ var init_src = __esm(() => {
|
|
|
3459
3459
|
],
|
|
3460
3460
|
kind: "cron",
|
|
3461
3461
|
name: "cron.label-releases",
|
|
3462
|
-
operatorNotes: "
|
|
3462
|
+
operatorNotes: "every 15 minutes from 03:00 through 08:45 UTC, run by a Hermes 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. The WORKER does all of it (`backfill_label_releases`, agent tier): it searches the official Spotify API (`label:\"<name>\" tag:new`), reads each hit as a SINGLE `GET /albums/{id}` then `GET /tracks/{id}` (the batch endpoints are 403 at our tier), and mints. The box sweep is a thin HTTP TRIGGER that POSTs bounded passes with the agent token — no vendor token, no CLI dependency (a pinned box CLI missing a flag broke an earlier run). The gate, both required: artist-grounding (an album's Spotify artist already in `artists.spotify_artist_id` — the PRIMARY anchor that stops cross-genre homonym junk) AND an EXACT fold-match of the seed name in the ℗/© copyright; an album with no release_date is dropped outright (/fresh could never show it). BUDGET: the tap shares the official app's per-app window with the user-facing paths, so it paces itself against the shared call meter and stops at a FRACTION of the window (its own ceiling) — user write paths get the window, the tap takes only slack. Hitting that ceiling ends the pass cleanly and the durable per-label cadence stamps resume it next tick. 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). No vendor spend and zero LLM tokens. Source: docs/agents/hermes/scripts/label-releases-sweep.*. See docs/catalogue-crawler.md.",
|
|
3463
3463
|
probeConfig: {
|
|
3464
3464
|
cadenceMs: 24 * 60 * MINUTE_MS,
|
|
3465
3465
|
cronName: "fluncle-label-releases",
|
|
3466
3466
|
kind: "cron",
|
|
3467
|
-
schedule: { time: "
|
|
3467
|
+
schedule: { time: "03:00", tz: "UTC" }
|
|
3468
3468
|
},
|
|
3469
3469
|
statusDescription: "taps day-one releases from the enabled labels",
|
|
3470
3470
|
title: "Freshness tap",
|
|
@@ -10193,7 +10193,7 @@ var init_zod = __esm(() => {
|
|
|
10193
10193
|
});
|
|
10194
10194
|
|
|
10195
10195
|
// ../../packages/contracts/src/orpc/admin-catalogue.ts
|
|
10196
|
-
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, CrawlFetchPlanSchema, SuppliedCrawlBodySchema, MAX_CRAWL_COMMIT_BATCH, CRAWL_COMMIT_TOKEN_MAX_BYTES, CRAWL_COMMIT_BATCH_MAX_TOTAL_BYTES, CrawlCommitItemSchema, CrawlCommitReceiptSchema, CrawlPhaseCapabilitiesSchema, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, CrawlPipelineSummarySchema, PipelineWatchBoundedCountSchema, getPipeline, crawlCatalogue, commitCrawlNodes, 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, recordAnchorFailure, 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, AnchorApifyBudgetSchema, getAnchorApifyBudget, setAnchorApifyBudget, SpotifyAnchorBreakerStateSchema, AnchorRungFlagsSchema, getSpotifyAnchorBreaker, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
|
|
10196
|
+
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, CrawlFetchPlanSchema, SuppliedCrawlBodySchema, MAX_CRAWL_COMMIT_BATCH, CRAWL_COMMIT_TOKEN_MAX_BYTES, CRAWL_COMMIT_BATCH_MAX_TOTAL_BYTES, CrawlCommitItemSchema, CrawlCommitReceiptSchema, CrawlPhaseCapabilitiesSchema, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, CrawlPipelineSummarySchema, PipelineWatchBoundedCountSchema, getPipeline, crawlCatalogue, commitCrawlNodes, 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, recordAnchorFailure, 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, AnchorApifyBudgetSchema, getAnchorApifyBudget, setAnchorApifyBudget, SpotifyAnchorBreakerStateSchema, AnchorRungFlagsSchema, getSpotifyAnchorBreaker, LabelReleasesBudgetSchema, getLabelReleasesBudget, setLabelReleasesBudget, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
|
|
10197
10197
|
var init_admin_catalogue = __esm(() => {
|
|
10198
10198
|
init_dist3();
|
|
10199
10199
|
init_zod();
|
|
@@ -10826,7 +10826,30 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10826
10826
|
path: "/admin/catalogue/anchor/breaker",
|
|
10827
10827
|
summary: "The Spotify anchor-search throttle breaker: tripped, why, how long left, and the rungs",
|
|
10828
10828
|
tags: ["Admin"]
|
|
10829
|
-
}).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({
|
|
10829
|
+
}).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({
|
|
10830
|
+
ok: literal(true),
|
|
10831
|
+
rungs: AnchorRungFlagsSchema,
|
|
10832
|
+
spotifyDailyCalls: number2().int().nullable()
|
|
10833
|
+
}));
|
|
10834
|
+
LabelReleasesBudgetSchema = object({
|
|
10835
|
+
callsSpent: number2().int().nonnegative(),
|
|
10836
|
+
dailyBudget: number2().int().nonnegative(),
|
|
10837
|
+
spotifyDailyCalls: number2().int().nonnegative()
|
|
10838
|
+
});
|
|
10839
|
+
getLabelReleasesBudget = oc.route({
|
|
10840
|
+
method: "GET",
|
|
10841
|
+
operationId: "getLabelReleasesBudget",
|
|
10842
|
+
path: "/admin/catalogue/label-releases-budget",
|
|
10843
|
+
summary: "Read the label releases tap daily budget and Spotify usage",
|
|
10844
|
+
tags: ["Admin"]
|
|
10845
|
+
}).input(object({})).output(LabelReleasesBudgetSchema.extend({ ok: literal(true) }));
|
|
10846
|
+
setLabelReleasesBudget = oc.route({
|
|
10847
|
+
method: "PUT",
|
|
10848
|
+
operationId: "setLabelReleasesBudget",
|
|
10849
|
+
path: "/admin/catalogue/label-releases-budget",
|
|
10850
|
+
summary: "Set the label releases tap daily budget (operator)",
|
|
10851
|
+
tags: ["Admin"]
|
|
10852
|
+
}).input(object({ dailyBudget: number2().int().min(0).max(1e6) })).output(LabelReleasesBudgetSchema.extend({ ok: literal(true) }));
|
|
10830
10853
|
resetSpotifyAnchorBreaker = oc.route({
|
|
10831
10854
|
method: "POST",
|
|
10832
10855
|
operationId: "resetSpotifyAnchorBreaker",
|
|
@@ -10858,6 +10881,7 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10858
10881
|
get_anchor_apify_budget: getAnchorApifyBudget,
|
|
10859
10882
|
get_capture_budget: getCaptureBudget,
|
|
10860
10883
|
get_crawl_status: getCrawlStatus,
|
|
10884
|
+
get_label_releases_budget: getLabelReleasesBudget,
|
|
10861
10885
|
get_pipeline: getPipeline,
|
|
10862
10886
|
get_spotify_anchor_breaker: getSpotifyAnchorBreaker,
|
|
10863
10887
|
list_catalogue_tracks: listCatalogueTracks,
|
|
@@ -10876,6 +10900,7 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10876
10900
|
set_anchor_apify_budget: setAnchorApifyBudget,
|
|
10877
10901
|
set_anchor_search: setAnchorSearch,
|
|
10878
10902
|
set_capture_budget: setCaptureBudget,
|
|
10903
|
+
set_label_releases_budget: setLabelReleasesBudget,
|
|
10879
10904
|
set_track_dismissed: setTrackDismissed,
|
|
10880
10905
|
verify_capture: verifyCapture
|
|
10881
10906
|
};
|
|
@@ -11928,6 +11953,13 @@ var init_admin_backfills = __esm(() => {
|
|
|
11928
11953
|
})).output(object({
|
|
11929
11954
|
albumsMatched: number2(),
|
|
11930
11955
|
albumsSeen: number2(),
|
|
11956
|
+
blockedReason: _enum([
|
|
11957
|
+
"spotify_breaker",
|
|
11958
|
+
"spotify_budget",
|
|
11959
|
+
"spotify_budget_spent",
|
|
11960
|
+
"spotify_quota",
|
|
11961
|
+
"spotify_throttle"
|
|
11962
|
+
]).nullable(),
|
|
11931
11963
|
budgetPaused: boolean2(),
|
|
11932
11964
|
configured: boolean2(),
|
|
11933
11965
|
dryRun: boolean2(),
|
|
@@ -11935,14 +11967,20 @@ var init_admin_backfills = __esm(() => {
|
|
|
11935
11967
|
failedLabels: array(string2()),
|
|
11936
11968
|
fetchCeilingHit: boolean2(),
|
|
11937
11969
|
labelSlugs: array(string2()),
|
|
11970
|
+
labelsDue: number2(),
|
|
11938
11971
|
labelsProbed: number2(),
|
|
11972
|
+
neverChecked: number2(),
|
|
11939
11973
|
newRows: number2(),
|
|
11940
11974
|
newTrackIds: array(string2()),
|
|
11941
11975
|
ok: literal(true),
|
|
11976
|
+
quotaExceeded: boolean2(),
|
|
11942
11977
|
rateLimited: boolean2(),
|
|
11978
|
+
retryAfterMs: number2(),
|
|
11943
11979
|
skippedKnown: number2(),
|
|
11944
11980
|
skippedUndated: number2(),
|
|
11945
11981
|
skippedUngrounded: number2(),
|
|
11982
|
+
tapDailyBudget: number2().int(),
|
|
11983
|
+
tapDailyCallsSpent: number2().int(),
|
|
11946
11984
|
tracksSkippedArtistRule: number2().optional()
|
|
11947
11985
|
}));
|
|
11948
11986
|
LabelImagesBackfillFailedSchema = object({
|
|
@@ -13602,7 +13640,7 @@ var init_admin_hub_counts = __esm(() => {
|
|
|
13602
13640
|
});
|
|
13603
13641
|
|
|
13604
13642
|
// ../../packages/contracts/src/orpc/admin-labels.ts
|
|
13605
|
-
var LabelSeedStateSchema, LabelTriageVerdictSchema, LabelAdminItemSchema, listLabelsAdmin, updateLabel, listLabelArtistRules, replaceLabelArtistRules, MergeLabelResultSchema, mergeLabel, MB_LABEL_MBID_PATTERN, MintLabelOutcomeSchema, LabelTakeOverResultSchema, mintLabel, LabelAliasSourceSchema, LabelAliasKindSchema, LabelAliasCandidateSchema, listLabelAliases, confirmLabelAlias, rejectLabelAlias, DescribeLabelBodySchema, describeLabel, draftLabelBio, LabelBioWorkItemSchema, listLabelsMissingBio, TriageRuleProposalSchema, RecordLabelTriageBodySchema, recordLabelTriage, adminLabelsContract;
|
|
13643
|
+
var LabelSeedStateSchema, LabelTriageVerdictSchema, LabelAdminItemSchema, listLabelsAdmin, updateLabel, listLabelArtistRules, LABEL_ARTIST_RULES_MAX = 100, LABEL_TRIAGE_RULE_PROPOSALS_MAX, replaceLabelArtistRules, MergeLabelResultSchema, mergeLabel, MB_LABEL_MBID_PATTERN, MintLabelOutcomeSchema, LabelTakeOverResultSchema, mintLabel, LabelAliasSourceSchema, LabelAliasKindSchema, LabelAliasCandidateSchema, listLabelAliases, confirmLabelAlias, rejectLabelAlias, DescribeLabelBodySchema, describeLabel, draftLabelBio, LabelBioWorkItemSchema, listLabelsMissingBio, TriageRuleProposalSchema, RecordLabelTriageBodySchema, recordLabelTriage, adminLabelsContract;
|
|
13606
13644
|
var init_admin_labels = __esm(() => {
|
|
13607
13645
|
init_dist3();
|
|
13608
13646
|
init_zod();
|
|
@@ -13655,6 +13693,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13655
13693
|
summary: "List one label's artist rules for future catalogue acquisition",
|
|
13656
13694
|
tags: ["Admin"]
|
|
13657
13695
|
}).input(object({ id: string2() })).output(object({ ok: literal(true), rules: array(ArtistRuleSchema) }));
|
|
13696
|
+
LABEL_TRIAGE_RULE_PROPOSALS_MAX = LABEL_ARTIST_RULES_MAX * 2;
|
|
13658
13697
|
replaceLabelArtistRules = oc.route({
|
|
13659
13698
|
method: "PUT",
|
|
13660
13699
|
operationId: "replaceLabelArtistRules",
|
|
@@ -13663,7 +13702,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13663
13702
|
tags: ["Admin"]
|
|
13664
13703
|
}).input(object({
|
|
13665
13704
|
id: string2(),
|
|
13666
|
-
rules: array(ArtistRuleInputSchema).max(
|
|
13705
|
+
rules: array(ArtistRuleInputSchema).max(LABEL_ARTIST_RULES_MAX).superRefine((rules, context) => {
|
|
13667
13706
|
const seen = new Set;
|
|
13668
13707
|
for (const [index, rule] of rules.entries()) {
|
|
13669
13708
|
if (seen.has(rule.artistMbid)) {
|
|
@@ -13820,7 +13859,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13820
13859
|
reason: string2().optional(),
|
|
13821
13860
|
residualOffLaneShare: number2().min(0).max(1).optional(),
|
|
13822
13861
|
roundId: string2(),
|
|
13823
|
-
rules: array(TriageRuleProposalSchema).optional(),
|
|
13862
|
+
rules: array(TriageRuleProposalSchema).max(LABEL_TRIAGE_RULE_PROPOSALS_MAX).optional(),
|
|
13824
13863
|
verdict: LabelTriageVerdictSchema,
|
|
13825
13864
|
verifyAgrees: boolean2().optional(),
|
|
13826
13865
|
verifyEvidence: string2().optional()
|
|
@@ -15435,7 +15474,7 @@ var init_admin_tracks = __esm(() => {
|
|
|
15435
15474
|
debtAware: string2().optional(),
|
|
15436
15475
|
kind: TrackWorkKindSchema,
|
|
15437
15476
|
limit: number3().int().min(1).max(250).default(50),
|
|
15438
|
-
paidMode: _enum(["quota", "prior"]).optional(),
|
|
15477
|
+
paidMode: _enum(["quota", "prior", "unasked"]).optional(),
|
|
15439
15478
|
scope: TrackWorkScopeSchema.default("all")
|
|
15440
15479
|
})).output(object({
|
|
15441
15480
|
capabilities: TrackWorkCapabilitiesSchema.optional(),
|
|
@@ -19297,6 +19336,12 @@ async function requeueAnchorCommand(trackIds) {
|
|
|
19297
19336
|
async function requeueIsrcRecoveryCommand(input) {
|
|
19298
19337
|
return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
|
|
19299
19338
|
}
|
|
19339
|
+
async function labelReleasesBudgetCommand() {
|
|
19340
|
+
return adminApiGet("/api/v1/admin/catalogue/label-releases-budget");
|
|
19341
|
+
}
|
|
19342
|
+
async function setLabelReleasesBudgetCommand(dailyBudget) {
|
|
19343
|
+
return adminApiPut("/api/v1/admin/catalogue/label-releases-budget", { dailyBudget });
|
|
19344
|
+
}
|
|
19300
19345
|
async function anchorApifyBudgetCommand() {
|
|
19301
19346
|
return adminApiGet("/api/v1/admin/catalogue/anchor/apify-budget");
|
|
19302
19347
|
}
|
|
@@ -23088,6 +23133,29 @@ JSON field reference:
|
|
|
23088
23133
|
}
|
|
23089
23134
|
console.log(`Re-queued ${requeued} of ${trackIds.length} row(s) \u2014 the next anchor tick picks them up at their real priority.`);
|
|
23090
23135
|
});
|
|
23136
|
+
const labelReleasesBudget = catalogue.command("label-releases-budget").description("Read or set the label releases tap daily Spotify call budget");
|
|
23137
|
+
labelReleasesBudget.option("--json", "Print JSON", false).action(async (options) => {
|
|
23138
|
+
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23139
|
+
const state = await labelReleasesBudgetCommand();
|
|
23140
|
+
if (options.json) {
|
|
23141
|
+
printJson({ ...state, ok: true });
|
|
23142
|
+
return;
|
|
23143
|
+
}
|
|
23144
|
+
console.log(`LABEL RELEASES BUDGET \u2014 ${state.callsSpent}/${state.dailyBudget} tap calls today \xB7 ${state.spotifyDailyCalls} Spotify calls total`);
|
|
23145
|
+
});
|
|
23146
|
+
labelReleasesBudget.command("set").requiredOption("--calls <count>", "Daily tap Spotify call budget").option("--json", "Print JSON", false).action(async (options) => {
|
|
23147
|
+
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23148
|
+
const calls = Number(options.calls);
|
|
23149
|
+
if (!Number.isSafeInteger(calls) || calls < 0 || calls > 1e6) {
|
|
23150
|
+
throw new Error("--calls must be an integer from 0 to 1000000");
|
|
23151
|
+
}
|
|
23152
|
+
const state = await setLabelReleasesBudgetCommand(calls);
|
|
23153
|
+
if (options.json) {
|
|
23154
|
+
printJson({ ...state, ok: true });
|
|
23155
|
+
return;
|
|
23156
|
+
}
|
|
23157
|
+
console.log(`LABEL RELEASES BUDGET \u2014 ${state.callsSpent}/${state.dailyBudget} tap calls today \xB7 ${state.spotifyDailyCalls} Spotify calls total`);
|
|
23158
|
+
});
|
|
23091
23159
|
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) => {
|
|
23092
23160
|
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23093
23161
|
const state = await anchorBreakerCommand();
|
|
@@ -26432,6 +26500,7 @@ var stringOptions = new Set([
|
|
|
26432
26500
|
"--bpm",
|
|
26433
26501
|
"--bpm-confidence",
|
|
26434
26502
|
"--bpm-source",
|
|
26503
|
+
"--calls",
|
|
26435
26504
|
"--composition",
|
|
26436
26505
|
"--consumer-digest",
|
|
26437
26506
|
"--consumer-item-count",
|
package/package.json
CHANGED