fluncle 0.267.0 → 0.268.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 +147 -7
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -651,7 +651,7 @@ function parseVersion(version) {
651
651
  var currentVersion;
652
652
  var init_version = __esm(() => {
653
653
  init_output();
654
- currentVersion = "0.267.0".trim() ? "0.267.0".trim() : "0.1.0";
654
+ currentVersion = "0.268.0".trim() ? "0.268.0".trim() : "0.1.0";
655
655
  });
656
656
 
657
657
  // src/update-notifier.ts
@@ -2364,7 +2364,7 @@ function parseVersion2(version) {
2364
2364
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2365
2365
  var init_version2 = __esm(() => {
2366
2366
  init_output();
2367
- currentVersion2 = "0.267.0".trim() ? "0.267.0".trim() : "0.1.0";
2367
+ currentVersion2 = "0.268.0".trim() ? "0.268.0".trim() : "0.1.0";
2368
2368
  });
2369
2369
 
2370
2370
  // ../../packages/registry/src/index.ts
@@ -10258,7 +10258,7 @@ var init_zod = __esm(() => {
10258
10258
  });
10259
10259
 
10260
10260
  // ../../packages/contracts/src/orpc/admin-catalogue.ts
10261
- 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, CrawlHoldReasonSchema, CrawlHoldStateSchema, CrawlHoldSchema, listCrawlHolds, resolveCrawlHold, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, AnchorApifyBudgetSchema, getAnchorApifyBudget, setAnchorApifyBudget, SpotifyAnchorBreakerStateSchema, AnchorRungFlagsSchema, getSpotifyAnchorBreaker, LabelReleasesBudgetSchema, getLabelReleasesBudget, setLabelReleasesBudget, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
10261
+ 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, ReleaseLinkResultSchema, ReleaseLinkProbeSchema, resolveAnchorReleaseProbe, commitAnchorRelease, resolveAnchorReview, CrawlHoldReasonSchema, CrawlHoldStateSchema, CrawlHoldSchema, listCrawlHolds, resolveCrawlHold, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, AnchorApifyBudgetSchema, getAnchorApifyBudget, setAnchorApifyBudget, SpotifyAnchorBreakerStateSchema, AnchorRungFlagsSchema, getSpotifyAnchorBreaker, setSpotifyConsumerBudget, LabelReleasesBudgetSchema, getLabelReleasesBudget, setLabelReleasesBudget, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
10262
10262
  var init_admin_catalogue = __esm(() => {
10263
10263
  init_dist3();
10264
10264
  init_zod();
@@ -10754,6 +10754,7 @@ var init_admin_catalogue = __esm(() => {
10754
10754
  }).meta({ id: "DeezerIsrcCandidate" });
10755
10755
  AnchorResolveResultSchema = object({
10756
10756
  anchored: boolean2(),
10757
+ anchoredByReleaseLink: number2().int().nonnegative().optional(),
10757
10758
  apifyBudgetRemaining: number2().int().nonnegative(),
10758
10759
  apifyEligible: boolean2(),
10759
10760
  apifyEnabled: boolean2(),
@@ -10773,7 +10774,18 @@ var init_admin_catalogue = __esm(() => {
10773
10774
  ]),
10774
10775
  ok: literal(true),
10775
10776
  paidResultToken: string2().optional(),
10776
- source: _enum(["listenbrainz", "spotify-isrc", "spotify-search"]).nullable(),
10777
+ releaseLinkAlbumFetchFailed: number2().int().nonnegative().optional(),
10778
+ releaseLinkAlbumsFetched: number2().int().nonnegative().optional(),
10779
+ releaseLinkBackoffSkipped: number2().int().nonnegative().optional(),
10780
+ releaseLinkCacheHits: number2().int().nonnegative().optional(),
10781
+ releaseLinkNoAlbum: number2().int().nonnegative().optional(),
10782
+ source: _enum([
10783
+ "listenbrainz",
10784
+ "listenbrainz-metadata",
10785
+ "release-link",
10786
+ "spotify-isrc",
10787
+ "spotify-search"
10788
+ ]).nullable(),
10777
10789
  spotifyIsrcAsked: boolean2(),
10778
10790
  spotifySearchDone: boolean2(),
10779
10791
  spotifySearchEnabled: boolean2(),
@@ -10907,13 +10919,65 @@ var init_admin_catalogue = __esm(() => {
10907
10919
  method: "POST",
10908
10920
  operationId: "resolveAnchor",
10909
10921
  path: "/admin/catalogue/anchor/resolve",
10910
- summary: "Resolve a catalogue row's Spotify anchor from the free rungs (ListenBrainz + dark Spotify search)",
10922
+ summary: "Resolve a catalogue row's Spotify anchor from release links, ListenBrainz, and Spotify search",
10911
10923
  tags: ["Admin"]
10912
10924
  }).input(object({
10913
10925
  deezerCandidates: array(DeezerIsrcCandidateSchema).max(DEEZER_CANDIDATE_LIMIT).optional(),
10914
10926
  spotifySearch: boolean2().optional(),
10915
10927
  trackId: string2().min(1)
10916
10928
  })).output(AnchorResolveResultSchema);
10929
+ ReleaseLinkResultSchema = object({
10930
+ albumFetchFailed: number2().int().nonnegative(),
10931
+ albumsFetched: number2().int().nonnegative(),
10932
+ anchored: boolean2(),
10933
+ anchoredCount: number2().int().nonnegative(),
10934
+ backoffSkipped: number2().int().nonnegative(),
10935
+ cacheHits: number2().int().nonnegative(),
10936
+ noAlbum: number2().int().nonnegative(),
10937
+ ok: literal(true),
10938
+ remainder: number2().int().nonnegative().nullable(),
10939
+ throttled: boolean2(),
10940
+ verifiedBy: _enum(["isrc", "search", "search-subset"]).nullable()
10941
+ });
10942
+ ReleaseLinkProbeSchema = object({
10943
+ evidence: array(object({
10944
+ albumId: string2().nullable(),
10945
+ checkedAt: string2().nullable(),
10946
+ recordingIds: array(string2()).max(1000),
10947
+ releaseId: string2(),
10948
+ siblingTrackIds: array(string2()).max(1000),
10949
+ tracks: array(object({
10950
+ artists: array(object({ id: string2(), name: string2() })),
10951
+ discNumber: number2(),
10952
+ durationMs: number2(),
10953
+ isrc: string2().nullable(),
10954
+ spotifyTrackId: string2(),
10955
+ title: string2(),
10956
+ trackNumber: number2()
10957
+ })).max(1000)
10958
+ })).max(3),
10959
+ issuedAt: number2().int().nonnegative(),
10960
+ result: ReleaseLinkResultSchema.omit({ ok: true }),
10961
+ trackId: string2().min(1)
10962
+ });
10963
+ resolveAnchorReleaseProbe = oc.route({
10964
+ method: "POST",
10965
+ operationId: "resolveAnchorReleaseProbe",
10966
+ path: "/admin/catalogue/anchor/release/probe",
10967
+ summary: "Probe MusicBrainz release links and Spotify album tracks",
10968
+ tags: ["Admin"]
10969
+ }).input(object({ spotifySearch: boolean2().optional(), trackId: string2().min(1) })).output(object({ ok: literal(true), probe: ReleaseLinkProbeSchema, proof: string2() }));
10970
+ commitAnchorRelease = oc.route({
10971
+ method: "POST",
10972
+ operationId: "commitAnchorRelease",
10973
+ path: "/admin/catalogue/anchor/release/commit",
10974
+ summary: "Commit release mappings and a bounded sibling anchor batch",
10975
+ tags: ["Admin"]
10976
+ }).input(object({
10977
+ cursor: number2().int().nonnegative(),
10978
+ probe: ReleaseLinkProbeSchema,
10979
+ proof: string2()
10980
+ })).output(ReleaseLinkResultSchema);
10917
10981
  resolveAnchorReview = oc.route({
10918
10982
  method: "POST",
10919
10983
  operationId: "resolveAnchorReview",
@@ -11052,6 +11116,7 @@ var init_admin_catalogue = __esm(() => {
11052
11116
  "quota_hold",
11053
11117
  "breaker_quota",
11054
11118
  "breaker_throttle",
11119
+ "daily_budget",
11055
11120
  "shared_meter",
11056
11121
  "open"
11057
11122
  ]),
@@ -11065,10 +11130,37 @@ var init_admin_catalogue = __esm(() => {
11065
11130
  summary: "The Spotify anchor-search throttle breaker: tripped, why, how long left, and the rungs",
11066
11131
  tags: ["Admin"]
11067
11132
  }).input(object({})).output(SpotifyAnchorBreakerStateSchema.extend({
11133
+ consumerBudgets: object({
11134
+ anchor: object({
11135
+ callsSpent: number2().int().nullable(),
11136
+ dailyBudget: number2().int().nullable()
11137
+ }),
11138
+ artist_images: object({
11139
+ callsSpent: number2().int().nullable(),
11140
+ dailyBudget: number2().int().nullable()
11141
+ }),
11142
+ public_search: object({
11143
+ callsSpent: number2().int().nullable(),
11144
+ dailyBudget: number2().int().nullable()
11145
+ })
11146
+ }),
11147
+ essentialDailyCalls: number2().int().nullable(),
11068
11148
  ok: literal(true),
11149
+ quotaHoldState: _enum(["clear", "held", "unknown"]),
11150
+ quotaHoldUntil: string2().nullable(),
11069
11151
  rungs: AnchorRungFlagsSchema,
11070
11152
  spotifyDailyCalls: number2().int().nullable()
11071
11153
  }));
11154
+ setSpotifyConsumerBudget = oc.route({
11155
+ method: "PUT",
11156
+ operationId: "setSpotifyConsumerBudget",
11157
+ path: "/admin/catalogue/spotify-budget",
11158
+ summary: "Set a Spotify consumer daily budget (operator)",
11159
+ tags: ["Admin"]
11160
+ }).input(object({
11161
+ consumer: _enum(["anchor", "artist_images", "public_search"]),
11162
+ dailyBudget: number2().int().min(0).max(1e6)
11163
+ })).output(object({ ok: literal(true) }));
11072
11164
  LabelReleasesBudgetSchema = object({
11073
11165
  callsSpent: number2().int().nonnegative(),
11074
11166
  dailyBudget: number2().int().nonnegative(),
@@ -11115,6 +11207,7 @@ var init_admin_catalogue = __esm(() => {
11115
11207
  clear_wrong_audio: clearWrongAudio,
11116
11208
  commit_anchor: commitAnchor,
11117
11209
  commit_anchor_batch: commitAnchorBatch,
11210
+ commit_anchor_release: commitAnchorRelease,
11118
11211
  commit_crawl_nodes: commitCrawlNodes,
11119
11212
  crawl_catalogue: crawlCatalogue,
11120
11213
  flag_wrong_audio: flagWrongAudio,
@@ -11143,6 +11236,7 @@ var init_admin_catalogue = __esm(() => {
11143
11236
  resolve_anchor: resolveAnchor,
11144
11237
  resolve_anchor_candidate: resolveAnchorCandidate,
11145
11238
  resolve_anchor_paid_result: resolveAnchorPaidResult,
11239
+ resolve_anchor_release_probe: resolveAnchorReleaseProbe,
11146
11240
  resolve_anchor_review: resolveAnchorReview,
11147
11241
  resolve_crawl_hold: resolveCrawlHold,
11148
11242
  set_anchor_apify: setAnchorApify,
@@ -11150,6 +11244,7 @@ var init_admin_catalogue = __esm(() => {
11150
11244
  set_anchor_search: setAnchorSearch,
11151
11245
  set_capture_budget: setCaptureBudget,
11152
11246
  set_label_releases_budget: setLabelReleasesBudget,
11247
+ set_spotify_consumer_budget: setSpotifyConsumerBudget,
11153
11248
  set_track_dismissed: setTrackDismissed,
11154
11249
  verify_capture: verifyCapture
11155
11250
  };
@@ -14140,7 +14235,7 @@ var init_admin_labels = __esm(() => {
14140
14235
  method: "PATCH",
14141
14236
  operationId: "updateLabel",
14142
14237
  path: "/admin/labels/{id}",
14143
- summary: "Update a label's crawl scope or arm a re-walk (operator; never storage)",
14238
+ summary: "Update a label's crawl scope or arm a re-walk (operator, or an agent applying a ratified triage round; never storage)",
14144
14239
  tags: ["Admin"]
14145
14240
  }).input(object({
14146
14241
  id: string2(),
@@ -14161,7 +14256,7 @@ var init_admin_labels = __esm(() => {
14161
14256
  method: "PUT",
14162
14257
  operationId: "replaceLabelArtistRules",
14163
14258
  path: "/admin/labels/{id}/artists",
14164
- summary: "Replace one label's complete artist-rule set (operator)",
14259
+ summary: "Replace one label's complete artist-rule set (operator, or an agent applying a ratified triage round)",
14165
14260
  tags: ["Admin"]
14166
14261
  }).input(object({
14167
14262
  id: string2(),
@@ -17678,6 +17773,7 @@ var contract, CONTRACT_OPERATION_NAMES, CONTRACT_OPERATION_ROUTES;
17678
17773
  var init_orpc = __esm(() => {
17679
17774
  init_dist3();
17680
17775
  init_admin_catalogue();
17776
+ init_admin_catalogue();
17681
17777
  init_admin_albums();
17682
17778
  init_admin_artifacts();
17683
17779
  init_admin_artist_rules();
@@ -19946,6 +20042,15 @@ async function requeueAnchorCommand(trackIds, clearPaid = false) {
19946
20042
  async function requeueIsrcRecoveryCommand(input) {
19947
20043
  return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
19948
20044
  }
20045
+ async function spotifyConsumerBudgetCommand() {
20046
+ return anchorBreakerCommand();
20047
+ }
20048
+ async function setSpotifyConsumerBudgetCommand(consumer, dailyBudget) {
20049
+ return adminApiPut("/api/v1/admin/catalogue/spotify-budget", {
20050
+ consumer,
20051
+ dailyBudget
20052
+ });
20053
+ }
19949
20054
  async function labelReleasesBudgetCommand() {
19950
20055
  return adminApiGet("/api/v1/admin/catalogue/label-releases-budget");
19951
20056
  }
@@ -24737,6 +24842,40 @@ JSON field reference:
24737
24842
  }
24738
24843
  console.log(`LABEL RELEASES BUDGET \u2014 ${state.callsSpent}/${state.dailyBudget} tap calls today \xB7 ${state.spotifyDailyCalls} Spotify calls total`);
24739
24844
  });
24845
+ const spotifyBudget = catalogue.command("spotify-consumer-budget").description("Read or set Spotify daily call budgets by consumer");
24846
+ const showSpotifyConsumerBudget = async (options) => {
24847
+ await Promise.resolve().then(() => init_admin_catalogue2());
24848
+ const state = await spotifyConsumerBudgetCommand();
24849
+ if (options.json || spotifyBudget.opts().json) {
24850
+ printJson({
24851
+ consumerBudgets: state.consumerBudgets,
24852
+ ok: true,
24853
+ spotifyDailyCalls: state.spotifyDailyCalls
24854
+ });
24855
+ return;
24856
+ }
24857
+ for (const [consumer, budget] of Object.entries(state.consumerBudgets)) {
24858
+ console.log(`SPOTIFY BUDGET ${consumer} \u2014 ${budget.callsSpent}/${budget.dailyBudget} calls today`);
24859
+ }
24860
+ };
24861
+ spotifyBudget.option("--json", "Print JSON", false).action(showSpotifyConsumerBudget);
24862
+ spotifyBudget.command("get").option("--json", "Print JSON", false).action(showSpotifyConsumerBudget);
24863
+ spotifyBudget.command("set").requiredOption("--consumer <consumer>", "anchor | artist_images | public_search").requiredOption("--calls <n>", "Daily Spotify calls allowed for this consumer").option("--json", "Print JSON", false).action(async (options) => {
24864
+ if (!["anchor", "artist_images", "public_search"].includes(options.consumer)) {
24865
+ throw new Error("--consumer must be anchor, artist_images, or public_search");
24866
+ }
24867
+ const calls = Number(options.calls);
24868
+ if (!Number.isSafeInteger(calls) || calls < 0 || calls > 1e6) {
24869
+ throw new Error("--calls must be an integer from 0 to 1000000");
24870
+ }
24871
+ await Promise.resolve().then(() => init_admin_catalogue2());
24872
+ await setSpotifyConsumerBudgetCommand(options.consumer, calls);
24873
+ if (options.json || spotifyBudget.opts().json) {
24874
+ printJson({ calls, consumer: options.consumer, ok: true });
24875
+ return;
24876
+ }
24877
+ console.log(`SPOTIFY BUDGET ${options.consumer} \u2014 ${calls} calls per day`);
24878
+ });
24740
24879
  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) => {
24741
24880
  await Promise.resolve().then(() => init_admin_catalogue2());
24742
24881
  const state = await anchorBreakerCommand();
@@ -28105,6 +28244,7 @@ var stringOptions = new Set([
28105
28244
  "--census-pages",
28106
28245
  "--composition",
28107
28246
  "--consumer-digest",
28247
+ "--consumer",
28108
28248
  "--consumer-item-count",
28109
28249
  "--content-file",
28110
28250
  "--contract",
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.267.0"
34
+ "version": "0.268.0"
35
35
  }