fluncle 0.261.0 → 0.263.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 +238 -31
- 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.263.0".trim() ? "0.263.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.263.0".trim() ? "0.263.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, AnchorResolveResultSchema, AnchorCommitResultSchema, prepareAnchor, AnchorPhaseBatchItemFailureSchema, prepareAnchorBatch, resolveAnchorCandidate, commitAnchor, commitAnchorBatch, getAnchorReceipt, getAnchorPaidToken, cancelAnchorPaidResult, resolveAnchorPaidResult, 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();
|
|
@@ -10320,9 +10320,12 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10320
10320
|
method: "POST",
|
|
10321
10321
|
operationId: "requeueAnchor",
|
|
10322
10322
|
path: "/admin/catalogue/anchor/requeue",
|
|
10323
|
-
summary: "
|
|
10323
|
+
summary: "Requeue named anchor rows and optionally reconcile pending paid receipts (operator)",
|
|
10324
10324
|
tags: ["Admin"]
|
|
10325
|
-
}).input(object({
|
|
10325
|
+
}).input(object({
|
|
10326
|
+
clearPaid: boolean2().optional(),
|
|
10327
|
+
trackIds: array(string2().min(1)).min(1).max(250)
|
|
10328
|
+
})).output(object({ ok: literal(true), paidCleared: number2(), requeued: number2() }));
|
|
10326
10329
|
requeueIsrcRecovery = oc.route({
|
|
10327
10330
|
method: "POST",
|
|
10328
10331
|
operationId: "requeueIsrcRecovery",
|
|
@@ -10660,6 +10663,7 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10660
10663
|
tags: ["Admin"]
|
|
10661
10664
|
}).input(object({
|
|
10662
10665
|
candidates: array(AnchorCandidateSchema).max(ANCHOR_CANDIDATE_LIMIT).default([]),
|
|
10666
|
+
paidResultToken: string2().max(32 * 1024).optional(),
|
|
10663
10667
|
trackId: string2().min(1)
|
|
10664
10668
|
})).output(object({
|
|
10665
10669
|
anchored: boolean2(),
|
|
@@ -10680,22 +10684,12 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10680
10684
|
isrc: string2().max(DEEZER_ISRC_MAX),
|
|
10681
10685
|
title: string2().max(DEEZER_TEXT_MAX)
|
|
10682
10686
|
}).meta({ id: "DeezerIsrcCandidate" });
|
|
10683
|
-
|
|
10684
|
-
method: "POST",
|
|
10685
|
-
operationId: "resolveAnchor",
|
|
10686
|
-
path: "/admin/catalogue/anchor/resolve",
|
|
10687
|
-
summary: "Resolve a catalogue row's Spotify anchor from the free rungs (ListenBrainz + dark Spotify search)",
|
|
10688
|
-
tags: ["Admin"]
|
|
10689
|
-
}).input(object({
|
|
10690
|
-
deezerCandidates: array(DeezerIsrcCandidateSchema).max(DEEZER_CANDIDATE_LIMIT).optional(),
|
|
10691
|
-
spotifySearch: boolean2().optional(),
|
|
10692
|
-
trackId: string2().min(1)
|
|
10693
|
-
})).output(object({
|
|
10687
|
+
AnchorResolveResultSchema = object({
|
|
10694
10688
|
anchored: boolean2(),
|
|
10695
10689
|
apifyBudgetRemaining: number2().int().nonnegative(),
|
|
10696
10690
|
apifyEligible: boolean2(),
|
|
10697
10691
|
apifyEnabled: boolean2(),
|
|
10698
|
-
apifyIneligibleReason: _enum(["apify_budget_spent", "awaiting_free_ask"]).nullable(),
|
|
10692
|
+
apifyIneligibleReason: _enum(["apify_budget_spent", "awaiting_free_ask", "awaiting_paid_result"]).nullable(),
|
|
10699
10693
|
freeDurationMsOmitted: number2().int().nonnegative(),
|
|
10700
10694
|
isrcRecoveredByDeezer: boolean2(),
|
|
10701
10695
|
listenbrainzOutcome: _enum([
|
|
@@ -10710,6 +10704,7 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10710
10704
|
"yielded-on-breaker"
|
|
10711
10705
|
]),
|
|
10712
10706
|
ok: literal(true),
|
|
10707
|
+
paidResultToken: string2().optional(),
|
|
10713
10708
|
source: _enum(["listenbrainz", "spotify-isrc", "spotify-search"]).nullable(),
|
|
10714
10709
|
spotifyIsrcAsked: boolean2(),
|
|
10715
10710
|
spotifySearchDone: boolean2(),
|
|
@@ -10717,7 +10712,140 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10717
10712
|
spotifyThrottled: boolean2(),
|
|
10718
10713
|
stamped: boolean2(),
|
|
10719
10714
|
verifiedBy: _enum(["isrc", "search", "search-subset"]).nullable()
|
|
10715
|
+
});
|
|
10716
|
+
AnchorCommitResultSchema = AnchorResolveResultSchema.extend({
|
|
10717
|
+
paidReceiptPending: boolean2()
|
|
10718
|
+
});
|
|
10719
|
+
prepareAnchor = oc.route({
|
|
10720
|
+
method: "POST",
|
|
10721
|
+
operationId: "prepareAnchor",
|
|
10722
|
+
path: "/admin/catalogue/anchor/prepare",
|
|
10723
|
+
summary: "Prepare one anchor row for provider lookup",
|
|
10724
|
+
tags: ["Admin"]
|
|
10725
|
+
}).input(object({
|
|
10726
|
+
deezerCandidates: array(DeezerIsrcCandidateSchema).max(DEEZER_CANDIDATE_LIMIT).optional(),
|
|
10727
|
+
trackId: string2().min(1)
|
|
10728
|
+
})).output(object({ ok: literal(true), prepared: string2(), receiptAt: string2() }));
|
|
10729
|
+
AnchorPhaseBatchItemFailureSchema = object({
|
|
10730
|
+
elapsedMs: number2().nonnegative(),
|
|
10731
|
+
error: string2().optional(),
|
|
10732
|
+
httpStatus: number2().int().optional(),
|
|
10733
|
+
status: _enum(["deferred", "error"]),
|
|
10734
|
+
trackId: string2()
|
|
10735
|
+
});
|
|
10736
|
+
prepareAnchorBatch = oc.route({
|
|
10737
|
+
method: "POST",
|
|
10738
|
+
operationId: "prepareAnchorBatch",
|
|
10739
|
+
path: "/admin/catalogue/anchor/prepares",
|
|
10740
|
+
summary: "Prepare a bounded batch of anchor rows",
|
|
10741
|
+
tags: ["Admin"]
|
|
10742
|
+
}).input(object({
|
|
10743
|
+
items: array(object({
|
|
10744
|
+
deezerCandidates: array(DeezerIsrcCandidateSchema).max(DEEZER_CANDIDATE_LIMIT).optional(),
|
|
10745
|
+
trackId: string2().min(1)
|
|
10746
|
+
})).min(1).max(15)
|
|
10747
|
+
})).output(object({
|
|
10748
|
+
items: array(union([
|
|
10749
|
+
object({
|
|
10750
|
+
elapsedMs: number2().nonnegative(),
|
|
10751
|
+
prepared: string2(),
|
|
10752
|
+
receiptAt: string2(),
|
|
10753
|
+
status: literal("done"),
|
|
10754
|
+
trackId: string2()
|
|
10755
|
+
}),
|
|
10756
|
+
AnchorPhaseBatchItemFailureSchema
|
|
10757
|
+
])),
|
|
10758
|
+
ok: literal(true)
|
|
10759
|
+
}));
|
|
10760
|
+
resolveAnchorCandidate = oc.route({
|
|
10761
|
+
method: "POST",
|
|
10762
|
+
operationId: "resolveAnchorCandidate",
|
|
10763
|
+
path: "/admin/catalogue/anchor/candidates/resolve",
|
|
10764
|
+
summary: "Look up free anchor candidates from providers",
|
|
10765
|
+
tags: ["Admin"]
|
|
10766
|
+
}).input(object({ prepared: string2().max(32 * 1024), spotifySearch: boolean2().optional() })).output(object({ evidence: string2(), ok: literal(true) }));
|
|
10767
|
+
commitAnchor = oc.route({
|
|
10768
|
+
method: "POST",
|
|
10769
|
+
operationId: "commitAnchor",
|
|
10770
|
+
path: "/admin/catalogue/anchor/commit",
|
|
10771
|
+
summary: "Verify provider evidence and settle one anchor row",
|
|
10772
|
+
tags: ["Admin"]
|
|
10773
|
+
}).input(object({
|
|
10774
|
+
allowPaid: boolean2().optional(),
|
|
10775
|
+
evidence: string2().max(32 * 1024),
|
|
10776
|
+
prepared: string2().max(32 * 1024)
|
|
10777
|
+
})).output(AnchorCommitResultSchema);
|
|
10778
|
+
commitAnchorBatch = oc.route({
|
|
10779
|
+
method: "POST",
|
|
10780
|
+
operationId: "commitAnchorBatch",
|
|
10781
|
+
path: "/admin/catalogue/anchor/commits",
|
|
10782
|
+
summary: "Settle a bounded batch of anchor rows",
|
|
10783
|
+
tags: ["Admin"]
|
|
10784
|
+
}).input(object({
|
|
10785
|
+
items: array(object({
|
|
10786
|
+
allowPaid: boolean2().optional(),
|
|
10787
|
+
evidence: string2().max(32 * 1024),
|
|
10788
|
+
prepared: string2().max(32 * 1024),
|
|
10789
|
+
trackId: string2().min(1)
|
|
10790
|
+
})).min(1).max(15)
|
|
10791
|
+
})).output(object({
|
|
10792
|
+
items: array(union([
|
|
10793
|
+
AnchorCommitResultSchema.extend({
|
|
10794
|
+
elapsedMs: number2().nonnegative(),
|
|
10795
|
+
status: literal("done"),
|
|
10796
|
+
trackId: string2()
|
|
10797
|
+
}),
|
|
10798
|
+
AnchorPhaseBatchItemFailureSchema
|
|
10799
|
+
])),
|
|
10800
|
+
ok: literal(true)
|
|
10801
|
+
}));
|
|
10802
|
+
getAnchorReceipt = oc.route({
|
|
10803
|
+
method: "POST",
|
|
10804
|
+
operationId: "getAnchorReceipt",
|
|
10805
|
+
path: "/admin/catalogue/anchor/receipt",
|
|
10806
|
+
summary: "Read exact paid admission receipt status",
|
|
10807
|
+
tags: ["Admin"]
|
|
10808
|
+
}).input(object({ receiptAt: string2().min(1), trackId: string2().min(1) })).output(object({
|
|
10809
|
+
admitted: boolean2(),
|
|
10810
|
+
ok: literal(true),
|
|
10811
|
+
paidState: string2().nullable()
|
|
10720
10812
|
}));
|
|
10813
|
+
getAnchorPaidToken = oc.route({
|
|
10814
|
+
method: "POST",
|
|
10815
|
+
operationId: "getAnchorPaidToken",
|
|
10816
|
+
path: "/admin/catalogue/anchor/paid-result/token",
|
|
10817
|
+
summary: "Read a fresh token for an exact pending paid anchor receipt",
|
|
10818
|
+
tags: ["Admin"]
|
|
10819
|
+
}).input(object({ receiptAt: string2().min(1), trackId: string2().min(1) })).output(object({ ok: literal(true), paidResultToken: string2() }));
|
|
10820
|
+
cancelAnchorPaidResult = oc.route({
|
|
10821
|
+
method: "POST",
|
|
10822
|
+
operationId: "cancelAnchorPaidResult",
|
|
10823
|
+
path: "/admin/catalogue/anchor/paid-result/cancel",
|
|
10824
|
+
summary: "Settle a paid admission when its actor never started",
|
|
10825
|
+
tags: ["Admin"]
|
|
10826
|
+
}).input(object({
|
|
10827
|
+
paidResultToken: string2().max(32 * 1024),
|
|
10828
|
+
refundCap: boolean2().optional(),
|
|
10829
|
+
trackId: string2().min(1)
|
|
10830
|
+
})).output(object({ ok: literal(true), settled: literal(true) }));
|
|
10831
|
+
resolveAnchorPaidResult = oc.route({
|
|
10832
|
+
method: "POST",
|
|
10833
|
+
operationId: "resolveAnchorPaidResult",
|
|
10834
|
+
path: "/admin/catalogue/anchor/paid-result/resolve",
|
|
10835
|
+
summary: "Settle a paid receipt for a track no longer available to anchor",
|
|
10836
|
+
tags: ["Admin"]
|
|
10837
|
+
}).input(object({ receiptAt: string2().min(1), trackId: string2().min(1) })).output(object({ ok: literal(true), reason: _enum(["missing", "settled", "unavailable"]) }));
|
|
10838
|
+
resolveAnchor = oc.route({
|
|
10839
|
+
method: "POST",
|
|
10840
|
+
operationId: "resolveAnchor",
|
|
10841
|
+
path: "/admin/catalogue/anchor/resolve",
|
|
10842
|
+
summary: "Resolve a catalogue row's Spotify anchor from the free rungs (ListenBrainz + dark Spotify search)",
|
|
10843
|
+
tags: ["Admin"]
|
|
10844
|
+
}).input(object({
|
|
10845
|
+
deezerCandidates: array(DeezerIsrcCandidateSchema).max(DEEZER_CANDIDATE_LIMIT).optional(),
|
|
10846
|
+
spotifySearch: boolean2().optional(),
|
|
10847
|
+
trackId: string2().min(1)
|
|
10848
|
+
})).output(AnchorResolveResultSchema);
|
|
10721
10849
|
resolveAnchorReview = oc.route({
|
|
10722
10850
|
method: "POST",
|
|
10723
10851
|
operationId: "resolveAnchorReview",
|
|
@@ -10826,7 +10954,30 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10826
10954
|
path: "/admin/catalogue/anchor/breaker",
|
|
10827
10955
|
summary: "The Spotify anchor-search throttle breaker: tripped, why, how long left, and the rungs",
|
|
10828
10956
|
tags: ["Admin"]
|
|
10829
|
-
}).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({
|
|
10957
|
+
}).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({
|
|
10958
|
+
ok: literal(true),
|
|
10959
|
+
rungs: AnchorRungFlagsSchema,
|
|
10960
|
+
spotifyDailyCalls: number2().int().nullable()
|
|
10961
|
+
}));
|
|
10962
|
+
LabelReleasesBudgetSchema = object({
|
|
10963
|
+
callsSpent: number2().int().nonnegative(),
|
|
10964
|
+
dailyBudget: number2().int().nonnegative(),
|
|
10965
|
+
spotifyDailyCalls: number2().int().nonnegative()
|
|
10966
|
+
});
|
|
10967
|
+
getLabelReleasesBudget = oc.route({
|
|
10968
|
+
method: "GET",
|
|
10969
|
+
operationId: "getLabelReleasesBudget",
|
|
10970
|
+
path: "/admin/catalogue/label-releases-budget",
|
|
10971
|
+
summary: "Read the label releases tap daily budget and Spotify usage",
|
|
10972
|
+
tags: ["Admin"]
|
|
10973
|
+
}).input(object({})).output(LabelReleasesBudgetSchema.extend({ ok: literal(true) }));
|
|
10974
|
+
setLabelReleasesBudget = oc.route({
|
|
10975
|
+
method: "PUT",
|
|
10976
|
+
operationId: "setLabelReleasesBudget",
|
|
10977
|
+
path: "/admin/catalogue/label-releases-budget",
|
|
10978
|
+
summary: "Set the label releases tap daily budget (operator)",
|
|
10979
|
+
tags: ["Admin"]
|
|
10980
|
+
}).input(object({ dailyBudget: number2().int().min(0).max(1e6) })).output(LabelReleasesBudgetSchema.extend({ ok: literal(true) }));
|
|
10830
10981
|
resetSpotifyAnchorBreaker = oc.route({
|
|
10831
10982
|
method: "POST",
|
|
10832
10983
|
operationId: "resetSpotifyAnchorBreaker",
|
|
@@ -10849,19 +11000,27 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10849
11000
|
}).input(object({})).output(AppleBreakerStateSchema.extend({ ok: literal(true) }));
|
|
10850
11001
|
adminCatalogueContract = {
|
|
10851
11002
|
anchor_track: anchorTrack,
|
|
11003
|
+
cancel_anchor_paid_result: cancelAnchorPaidResult,
|
|
10852
11004
|
certify_track: certifyTrack,
|
|
10853
11005
|
clear_wrong_audio: clearWrongAudio,
|
|
11006
|
+
commit_anchor: commitAnchor,
|
|
11007
|
+
commit_anchor_batch: commitAnchorBatch,
|
|
10854
11008
|
commit_crawl_nodes: commitCrawlNodes,
|
|
10855
11009
|
crawl_catalogue: crawlCatalogue,
|
|
10856
11010
|
flag_wrong_audio: flagWrongAudio,
|
|
10857
11011
|
force_capture: forceCapture,
|
|
10858
11012
|
get_anchor_apify_budget: getAnchorApifyBudget,
|
|
11013
|
+
get_anchor_paid_token: getAnchorPaidToken,
|
|
11014
|
+
get_anchor_receipt: getAnchorReceipt,
|
|
10859
11015
|
get_capture_budget: getCaptureBudget,
|
|
10860
11016
|
get_crawl_status: getCrawlStatus,
|
|
11017
|
+
get_label_releases_budget: getLabelReleasesBudget,
|
|
10861
11018
|
get_pipeline: getPipeline,
|
|
10862
11019
|
get_spotify_anchor_breaker: getSpotifyAnchorBreaker,
|
|
10863
11020
|
list_catalogue_tracks: listCatalogueTracks,
|
|
10864
11021
|
list_unverified_captures: listUnverifiedCaptures,
|
|
11022
|
+
prepare_anchor: prepareAnchor,
|
|
11023
|
+
prepare_anchor_batch: prepareAnchorBatch,
|
|
10865
11024
|
rank_catalogue: rankCatalogue,
|
|
10866
11025
|
record_anchor_failure: recordAnchorFailure,
|
|
10867
11026
|
record_demand: recordDemand,
|
|
@@ -10871,11 +11030,14 @@ var init_admin_catalogue = __esm(() => {
|
|
|
10871
11030
|
reset_apple_breaker: resetAppleBreaker,
|
|
10872
11031
|
reset_spotify_anchor_breaker: resetSpotifyAnchorBreaker,
|
|
10873
11032
|
resolve_anchor: resolveAnchor,
|
|
11033
|
+
resolve_anchor_candidate: resolveAnchorCandidate,
|
|
11034
|
+
resolve_anchor_paid_result: resolveAnchorPaidResult,
|
|
10874
11035
|
resolve_anchor_review: resolveAnchorReview,
|
|
10875
11036
|
set_anchor_apify: setAnchorApify,
|
|
10876
11037
|
set_anchor_apify_budget: setAnchorApifyBudget,
|
|
10877
11038
|
set_anchor_search: setAnchorSearch,
|
|
10878
11039
|
set_capture_budget: setCaptureBudget,
|
|
11040
|
+
set_label_releases_budget: setLabelReleasesBudget,
|
|
10879
11041
|
set_track_dismissed: setTrackDismissed,
|
|
10880
11042
|
verify_capture: verifyCapture
|
|
10881
11043
|
};
|
|
@@ -11928,6 +12090,13 @@ var init_admin_backfills = __esm(() => {
|
|
|
11928
12090
|
})).output(object({
|
|
11929
12091
|
albumsMatched: number2(),
|
|
11930
12092
|
albumsSeen: number2(),
|
|
12093
|
+
blockedReason: _enum([
|
|
12094
|
+
"spotify_breaker",
|
|
12095
|
+
"spotify_budget",
|
|
12096
|
+
"spotify_budget_spent",
|
|
12097
|
+
"spotify_quota",
|
|
12098
|
+
"spotify_throttle"
|
|
12099
|
+
]).nullable(),
|
|
11931
12100
|
budgetPaused: boolean2(),
|
|
11932
12101
|
configured: boolean2(),
|
|
11933
12102
|
dryRun: boolean2(),
|
|
@@ -11935,14 +12104,20 @@ var init_admin_backfills = __esm(() => {
|
|
|
11935
12104
|
failedLabels: array(string2()),
|
|
11936
12105
|
fetchCeilingHit: boolean2(),
|
|
11937
12106
|
labelSlugs: array(string2()),
|
|
12107
|
+
labelsDue: number2(),
|
|
11938
12108
|
labelsProbed: number2(),
|
|
12109
|
+
neverChecked: number2(),
|
|
11939
12110
|
newRows: number2(),
|
|
11940
12111
|
newTrackIds: array(string2()),
|
|
11941
12112
|
ok: literal(true),
|
|
12113
|
+
quotaExceeded: boolean2(),
|
|
11942
12114
|
rateLimited: boolean2(),
|
|
12115
|
+
retryAfterMs: number2(),
|
|
11943
12116
|
skippedKnown: number2(),
|
|
11944
12117
|
skippedUndated: number2(),
|
|
11945
12118
|
skippedUngrounded: number2(),
|
|
12119
|
+
tapDailyBudget: number2().int(),
|
|
12120
|
+
tapDailyCallsSpent: number2().int(),
|
|
11946
12121
|
tracksSkippedArtistRule: number2().optional()
|
|
11947
12122
|
}));
|
|
11948
12123
|
LabelImagesBackfillFailedSchema = object({
|
|
@@ -13602,7 +13777,7 @@ var init_admin_hub_counts = __esm(() => {
|
|
|
13602
13777
|
});
|
|
13603
13778
|
|
|
13604
13779
|
// ../../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;
|
|
13780
|
+
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
13781
|
var init_admin_labels = __esm(() => {
|
|
13607
13782
|
init_dist3();
|
|
13608
13783
|
init_zod();
|
|
@@ -13655,6 +13830,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13655
13830
|
summary: "List one label's artist rules for future catalogue acquisition",
|
|
13656
13831
|
tags: ["Admin"]
|
|
13657
13832
|
}).input(object({ id: string2() })).output(object({ ok: literal(true), rules: array(ArtistRuleSchema) }));
|
|
13833
|
+
LABEL_TRIAGE_RULE_PROPOSALS_MAX = LABEL_ARTIST_RULES_MAX * 2;
|
|
13658
13834
|
replaceLabelArtistRules = oc.route({
|
|
13659
13835
|
method: "PUT",
|
|
13660
13836
|
operationId: "replaceLabelArtistRules",
|
|
@@ -13663,7 +13839,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13663
13839
|
tags: ["Admin"]
|
|
13664
13840
|
}).input(object({
|
|
13665
13841
|
id: string2(),
|
|
13666
|
-
rules: array(ArtistRuleInputSchema).max(
|
|
13842
|
+
rules: array(ArtistRuleInputSchema).max(LABEL_ARTIST_RULES_MAX).superRefine((rules, context) => {
|
|
13667
13843
|
const seen = new Set;
|
|
13668
13844
|
for (const [index, rule] of rules.entries()) {
|
|
13669
13845
|
if (seen.has(rule.artistMbid)) {
|
|
@@ -13820,7 +13996,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13820
13996
|
reason: string2().optional(),
|
|
13821
13997
|
residualOffLaneShare: number2().min(0).max(1).optional(),
|
|
13822
13998
|
roundId: string2(),
|
|
13823
|
-
rules: array(TriageRuleProposalSchema).optional(),
|
|
13999
|
+
rules: array(TriageRuleProposalSchema).max(LABEL_TRIAGE_RULE_PROPOSALS_MAX).optional(),
|
|
13824
14000
|
verdict: LabelTriageVerdictSchema,
|
|
13825
14001
|
verifyAgrees: boolean2().optional(),
|
|
13826
14002
|
verifyEvidence: string2().optional()
|
|
@@ -15435,7 +15611,7 @@ var init_admin_tracks = __esm(() => {
|
|
|
15435
15611
|
debtAware: string2().optional(),
|
|
15436
15612
|
kind: TrackWorkKindSchema,
|
|
15437
15613
|
limit: number3().int().min(1).max(250).default(50),
|
|
15438
|
-
paidMode: _enum(["quota", "prior"]).optional(),
|
|
15614
|
+
paidMode: _enum(["quota", "prior", "unasked"]).optional(),
|
|
15439
15615
|
scope: TrackWorkScopeSchema.default("all")
|
|
15440
15616
|
})).output(object({
|
|
15441
15617
|
capabilities: TrackWorkCapabilitiesSchema.optional(),
|
|
@@ -19289,14 +19465,18 @@ async function clearWrongAudioCommand(trackId) {
|
|
|
19289
19465
|
async function requeueUnmatchedCommand() {
|
|
19290
19466
|
return adminApiPost("/api/v1/admin/catalogue/captures/requeue-unmatched", {});
|
|
19291
19467
|
}
|
|
19292
|
-
async function requeueAnchorCommand(trackIds) {
|
|
19293
|
-
return adminApiPost("/api/v1/admin/catalogue/anchor/requeue", {
|
|
19294
|
-
trackIds
|
|
19295
|
-
});
|
|
19468
|
+
async function requeueAnchorCommand(trackIds, clearPaid = false) {
|
|
19469
|
+
return adminApiPost("/api/v1/admin/catalogue/anchor/requeue", { clearPaid, trackIds });
|
|
19296
19470
|
}
|
|
19297
19471
|
async function requeueIsrcRecoveryCommand(input) {
|
|
19298
19472
|
return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
|
|
19299
19473
|
}
|
|
19474
|
+
async function labelReleasesBudgetCommand() {
|
|
19475
|
+
return adminApiGet("/api/v1/admin/catalogue/label-releases-budget");
|
|
19476
|
+
}
|
|
19477
|
+
async function setLabelReleasesBudgetCommand(dailyBudget) {
|
|
19478
|
+
return adminApiPut("/api/v1/admin/catalogue/label-releases-budget", { dailyBudget });
|
|
19479
|
+
}
|
|
19300
19480
|
async function anchorApifyBudgetCommand() {
|
|
19301
19481
|
return adminApiGet("/api/v1/admin/catalogue/anchor/apify-budget");
|
|
19302
19482
|
}
|
|
@@ -23079,14 +23259,40 @@ JSON field reference:
|
|
|
23079
23259
|
}
|
|
23080
23260
|
console.log(cleared ? `Kept ${trackId}. Its audio re-embeds and rejoins the ranking on the next sweep.` : `${trackId} was not quarantined \u2014 nothing to clear.`);
|
|
23081
23261
|
});
|
|
23082
|
-
catalogue.command("requeue-anchor").description("Clear rows' anchor re-ask backoff so the next tick retries them (operator)").argument("<trackIds...>", "The catalogue track id(s) to requeue (up to 250)").option("--json", "Print JSON", false).action(async (trackIds, options) => {
|
|
23262
|
+
catalogue.command("requeue-anchor").description("Clear rows' anchor re-ask backoff so the next tick retries them (operator)").argument("<trackIds...>", "The catalogue track id(s) to requeue (up to 250)").option("--clear-paid", "Settle a reconciled pending paid receipt; keeps its spend (operator)", false).option("--json", "Print JSON", false).action(async (trackIds, options) => {
|
|
23083
23263
|
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23084
|
-
const { requeued } = await requeueAnchorCommand(trackIds);
|
|
23264
|
+
const { paidCleared, requeued } = await requeueAnchorCommand(trackIds, options.clearPaid);
|
|
23085
23265
|
if (options.json) {
|
|
23086
|
-
printJson({ ok: true, requeued });
|
|
23266
|
+
printJson({ ok: true, paidCleared, requeued });
|
|
23087
23267
|
return;
|
|
23088
23268
|
}
|
|
23089
23269
|
console.log(`Re-queued ${requeued} of ${trackIds.length} row(s) \u2014 the next anchor tick picks them up at their real priority.`);
|
|
23270
|
+
if (options.clearPaid) {
|
|
23271
|
+
console.log(`Settled ${paidCleared} pending paid receipt(s); recorded spend remains charged.`);
|
|
23272
|
+
}
|
|
23273
|
+
});
|
|
23274
|
+
const labelReleasesBudget = catalogue.command("label-releases-budget").description("Read or set the label releases tap daily Spotify call budget");
|
|
23275
|
+
labelReleasesBudget.option("--json", "Print JSON", false).action(async (options) => {
|
|
23276
|
+
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23277
|
+
const state = await labelReleasesBudgetCommand();
|
|
23278
|
+
if (options.json) {
|
|
23279
|
+
printJson({ ...state, ok: true });
|
|
23280
|
+
return;
|
|
23281
|
+
}
|
|
23282
|
+
console.log(`LABEL RELEASES BUDGET \u2014 ${state.callsSpent}/${state.dailyBudget} tap calls today \xB7 ${state.spotifyDailyCalls} Spotify calls total`);
|
|
23283
|
+
});
|
|
23284
|
+
labelReleasesBudget.command("set").requiredOption("--calls <count>", "Daily tap Spotify call budget").option("--json", "Print JSON", false).action(async (options) => {
|
|
23285
|
+
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
23286
|
+
const calls = Number(options.calls);
|
|
23287
|
+
if (!Number.isSafeInteger(calls) || calls < 0 || calls > 1e6) {
|
|
23288
|
+
throw new Error("--calls must be an integer from 0 to 1000000");
|
|
23289
|
+
}
|
|
23290
|
+
const state = await setLabelReleasesBudgetCommand(calls);
|
|
23291
|
+
if (options.json) {
|
|
23292
|
+
printJson({ ...state, ok: true });
|
|
23293
|
+
return;
|
|
23294
|
+
}
|
|
23295
|
+
console.log(`LABEL RELEASES BUDGET \u2014 ${state.callsSpent}/${state.dailyBudget} tap calls today \xB7 ${state.spotifyDailyCalls} Spotify calls total`);
|
|
23090
23296
|
});
|
|
23091
23297
|
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
23298
|
await Promise.resolve().then(() => init_admin_catalogue2());
|
|
@@ -26432,6 +26638,7 @@ var stringOptions = new Set([
|
|
|
26432
26638
|
"--bpm",
|
|
26433
26639
|
"--bpm-confidence",
|
|
26434
26640
|
"--bpm-source",
|
|
26641
|
+
"--calls",
|
|
26435
26642
|
"--composition",
|
|
26436
26643
|
"--consumer-digest",
|
|
26437
26644
|
"--consumer-item-count",
|
package/package.json
CHANGED