fluncle 0.260.0 → 0.261.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 +33 -9
  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.260.0".trim() ? "0.260.0".trim() : "0.1.0";
625
+ currentVersion = "0.261.0".trim() ? "0.261.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.260.0".trim() ? "0.260.0".trim() : "0.1.0";
2338
+ currentVersion2 = "0.261.0".trim() ? "0.261.0".trim() : "0.1.0";
2339
2339
  });
2340
2340
 
2341
2341
  // ../../packages/registry/src/index.ts
@@ -3434,7 +3434,7 @@ var init_src = __esm(() => {
3434
3434
  ],
3435
3435
  kind: "cron",
3436
3436
  name: "cron.cluster",
3437
- operatorNotes: "nightly (02:20 Amsterdam), run by a rave-02 HOST systemd timer (docs/agents/hermes/cluster-timer/). Assignment-ONLY + idempotent (a no-op on an unchanged corpus): assign each finding to its nearest stored centroid, recompute centroids as members' means, retire an emptied galaxy, consume an operator split_requested_at (a k=2 fit). Zero LLM tokens; sub-second CPU. A full k=9 fit is an OPERATOR act (--cold-start / --remint), never scheduled. Source: docs/agents/hermes/scripts/cluster-sweep.* + cluster.py. See docs/agents/cluster-engine.md.",
3437
+ operatorNotes: "nightly (03:20 Amsterdam), run by a Hermes host systemd timer (docs/agents/hermes/cluster-timer/). Assignment-ONLY + idempotent (a no-op on an unchanged corpus): assign each finding to its nearest stored centroid, recompute centroids as members' means, retire an emptied galaxy, consume an operator split_requested_at (a k=2 fit). Zero LLM tokens; sub-second CPU. A full k=9 fit is an OPERATOR act (--cold-start / --remint), never scheduled. Source: docs/agents/hermes/scripts/cluster-sweep.* + cluster.py. See docs/agents/cluster-engine.md.",
3438
3438
  probeConfig: { cadenceMs: 24 * 60 * MINUTE_MS, cronName: "fluncle-cluster", kind: "cron" },
3439
3439
  statusDescription: "groups the archive into sonic galaxies",
3440
3440
  title: "Sonic galaxies",
@@ -3459,11 +3459,12 @@ var init_src = __esm(() => {
3459
3459
  ],
3460
3460
  kind: "cron",
3461
3461
  name: "cron.label-releases",
3462
- 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. 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.",
3462
+ operatorNotes: "daily at 07:20 Amsterdam, 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
- kind: "cron"
3466
+ kind: "cron",
3467
+ schedule: { time: "07:20", tz: "Europe/Amsterdam" }
3467
3468
  },
3468
3469
  statusDescription: "taps day-one releases from the enabled labels",
3469
3470
  title: "Freshness tap",
@@ -10192,11 +10193,11 @@ var init_zod = __esm(() => {
10192
10193
  });
10193
10194
 
10194
10195
  // ../../packages/contracts/src/orpc/admin-catalogue.ts
10195
- 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, 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, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
10196
10197
  var init_admin_catalogue = __esm(() => {
10197
10198
  init_dist3();
10198
10199
  init_zod();
10199
- CatalogueLensSchema = _enum(["capture", "dismissed", "ear", "failed", "quarantine", "unmatched"]).meta({ id: "CatalogueLens" });
10200
+ CatalogueLensSchema = _enum(["capture", "dismissed", "ear", "failed", "long", "quarantine", "unmatched"]).meta({ id: "CatalogueLens" });
10200
10201
  CapturePriorityReasonSchema = object({
10201
10202
  kind: _enum(["artist", "label", "none", "seed-label", "skipped-label", "unauthorized"]),
10202
10203
  name: string2().nullable()
@@ -10220,6 +10221,7 @@ var init_admin_catalogue = __esm(() => {
10220
10221
  duplicateOf: CatalogueMatchSchema.nullable(),
10221
10222
  hasCapturedAudio: boolean2(),
10222
10223
  hasPreview: boolean2(),
10224
+ hiddenFromPublic: boolean2(),
10223
10225
  isrc: string2().nullable(),
10224
10226
  key: string2().nullable(),
10225
10227
  label: string2().nullable(),
@@ -10582,6 +10584,21 @@ var init_admin_catalogue = __esm(() => {
10582
10584
  summary: literal(true),
10583
10585
  unstorablePending: number2()
10584
10586
  }).meta({ id: "CrawlPipelineSummary" });
10587
+ PipelineWatchBoundedCountSchema = object({ atLeast: boolean2(), count: number2().int().nonnegative() }).meta({ id: "PipelineWatchBoundedCount" });
10588
+ getPipeline = oc.route({
10589
+ method: "GET",
10590
+ operationId: "getPipeline",
10591
+ path: "/admin/catalogue/pipeline",
10592
+ summary: "Bounded pipeline counts for the box watchdog",
10593
+ tags: ["Admin"]
10594
+ }).input(object({})).output(object({
10595
+ anchors: PipelineWatchBoundedCountSchema,
10596
+ capture: PipelineWatchBoundedCountSchema.nullable(),
10597
+ frontier: PipelineWatchBoundedCountSchema,
10598
+ ok: literal(true),
10599
+ storable: PipelineWatchBoundedCountSchema,
10600
+ unstorable: PipelineWatchBoundedCountSchema
10601
+ }));
10585
10602
  crawlCatalogue = oc.route({
10586
10603
  inputStructure: "detailed",
10587
10604
  method: "POST",
@@ -10841,6 +10858,7 @@ var init_admin_catalogue = __esm(() => {
10841
10858
  get_anchor_apify_budget: getAnchorApifyBudget,
10842
10859
  get_capture_budget: getCaptureBudget,
10843
10860
  get_crawl_status: getCrawlStatus,
10861
+ get_pipeline: getPipeline,
10844
10862
  get_spotify_anchor_breaker: getSpotifyAnchorBreaker,
10845
10863
  list_catalogue_tracks: listCatalogueTracks,
10846
10864
  list_unverified_captures: listUnverifiedCaptures,
@@ -12484,7 +12502,10 @@ var init_admin_projections = __esm(() => {
12484
12502
  projections: object({
12485
12503
  artistQualification: ProjectionFamilyStatusSchema,
12486
12504
  crawlDueWork: ProjectionFamilyStatusSchema,
12487
- publicAggregates: ProjectionFamilyStatusSchema.extend({ anchorsReady: boolean2() }),
12505
+ publicAggregates: ProjectionFamilyStatusSchema.extend({
12506
+ anchorsReady: boolean2(),
12507
+ durationGenerationReady: boolean2()
12508
+ }),
12488
12509
  trackDueWork: ProjectionFamilyStatusSchema.extend({
12489
12510
  catalogueRankMarkerAgeMs: CountSchema.nullable().optional()
12490
12511
  })
@@ -15789,7 +15810,8 @@ var init_follow_digest = __esm(() => {
15789
15810
  sent: number2(),
15790
15811
  skipped: number2(),
15791
15812
  unknown: number2(),
15792
- weekKey: string2()
15813
+ weekKey: string2(),
15814
+ windowClosed: boolean2()
15793
15815
  }));
15794
15816
  getFollowDigestState = oc.route({
15795
15817
  method: "GET",
@@ -22715,6 +22737,8 @@ JSON field reference:
22715
22737
  });
22716
22738
  if (options.json) {
22717
22739
  printJson(result);
22740
+ } else if (result.windowClosed && !result.dryRun) {
22741
+ console.log("Follow digest sends open Friday at 17:00 Amsterdam.");
22718
22742
  } else {
22719
22743
  console.log(`Considered ${result.considered}; sent ${result.sent}; empty ${result.empty}; next ${result.nextCursor ?? "done"}.`);
22720
22744
  }
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.260.0"
34
+ "version": "0.261.0"
35
35
  }