fluncle 0.262.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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +162 -24
  2. 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.262.0".trim() ? "0.262.0".trim() : "0.1.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.262.0".trim() ? "0.262.0".trim() : "0.1.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
@@ -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, LabelReleasesBudgetSchema, getLabelReleasesBudget, setLabelReleasesBudget, 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: "Clear named rows' anchor re-ask backoff or terminal validation error (operator)",
10323
+ summary: "Requeue named anchor rows and optionally reconcile pending paid receipts (operator)",
10324
10324
  tags: ["Admin"]
10325
- }).input(object({ trackIds: array(string2().min(1)).min(1).max(250) })).output(object({ ok: literal(true), requeued: number2() }));
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
- resolveAnchor = oc.route({
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)
10720
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()
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",
@@ -10872,13 +11000,18 @@ var init_admin_catalogue = __esm(() => {
10872
11000
  }).input(object({})).output(AppleBreakerStateSchema.extend({ ok: literal(true) }));
10873
11001
  adminCatalogueContract = {
10874
11002
  anchor_track: anchorTrack,
11003
+ cancel_anchor_paid_result: cancelAnchorPaidResult,
10875
11004
  certify_track: certifyTrack,
10876
11005
  clear_wrong_audio: clearWrongAudio,
11006
+ commit_anchor: commitAnchor,
11007
+ commit_anchor_batch: commitAnchorBatch,
10877
11008
  commit_crawl_nodes: commitCrawlNodes,
10878
11009
  crawl_catalogue: crawlCatalogue,
10879
11010
  flag_wrong_audio: flagWrongAudio,
10880
11011
  force_capture: forceCapture,
10881
11012
  get_anchor_apify_budget: getAnchorApifyBudget,
11013
+ get_anchor_paid_token: getAnchorPaidToken,
11014
+ get_anchor_receipt: getAnchorReceipt,
10882
11015
  get_capture_budget: getCaptureBudget,
10883
11016
  get_crawl_status: getCrawlStatus,
10884
11017
  get_label_releases_budget: getLabelReleasesBudget,
@@ -10886,6 +11019,8 @@ var init_admin_catalogue = __esm(() => {
10886
11019
  get_spotify_anchor_breaker: getSpotifyAnchorBreaker,
10887
11020
  list_catalogue_tracks: listCatalogueTracks,
10888
11021
  list_unverified_captures: listUnverifiedCaptures,
11022
+ prepare_anchor: prepareAnchor,
11023
+ prepare_anchor_batch: prepareAnchorBatch,
10889
11024
  rank_catalogue: rankCatalogue,
10890
11025
  record_anchor_failure: recordAnchorFailure,
10891
11026
  record_demand: recordDemand,
@@ -10895,6 +11030,8 @@ var init_admin_catalogue = __esm(() => {
10895
11030
  reset_apple_breaker: resetAppleBreaker,
10896
11031
  reset_spotify_anchor_breaker: resetSpotifyAnchorBreaker,
10897
11032
  resolve_anchor: resolveAnchor,
11033
+ resolve_anchor_candidate: resolveAnchorCandidate,
11034
+ resolve_anchor_paid_result: resolveAnchorPaidResult,
10898
11035
  resolve_anchor_review: resolveAnchorReview,
10899
11036
  set_anchor_apify: setAnchorApify,
10900
11037
  set_anchor_apify_budget: setAnchorApifyBudget,
@@ -19328,10 +19465,8 @@ async function clearWrongAudioCommand(trackId) {
19328
19465
  async function requeueUnmatchedCommand() {
19329
19466
  return adminApiPost("/api/v1/admin/catalogue/captures/requeue-unmatched", {});
19330
19467
  }
19331
- async function requeueAnchorCommand(trackIds) {
19332
- return adminApiPost("/api/v1/admin/catalogue/anchor/requeue", {
19333
- trackIds
19334
- });
19468
+ async function requeueAnchorCommand(trackIds, clearPaid = false) {
19469
+ return adminApiPost("/api/v1/admin/catalogue/anchor/requeue", { clearPaid, trackIds });
19335
19470
  }
19336
19471
  async function requeueIsrcRecoveryCommand(input) {
19337
19472
  return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
@@ -23124,14 +23259,17 @@ JSON field reference:
23124
23259
  }
23125
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.`);
23126
23261
  });
23127
- 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) => {
23128
23263
  await Promise.resolve().then(() => init_admin_catalogue2());
23129
- const { requeued } = await requeueAnchorCommand(trackIds);
23264
+ const { paidCleared, requeued } = await requeueAnchorCommand(trackIds, options.clearPaid);
23130
23265
  if (options.json) {
23131
- printJson({ ok: true, requeued });
23266
+ printJson({ ok: true, paidCleared, requeued });
23132
23267
  return;
23133
23268
  }
23134
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
+ }
23135
23273
  });
23136
23274
  const labelReleasesBudget = catalogue.command("label-releases-budget").description("Read or set the label releases tap daily Spotify call budget");
23137
23275
  labelReleasesBudget.option("--json", "Print JSON", false).action(async (options) => {
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.262.0"
34
+ "version": "0.263.0"
35
35
  }