fluncle 0.143.0 → 0.145.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 +76 -3
  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.143.0".trim() ? "0.143.0".trim() : "0.1.0";
564
+ currentVersion = "0.145.0".trim() ? "0.145.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.143.0".trim() ? "0.143.0".trim() : "0.1.0";
2192
+ currentVersion2 = "0.145.0".trim() ? "0.145.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,10 @@ 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,
5733
+ flagWrongAudioCommand: () => flagWrongAudioCommand,
5719
5734
  crawlStatusCommand: () => crawlStatusCommand,
5720
5735
  crawlCatalogueCommand: () => crawlCatalogueCommand,
5721
5736
  clearWrongAudioCommand: () => clearWrongAudioCommand,
@@ -5741,6 +5756,11 @@ async function clearWrongAudioCommand(trackId) {
5741
5756
  trackId
5742
5757
  });
5743
5758
  }
5759
+ async function flagWrongAudioCommand(trackId) {
5760
+ return adminApiPost("/api/admin/catalogue/wrong-audio/flag", {
5761
+ trackId
5762
+ });
5763
+ }
5744
5764
  async function certifyTrackCommand(trackId, note) {
5745
5765
  return adminApiPost("/api/admin/catalogue/certify", {
5746
5766
  ...note ? { note } : {},
@@ -5753,6 +5773,19 @@ async function setTrackDismissedCommand(trackId, dismissed) {
5753
5773
  trackId
5754
5774
  });
5755
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
+ }
5756
5789
  async function crawlCatalogueCommand(limit, maxHop, dryRun) {
5757
5790
  const params = new URLSearchParams({ limit: String(limit), maxHop: String(maxHop) });
5758
5791
  if (dryRun) {
@@ -8828,6 +8861,46 @@ function addAdminCommands(program2) {
8828
8861
  }
8829
8862
  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.`);
8830
8863
  });
8864
+ catalogue.command("flag-wrong-audio").description("Flag a finding's captured audio as the wrong recording (operator)").argument("<trackId>", "The finding's track id whose capture is wrong").option("--json", "Print JSON", false).action(async (trackId, options) => {
8865
+ const { flagWrongAudioCommand: flagWrongAudioCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
8866
+ const { flagged } = await flagWrongAudioCommand2(trackId);
8867
+ if (options.json) {
8868
+ printJson({ flagged, ok: true });
8869
+ return;
8870
+ }
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.`);
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
+ });
8831
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) => {
8832
8905
  const { certifyTrackCommand: certifyTrackCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
8833
8906
  const { logId } = await certifyTrackCommand2(trackId, options.note);
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.143.0"
34
+ "version": "0.145.0"
35
35
  }