fluncle 0.97.0 → 0.98.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 +72 -3
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -557,7 +557,7 @@ function parseVersion(version) {
557
557
  var currentVersion;
558
558
  var init_version = __esm(() => {
559
559
  init_output();
560
- currentVersion = "0.97.0".trim() ? "0.97.0".trim() : "0.1.0";
560
+ currentVersion = "0.98.0".trim() ? "0.98.0".trim() : "0.1.0";
561
561
  });
562
562
 
563
563
  // src/update-notifier.ts
@@ -2187,7 +2187,7 @@ function parseVersion2(version) {
2187
2187
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2188
2188
  var init_version2 = __esm(() => {
2189
2189
  init_output();
2190
- currentVersion2 = "0.97.0".trim() ? "0.97.0".trim() : "0.1.0";
2190
+ currentVersion2 = "0.98.0".trim() ? "0.98.0".trim() : "0.1.0";
2191
2191
  });
2192
2192
 
2193
2193
  // ../../packages/registry/src/index.ts
@@ -2737,6 +2737,17 @@ var init_src = __esm(() => {
2737
2737
  probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-enrich", kind: "cron" },
2738
2738
  weights: { status: "hidden" }
2739
2739
  },
2740
+ {
2741
+ command: "fluncle admin tracks embed --queue",
2742
+ exposedContent: [
2743
+ "MuQ-large audio embedding (1024-d) for sonic similarity + clusters (--no-agent, on-box torch)"
2744
+ ],
2745
+ kind: "cron",
2746
+ name: "cron.embed",
2747
+ operatorNotes: "every 5m. On-box MuQ (torch, ~16s/track), zero LLM tokens. Writes the vector via the agent-tier update_track. Source: docs/agents/hermes/scripts/embed-sweep.* + embed-track.py. See docs/audio-embedding-rfc.md.",
2748
+ probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-embed", kind: "cron" },
2749
+ weights: { status: "hidden" }
2750
+ },
2740
2751
  {
2741
2752
  command: "fluncle admin tracks context --queue",
2742
2753
  exposedContent: [
@@ -3079,6 +3090,9 @@ async function trackUpdateCommand(trackId, options) {
3079
3090
  if (options.features !== undefined) {
3080
3091
  body.features = options.features;
3081
3092
  }
3093
+ if (options.embedding !== undefined) {
3094
+ body.embedding = options.embedding;
3095
+ }
3082
3096
  if (options.note !== undefined) {
3083
3097
  body.note = options.note;
3084
3098
  }
@@ -3441,6 +3455,7 @@ __export(exports_admin_tracks, {
3441
3455
  noteQueueCommand: () => noteQueueCommand,
3442
3456
  listCommand: () => listCommand,
3443
3457
  enrichQueueCommand: () => enrichQueueCommand,
3458
+ embedQueueCommand: () => embedQueueCommand,
3444
3459
  contextQueueCommand: () => contextQueueCommand,
3445
3460
  backfillLastfmCommand: () => backfillLastfmCommand,
3446
3461
  backfillDiscogsCommand: () => backfillDiscogsCommand
@@ -3448,6 +3463,7 @@ __export(exports_admin_tracks, {
3448
3463
  async function fetchAdminTracks(options) {
3449
3464
  const {
3450
3465
  hasContext,
3466
+ hasEmbedding,
3451
3467
  hasKey,
3452
3468
  hasNote,
3453
3469
  hasObservation,
@@ -3467,6 +3483,9 @@ async function fetchAdminTracks(options) {
3467
3483
  if (hasKey !== undefined) {
3468
3484
  params.set("hasKey", String(hasKey));
3469
3485
  }
3486
+ if (hasEmbedding !== undefined) {
3487
+ params.set("hasEmbedding", String(hasEmbedding));
3488
+ }
3470
3489
  if (hasContext !== undefined) {
3471
3490
  params.set("hasContext", String(hasContext));
3472
3491
  }
@@ -3519,6 +3538,9 @@ async function queueCommand(limit, filters = {}) {
3519
3538
  async function enrichQueueCommand(limit) {
3520
3539
  return fetchAdminTracks({ max: limit, order: "asc", status: "queue" });
3521
3540
  }
3541
+ async function embedQueueCommand(limit) {
3542
+ return fetchAdminTracks({ hasEmbedding: false, max: limit, order: "asc" });
3543
+ }
3522
3544
  async function contextQueueCommand(limit, retryEmptyContext = false) {
3523
3545
  return fetchAdminTracks({ hasContext: false, max: limit, order: "asc", retryEmptyContext });
3524
3546
  }
@@ -7641,6 +7663,15 @@ function addAdminCommands(program2) {
7641
7663
  const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
7642
7664
  await runAdminEnrichQueue(options, enrichQueueCommand2);
7643
7665
  });
7666
+ adminTracks.command("embed").description("Audio-embedding worklist (findings with no MuQ vector yet) \u2014 use --queue").option("--queue", "Show the embedding worklist, oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--json", "Print JSON", false).action(async (options) => {
7667
+ if (!options.queue) {
7668
+ console.error("`tracks embed` is a worklist view \u2014 embedding runs on the on-box `fluncle-embed` cron.\nUse `tracks embed --queue` to see findings needing an audio embedding.");
7669
+ process.exitCode = 1;
7670
+ return;
7671
+ }
7672
+ const { embedQueueCommand: embedQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
7673
+ await runAdminEmbedQueue(options, embedQueueCommand2);
7674
+ });
7644
7675
  adminTracks.command("vehicles").description("Recent video vehicles, newest first (the style ledger for diversity)").option("--limit <limit>", "Number of vehicles to show", "10").option("--json", "Print JSON", false).action(async (options) => {
7645
7676
  const { vehiclesCommand: vehiclesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
7646
7677
  await runAdminVehicles(options, vehiclesCommand2);
@@ -7650,7 +7681,7 @@ function addAdminCommands(program2) {
7650
7681
  const { trackGetAdminCommand: trackGetAdminCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
7651
7682
  await runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2);
7652
7683
  });
7653
- adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--bpm <number>", "Track BPM").option("--features <json>", "Audio feature JSON").option("--json", "Print JSON", false).option("--key <key>", "Musical key").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
7684
+ adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--bpm <number>", "Track BPM").option("--embedding <json>", "MuQ audio embedding as a JSON array of 1024 floats").option("--embedding-file <file>", "Read the MuQ embedding JSON array from a file").option("--features <json>", "Audio feature JSON").option("--json", "Print JSON", false).option("--key <key>", "Musical key").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
7654
7685
  const { trackUpdateCommand: trackUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
7655
7686
  await runTrackUpdate(trackId, options, trackUpdateCommand2);
7656
7687
  });
@@ -8260,6 +8291,21 @@ async function runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2) {
8260
8291
  console.log(`Log: ${t.logPageUrl}`);
8261
8292
  }
8262
8293
  }
8294
+ function parseEmbeddingArg(raw) {
8295
+ if (raw === undefined) {
8296
+ return;
8297
+ }
8298
+ let parsed;
8299
+ try {
8300
+ parsed = JSON.parse(raw);
8301
+ } catch {
8302
+ throw new Error("Invalid --embedding: expected a JSON array of 1024 floats");
8303
+ }
8304
+ if (!Array.isArray(parsed) || parsed.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
8305
+ throw new Error("Invalid --embedding: expected a JSON array of 1024 finite numbers");
8306
+ }
8307
+ return parsed;
8308
+ }
8263
8309
  async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
8264
8310
  if (!trackId) {
8265
8311
  throw new Error("Missing track id. Usage: fluncle admin tracks update <track_id>");
@@ -8268,8 +8314,11 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
8268
8314
  if (bpm !== undefined && !Number.isFinite(bpm)) {
8269
8315
  throw new Error(`Invalid --bpm: ${options.bpm}`);
8270
8316
  }
8317
+ const embeddingRaw = options.embeddingFile ? readFileSync6(options.embeddingFile, "utf8") : options.embedding;
8318
+ const embedding = parseEmbeddingArg(embeddingRaw);
8271
8319
  const result = await trackUpdateCommand2(trackId, {
8272
8320
  bpm,
8321
+ embedding,
8273
8322
  features: options.features,
8274
8323
  key: options.key,
8275
8324
  note: options.note,
@@ -8732,6 +8781,26 @@ async function runAdminEnrichQueue(options, enrichQueueCommand2) {
8732
8781
  console.log(trackRows2(tracks).join(`
8733
8782
  `));
8734
8783
  }
8784
+ async function runAdminEmbedQueue(options, embedQueueCommand2) {
8785
+ const limit = parseListLimit(options.limit);
8786
+ const tracks = await embedQueueCommand2(limit);
8787
+ if (options.json) {
8788
+ printJson({
8789
+ ok: true,
8790
+ tracks
8791
+ });
8792
+ return;
8793
+ }
8794
+ if (tracks.length === 0) {
8795
+ console.log("Nothing awaiting an embedding. Every finding is embedded.");
8796
+ return;
8797
+ }
8798
+ const { trackRows: trackRows2 } = await Promise.resolve().then(() => (init_format2(), exports_format));
8799
+ const noun = tracks.length === 1 ? "finding" : "findings";
8800
+ console.log(`${tracks.length} ${noun} needing an audio embedding, oldest first:`);
8801
+ console.log(trackRows2(tracks).join(`
8802
+ `));
8803
+ }
8735
8804
  async function runAdminVehicles(options, vehiclesCommand2) {
8736
8805
  const limit = parseListLimit(options.limit);
8737
8806
  const vehicles = await vehiclesCommand2(limit);
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.97.0"
34
+ "version": "0.98.0"
35
35
  }