fluncle 0.144.0 → 0.146.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 +63 -4
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -561,7 +561,7 @@ function parseVersion(version) {
561
561
  var currentVersion;
562
562
  var init_version = __esm(() => {
563
563
  init_output();
564
- currentVersion = "0.144.0".trim() ? "0.144.0".trim() : "0.1.0";
564
+ currentVersion = "0.146.0".trim() ? "0.146.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2189,7 +2189,7 @@ function parseVersion2(version) {
2189
2189
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2190
2190
  var init_version2 = __esm(() => {
2191
2191
  init_output();
2192
- currentVersion2 = "0.144.0".trim() ? "0.144.0".trim() : "0.1.0";
2192
+ currentVersion2 = "0.146.0".trim() ? "0.146.0".trim() : "0.1.0";
2193
2193
  });
2194
2194
 
2195
2195
  // ../../packages/registry/src/index.ts
@@ -2783,7 +2783,7 @@ var init_src = __esm(() => {
2783
2783
  {
2784
2784
  command: "ssh rave.fluncle.com",
2785
2785
  exposedContent: [
2786
- "the rave terminal TUI: Latest bangers, Mixtape archive, Submit, Subscribe, Install CLI, About",
2786
+ "the rave terminal TUI: Latest bangers, Artist archive, Sonic galaxies, Mixtape archive, Random banger, Submit, Subscribe, Install CLI, System status, About",
2787
2787
  "deep-register one-shots: `ssh rave.fluncle.com latest|random`"
2788
2788
  ],
2789
2789
  kind: "ssh",
@@ -2980,6 +2980,17 @@ var init_src = __esm(() => {
2980
2980
  probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-capture", kind: "cron" },
2981
2981
  weights: { status: "hidden" }
2982
2982
  },
2983
+ {
2984
+ command: "fluncle admin catalogue verify --queue",
2985
+ exposedContent: [
2986
+ "fingerprint-check each captured song against its official preview → the wrong-audio verdict"
2987
+ ],
2988
+ kind: "cron",
2989
+ name: "cron.verify-captures",
2990
+ operatorNotes: "every 30m, run by a rave-02 HOST systemd timer (docs/agents/hermes/verify-captures-timer/). The HISTORIC half of the capture verification gate (docs/the-ear.md § Wrong audio): the capture sweep verifies every NEW download at ingest, and this sweep walks every capture that landed before the gate existed and gives each the same Chromaprint check against the track's ISRC-resolved official preview. The box only MEASURES (fpcalc + the sliding-window match) and reports a plain verdict; the Worker ROUTES it — match/no-preview stamp `capture_verification`, a CATALOGUE mismatch quarantines for re-capture, a FINDING mismatch only raises the `capture-suspect` /admin attention item (a machine never rewinds a public finding; the operator rules with flag_wrong_audio). Resumable by construction (a stamped row leaves the worklist); degrades honestly without fpcalc (pre-rebake: `fpcalc_missing`, nothing stamped). Zero LLM tokens. Source: docs/agents/hermes/scripts/verify-captures.* + fingerprint-match.ts. See docs/the-ear.md.",
2991
+ probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-verify-captures", kind: "cron" },
2992
+ weights: { status: "hidden" }
2993
+ },
2983
2994
  {
2984
2995
  command: "fluncle admin tracks context --queue",
2985
2996
  exposedContent: [
@@ -3572,6 +3583,7 @@ var init_admin_attention = __esm(() => {
3572
3583
  SOURCE_LABELS = {
3573
3584
  "artist-review": "artist links",
3574
3585
  "attach-cues": "cues",
3586
+ "capture-suspect": "capture check",
3575
3587
  distribute: "distribute",
3576
3588
  "drip-empty": "clip drip",
3577
3589
  "label-review": "label",
@@ -5715,7 +5727,9 @@ var init_admin_notes = __esm(() => {
5715
5727
  // src/commands/admin-catalogue.ts
5716
5728
  var exports_admin_catalogue = {};
5717
5729
  __export(exports_admin_catalogue, {
5730
+ verifyCaptureCommand: () => verifyCaptureCommand,
5718
5731
  setTrackDismissedCommand: () => setTrackDismissedCommand,
5732
+ listUnverifiedCapturesCommand: () => listUnverifiedCapturesCommand,
5719
5733
  flagWrongAudioCommand: () => flagWrongAudioCommand,
5720
5734
  crawlStatusCommand: () => crawlStatusCommand,
5721
5735
  crawlCatalogueCommand: () => crawlCatalogueCommand,
@@ -5759,6 +5773,19 @@ async function setTrackDismissedCommand(trackId, dismissed) {
5759
5773
  trackId
5760
5774
  });
5761
5775
  }
5776
+ async function listUnverifiedCapturesCommand(options) {
5777
+ const params = new URLSearchParams;
5778
+ if (options.limit) {
5779
+ params.set("limit", options.limit);
5780
+ }
5781
+ return adminApiGet(`/api/admin/catalogue/captures/unverified?${params.toString()}`);
5782
+ }
5783
+ async function verifyCaptureCommand(trackId, verdict) {
5784
+ return adminApiPost("/api/admin/catalogue/captures/verify", {
5785
+ trackId,
5786
+ verdict
5787
+ });
5788
+ }
5762
5789
  async function crawlCatalogueCommand(limit, maxHop, dryRun) {
5763
5790
  const params = new URLSearchParams({ limit: String(limit), maxHop: String(maxHop) });
5764
5791
  if (dryRun) {
@@ -8843,6 +8870,37 @@ function addAdminCommands(program2) {
8843
8870
  }
8844
8871
  console.log(flagged ? `Flagged ${trackId}. Its vector is out of the ranking and a fresh capture is queued (the bad bytes are hash-rejected).` : `${trackId} is not a captured finding \u2014 nothing to flag.`);
8845
8872
  });
8873
+ catalogue.command("verify").description("Capture verification: the unverified worklist (--queue), or record one verdict").argument("[trackId]", "The track whose capture verdict to record (omit with --queue)").option("--queue", "List captured rows not yet verified against their preview", false).option("--verdict <verdict>", "The fingerprint verdict: match | mismatch | no-preview").option("--limit <limit>", "Worklist rows (default 50, max 200)").option("--json", "Print JSON", false).action(async (trackId, options) => {
8874
+ const { listUnverifiedCapturesCommand: listUnverifiedCapturesCommand2, verifyCaptureCommand: verifyCaptureCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
8875
+ if (options.queue) {
8876
+ const { tracks } = await listUnverifiedCapturesCommand2(options.limit ? { limit: options.limit } : {});
8877
+ if (options.json) {
8878
+ printJson({ ok: true, tracks });
8879
+ return;
8880
+ }
8881
+ if (tracks.length === 0) {
8882
+ console.log("Every capture is verified. Nothing to check.");
8883
+ return;
8884
+ }
8885
+ for (const track of tracks) {
8886
+ console.log(`${track.logId ?? `${track.trackId} \xB7 catalogue`} \u2014 ${track.artists.join(", ")} \u2014 ${track.title}`);
8887
+ }
8888
+ return;
8889
+ }
8890
+ if (!trackId) {
8891
+ throw new Error("Pass a trackId with --verdict, or --queue for the worklist.");
8892
+ }
8893
+ const verdict = options.verdict;
8894
+ if (verdict !== "match" && verdict !== "mismatch" && verdict !== "no-preview") {
8895
+ throw new Error("--verdict must be match, mismatch, or no-preview.");
8896
+ }
8897
+ const { action } = await verifyCaptureCommand2(trackId, verdict);
8898
+ if (options.json) {
8899
+ printJson({ action, ok: true });
8900
+ return;
8901
+ }
8902
+ console.log(`Recorded. ${trackId} \u2192 ${action}.`);
8903
+ });
8846
8904
  catalogue.command("certify").description("Certify an existing catalogue track in place \u2014 mint its finding (operator)").argument("<trackId>", "The catalogue track id to log").option("--note <note>", "An optional editorial note to mint onto the finding").option("--json", "Print JSON", false).action(async (trackId, options) => {
8847
8905
  const { certifyTrackCommand: certifyTrackCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
8848
8906
  const { logId } = await certifyTrackCommand2(trackId, options.note);
@@ -10523,7 +10581,8 @@ async function runCatalogueRank(options, catalogueRankCommand2) {
10523
10581
  return;
10524
10582
  }
10525
10583
  const quarantine = summary.quarantined > 0 ? ` Quarantined ${summary.quarantined} as wrong audio.` : "";
10526
- console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture.${quarantine} ${summary.remaining} still stale.`);
10584
+ const deduped = summary.catalogueDuplicates > 0 ? ` Marked ${summary.catalogueDuplicates} as catalogue duplicates.` : "";
10585
+ console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture.${quarantine}${deduped} ${summary.remaining} still stale.`);
10527
10586
  }
10528
10587
  async function runCatalogueList(options, catalogueListCommand2) {
10529
10588
  const { summary, tracks } = await catalogueListCommand2({
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.144.0"
34
+ "version": "0.146.0"
35
35
  }