fluncle 0.96.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.
- package/bin/fluncle.mjs +84 -4
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -489,11 +489,12 @@ function baseTitleMatches(findingTitle, candidateTitle) {
|
|
|
489
489
|
}
|
|
490
490
|
return true;
|
|
491
491
|
}
|
|
492
|
-
var VERSION_MARKER, REMIX_MARKER, VERSION_STOPWORDS;
|
|
492
|
+
var VERSION_MARKER, REMIX_MARKER, VERSION_STOPWORDS, TIKTOK_DRAFT_STALE_MS;
|
|
493
493
|
var init_util = __esm(() => {
|
|
494
494
|
VERSION_MARKER = /\b(mix|edit|version|remix|dub|vip|bootleg|rework|re-?edit|flip|refix|remaster(?:ed)?|instrumental)\b/i;
|
|
495
495
|
REMIX_MARKER = /\b(remix|bootleg|vip|rework|re-?edit|flip|refix)\b/i;
|
|
496
496
|
VERSION_STOPWORDS = new Set(["mix", "the", "and", "feat", "ft", "edit", "version", "remix"]);
|
|
497
|
+
TIKTOK_DRAFT_STALE_MS = 24 * 60 * 60 * 1000;
|
|
497
498
|
});
|
|
498
499
|
|
|
499
500
|
// src/output.ts
|
|
@@ -556,7 +557,7 @@ function parseVersion(version) {
|
|
|
556
557
|
var currentVersion;
|
|
557
558
|
var init_version = __esm(() => {
|
|
558
559
|
init_output();
|
|
559
|
-
currentVersion = "0.
|
|
560
|
+
currentVersion = "0.98.0".trim() ? "0.98.0".trim() : "0.1.0";
|
|
560
561
|
});
|
|
561
562
|
|
|
562
563
|
// src/update-notifier.ts
|
|
@@ -2186,7 +2187,7 @@ function parseVersion2(version) {
|
|
|
2186
2187
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2187
2188
|
var init_version2 = __esm(() => {
|
|
2188
2189
|
init_output();
|
|
2189
|
-
currentVersion2 = "0.
|
|
2190
|
+
currentVersion2 = "0.98.0".trim() ? "0.98.0".trim() : "0.1.0";
|
|
2190
2191
|
});
|
|
2191
2192
|
|
|
2192
2193
|
// ../../packages/registry/src/index.ts
|
|
@@ -2736,6 +2737,17 @@ var init_src = __esm(() => {
|
|
|
2736
2737
|
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-enrich", kind: "cron" },
|
|
2737
2738
|
weights: { status: "hidden" }
|
|
2738
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
|
+
},
|
|
2739
2751
|
{
|
|
2740
2752
|
command: "fluncle admin tracks context --queue",
|
|
2741
2753
|
exposedContent: [
|
|
@@ -2835,6 +2847,16 @@ var init_src = __esm(() => {
|
|
|
2835
2847
|
kind: "cron"
|
|
2836
2848
|
},
|
|
2837
2849
|
weights: { status: "secondary" }
|
|
2850
|
+
},
|
|
2851
|
+
{
|
|
2852
|
+
exposedContent: [
|
|
2853
|
+
"daily gzip dump of the prod database → a PRIVATE R2 bucket (owned off-site backup) + 30 daily / 12 monthly retention (--no-agent)"
|
|
2854
|
+
],
|
|
2855
|
+
kind: "cron",
|
|
2856
|
+
name: "cron.backup",
|
|
2857
|
+
operatorNotes: "daily. An OWNED, off-Cloudflare backup: dumps prod Turso over the libSQL HTTP pipeline → gzip → a PRIVATE R2 bucket (never fluncle-videos, which is world-served at found.fluncle.com) + prune. Zero LLM tokens; talks to Turso + R2 directly (no fluncle CLI, no agent token). Turso's managed PITR is the belt; this is the braces. Restore is proven by apps/web/scripts/restore-drill.ts. Source: docs/agents/hermes/scripts/backup-sweep.*",
|
|
2858
|
+
probeConfig: { cadenceMs: 24 * 60 * MINUTE_MS, cronName: "fluncle-backup", kind: "cron" },
|
|
2859
|
+
weights: { status: "secondary" }
|
|
2838
2860
|
}
|
|
2839
2861
|
];
|
|
2840
2862
|
});
|
|
@@ -3068,6 +3090,9 @@ async function trackUpdateCommand(trackId, options) {
|
|
|
3068
3090
|
if (options.features !== undefined) {
|
|
3069
3091
|
body.features = options.features;
|
|
3070
3092
|
}
|
|
3093
|
+
if (options.embedding !== undefined) {
|
|
3094
|
+
body.embedding = options.embedding;
|
|
3095
|
+
}
|
|
3071
3096
|
if (options.note !== undefined) {
|
|
3072
3097
|
body.note = options.note;
|
|
3073
3098
|
}
|
|
@@ -3430,6 +3455,7 @@ __export(exports_admin_tracks, {
|
|
|
3430
3455
|
noteQueueCommand: () => noteQueueCommand,
|
|
3431
3456
|
listCommand: () => listCommand,
|
|
3432
3457
|
enrichQueueCommand: () => enrichQueueCommand,
|
|
3458
|
+
embedQueueCommand: () => embedQueueCommand,
|
|
3433
3459
|
contextQueueCommand: () => contextQueueCommand,
|
|
3434
3460
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
3435
3461
|
backfillDiscogsCommand: () => backfillDiscogsCommand
|
|
@@ -3437,6 +3463,7 @@ __export(exports_admin_tracks, {
|
|
|
3437
3463
|
async function fetchAdminTracks(options) {
|
|
3438
3464
|
const {
|
|
3439
3465
|
hasContext,
|
|
3466
|
+
hasEmbedding,
|
|
3440
3467
|
hasKey,
|
|
3441
3468
|
hasNote,
|
|
3442
3469
|
hasObservation,
|
|
@@ -3456,6 +3483,9 @@ async function fetchAdminTracks(options) {
|
|
|
3456
3483
|
if (hasKey !== undefined) {
|
|
3457
3484
|
params.set("hasKey", String(hasKey));
|
|
3458
3485
|
}
|
|
3486
|
+
if (hasEmbedding !== undefined) {
|
|
3487
|
+
params.set("hasEmbedding", String(hasEmbedding));
|
|
3488
|
+
}
|
|
3459
3489
|
if (hasContext !== undefined) {
|
|
3460
3490
|
params.set("hasContext", String(hasContext));
|
|
3461
3491
|
}
|
|
@@ -3508,6 +3538,9 @@ async function queueCommand(limit, filters = {}) {
|
|
|
3508
3538
|
async function enrichQueueCommand(limit) {
|
|
3509
3539
|
return fetchAdminTracks({ max: limit, order: "asc", status: "queue" });
|
|
3510
3540
|
}
|
|
3541
|
+
async function embedQueueCommand(limit) {
|
|
3542
|
+
return fetchAdminTracks({ hasEmbedding: false, max: limit, order: "asc" });
|
|
3543
|
+
}
|
|
3511
3544
|
async function contextQueueCommand(limit, retryEmptyContext = false) {
|
|
3512
3545
|
return fetchAdminTracks({ hasContext: false, max: limit, order: "asc", retryEmptyContext });
|
|
3513
3546
|
}
|
|
@@ -7630,6 +7663,15 @@ function addAdminCommands(program2) {
|
|
|
7630
7663
|
const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7631
7664
|
await runAdminEnrichQueue(options, enrichQueueCommand2);
|
|
7632
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
|
+
});
|
|
7633
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) => {
|
|
7634
7676
|
const { vehiclesCommand: vehiclesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7635
7677
|
await runAdminVehicles(options, vehiclesCommand2);
|
|
@@ -7639,7 +7681,7 @@ function addAdminCommands(program2) {
|
|
|
7639
7681
|
const { trackGetAdminCommand: trackGetAdminCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7640
7682
|
await runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2);
|
|
7641
7683
|
});
|
|
7642
|
-
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) => {
|
|
7643
7685
|
const { trackUpdateCommand: trackUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7644
7686
|
await runTrackUpdate(trackId, options, trackUpdateCommand2);
|
|
7645
7687
|
});
|
|
@@ -8249,6 +8291,21 @@ async function runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2) {
|
|
|
8249
8291
|
console.log(`Log: ${t.logPageUrl}`);
|
|
8250
8292
|
}
|
|
8251
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
|
+
}
|
|
8252
8309
|
async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
|
|
8253
8310
|
if (!trackId) {
|
|
8254
8311
|
throw new Error("Missing track id. Usage: fluncle admin tracks update <track_id>");
|
|
@@ -8257,8 +8314,11 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
|
|
|
8257
8314
|
if (bpm !== undefined && !Number.isFinite(bpm)) {
|
|
8258
8315
|
throw new Error(`Invalid --bpm: ${options.bpm}`);
|
|
8259
8316
|
}
|
|
8317
|
+
const embeddingRaw = options.embeddingFile ? readFileSync6(options.embeddingFile, "utf8") : options.embedding;
|
|
8318
|
+
const embedding = parseEmbeddingArg(embeddingRaw);
|
|
8260
8319
|
const result = await trackUpdateCommand2(trackId, {
|
|
8261
8320
|
bpm,
|
|
8321
|
+
embedding,
|
|
8262
8322
|
features: options.features,
|
|
8263
8323
|
key: options.key,
|
|
8264
8324
|
note: options.note,
|
|
@@ -8721,6 +8781,26 @@ async function runAdminEnrichQueue(options, enrichQueueCommand2) {
|
|
|
8721
8781
|
console.log(trackRows2(tracks).join(`
|
|
8722
8782
|
`));
|
|
8723
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
|
+
}
|
|
8724
8804
|
async function runAdminVehicles(options, vehiclesCommand2) {
|
|
8725
8805
|
const limit = parseListLimit(options.limit);
|
|
8726
8806
|
const vehicles = await vehiclesCommand2(limit);
|
package/package.json
CHANGED