fluncle 0.97.0 → 0.99.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 +87 -12
- 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.
|
|
560
|
+
currentVersion = "0.99.0".trim() ? "0.99.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.
|
|
2190
|
+
currentVersion2 = "0.99.0".trim() ? "0.99.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
|
}
|
|
@@ -3935,14 +3957,10 @@ var init_mixtape_youtube2 = __esm(() => {
|
|
|
3935
3957
|
init_output();
|
|
3936
3958
|
});
|
|
3937
3959
|
|
|
3938
|
-
// src/
|
|
3939
|
-
import { randomUUID } from "node:crypto";
|
|
3940
|
-
import { existsSync as existsSync2, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
|
|
3941
|
-
import { tmpdir } from "node:os";
|
|
3942
|
-
import { join as join5 } from "node:path";
|
|
3960
|
+
// ../../packages/contracts/src/util/multipart.ts
|
|
3943
3961
|
function planMultipart(contentLength, partSize = DEFAULT_PART_SIZE) {
|
|
3944
3962
|
if (!Number.isInteger(contentLength) || contentLength <= 0) {
|
|
3945
|
-
throw new
|
|
3963
|
+
throw new Error(`multipart content length must be a positive integer (got ${contentLength})`);
|
|
3946
3964
|
}
|
|
3947
3965
|
let effective = Math.max(partSize, MIN_PART_SIZE);
|
|
3948
3966
|
if (Math.ceil(contentLength / effective) > MAX_PARTS) {
|
|
@@ -3967,6 +3985,17 @@ function buildCompleteXml(parts) {
|
|
|
3967
3985
|
const body = ordered.map((part) => `<Part><PartNumber>${part.partNumber}</PartNumber><ETag>${escapeXml(part.etag)}</ETag></Part>`).join("");
|
|
3968
3986
|
return `<CompleteMultipartUpload>${body}</CompleteMultipartUpload>`;
|
|
3969
3987
|
}
|
|
3988
|
+
var DEFAULT_PART_SIZE, MIN_PART_SIZE, MAX_PARTS = 1e4;
|
|
3989
|
+
var init_multipart = __esm(() => {
|
|
3990
|
+
DEFAULT_PART_SIZE = 16 * 1024 * 1024;
|
|
3991
|
+
MIN_PART_SIZE = 5 * 1024 * 1024;
|
|
3992
|
+
});
|
|
3993
|
+
|
|
3994
|
+
// src/commands/mixtape-set-video.ts
|
|
3995
|
+
import { randomUUID } from "node:crypto";
|
|
3996
|
+
import { existsSync as existsSync2, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
|
|
3997
|
+
import { tmpdir } from "node:os";
|
|
3998
|
+
import { join as join5 } from "node:path";
|
|
3970
3999
|
function renditionFfmpegArgs(inputPath, outputPath) {
|
|
3971
4000
|
return [
|
|
3972
4001
|
"-y",
|
|
@@ -4099,8 +4128,9 @@ async function deriveRendition(inputPath, outputPath) {
|
|
|
4099
4128
|
throw new CliError2("ffmpeg_failed", `ffmpeg failed to derive the set-video rendition${detail ? `: ${detail}` : ""}`);
|
|
4100
4129
|
}
|
|
4101
4130
|
}
|
|
4102
|
-
var FOUND_BASE2 = "https://found.fluncle.com", SET_VIDEO_RENDITION,
|
|
4131
|
+
var FOUND_BASE2 = "https://found.fluncle.com", SET_VIDEO_RENDITION, MAX_PART_ATTEMPTS = 5;
|
|
4103
4132
|
var init_mixtape_set_video = __esm(() => {
|
|
4133
|
+
init_multipart();
|
|
4104
4134
|
init_api();
|
|
4105
4135
|
init_output();
|
|
4106
4136
|
SET_VIDEO_RENDITION = {
|
|
@@ -4109,8 +4139,6 @@ var init_mixtape_set_video = __esm(() => {
|
|
|
4109
4139
|
gopSeconds: 2,
|
|
4110
4140
|
height: 1080
|
|
4111
4141
|
};
|
|
4112
|
-
DEFAULT_PART_SIZE = 16 * 1024 * 1024;
|
|
4113
|
-
MIN_PART_SIZE = 5 * 1024 * 1024;
|
|
4114
4142
|
});
|
|
4115
4143
|
|
|
4116
4144
|
// src/commands/recordings.ts
|
|
@@ -7641,6 +7669,15 @@ function addAdminCommands(program2) {
|
|
|
7641
7669
|
const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7642
7670
|
await runAdminEnrichQueue(options, enrichQueueCommand2);
|
|
7643
7671
|
});
|
|
7672
|
+
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) => {
|
|
7673
|
+
if (!options.queue) {
|
|
7674
|
+
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.");
|
|
7675
|
+
process.exitCode = 1;
|
|
7676
|
+
return;
|
|
7677
|
+
}
|
|
7678
|
+
const { embedQueueCommand: embedQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7679
|
+
await runAdminEmbedQueue(options, embedQueueCommand2);
|
|
7680
|
+
});
|
|
7644
7681
|
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
7682
|
const { vehiclesCommand: vehiclesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7646
7683
|
await runAdminVehicles(options, vehiclesCommand2);
|
|
@@ -7650,7 +7687,7 @@ function addAdminCommands(program2) {
|
|
|
7650
7687
|
const { trackGetAdminCommand: trackGetAdminCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7651
7688
|
await runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2);
|
|
7652
7689
|
});
|
|
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) => {
|
|
7690
|
+
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
7691
|
const { trackUpdateCommand: trackUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7655
7692
|
await runTrackUpdate(trackId, options, trackUpdateCommand2);
|
|
7656
7693
|
});
|
|
@@ -8260,6 +8297,21 @@ async function runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2) {
|
|
|
8260
8297
|
console.log(`Log: ${t.logPageUrl}`);
|
|
8261
8298
|
}
|
|
8262
8299
|
}
|
|
8300
|
+
function parseEmbeddingArg(raw) {
|
|
8301
|
+
if (raw === undefined) {
|
|
8302
|
+
return;
|
|
8303
|
+
}
|
|
8304
|
+
let parsed;
|
|
8305
|
+
try {
|
|
8306
|
+
parsed = JSON.parse(raw);
|
|
8307
|
+
} catch {
|
|
8308
|
+
throw new Error("Invalid --embedding: expected a JSON array of 1024 floats");
|
|
8309
|
+
}
|
|
8310
|
+
if (!Array.isArray(parsed) || parsed.some((value) => typeof value !== "number" || !Number.isFinite(value))) {
|
|
8311
|
+
throw new Error("Invalid --embedding: expected a JSON array of 1024 finite numbers");
|
|
8312
|
+
}
|
|
8313
|
+
return parsed;
|
|
8314
|
+
}
|
|
8263
8315
|
async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
|
|
8264
8316
|
if (!trackId) {
|
|
8265
8317
|
throw new Error("Missing track id. Usage: fluncle admin tracks update <track_id>");
|
|
@@ -8268,8 +8320,11 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
|
|
|
8268
8320
|
if (bpm !== undefined && !Number.isFinite(bpm)) {
|
|
8269
8321
|
throw new Error(`Invalid --bpm: ${options.bpm}`);
|
|
8270
8322
|
}
|
|
8323
|
+
const embeddingRaw = options.embeddingFile ? readFileSync6(options.embeddingFile, "utf8") : options.embedding;
|
|
8324
|
+
const embedding = parseEmbeddingArg(embeddingRaw);
|
|
8271
8325
|
const result = await trackUpdateCommand2(trackId, {
|
|
8272
8326
|
bpm,
|
|
8327
|
+
embedding,
|
|
8273
8328
|
features: options.features,
|
|
8274
8329
|
key: options.key,
|
|
8275
8330
|
note: options.note,
|
|
@@ -8732,6 +8787,26 @@ async function runAdminEnrichQueue(options, enrichQueueCommand2) {
|
|
|
8732
8787
|
console.log(trackRows2(tracks).join(`
|
|
8733
8788
|
`));
|
|
8734
8789
|
}
|
|
8790
|
+
async function runAdminEmbedQueue(options, embedQueueCommand2) {
|
|
8791
|
+
const limit = parseListLimit(options.limit);
|
|
8792
|
+
const tracks = await embedQueueCommand2(limit);
|
|
8793
|
+
if (options.json) {
|
|
8794
|
+
printJson({
|
|
8795
|
+
ok: true,
|
|
8796
|
+
tracks
|
|
8797
|
+
});
|
|
8798
|
+
return;
|
|
8799
|
+
}
|
|
8800
|
+
if (tracks.length === 0) {
|
|
8801
|
+
console.log("Nothing awaiting an embedding. Every finding is embedded.");
|
|
8802
|
+
return;
|
|
8803
|
+
}
|
|
8804
|
+
const { trackRows: trackRows2 } = await Promise.resolve().then(() => (init_format2(), exports_format));
|
|
8805
|
+
const noun = tracks.length === 1 ? "finding" : "findings";
|
|
8806
|
+
console.log(`${tracks.length} ${noun} needing an audio embedding, oldest first:`);
|
|
8807
|
+
console.log(trackRows2(tracks).join(`
|
|
8808
|
+
`));
|
|
8809
|
+
}
|
|
8735
8810
|
async function runAdminVehicles(options, vehiclesCommand2) {
|
|
8736
8811
|
const limit = parseListLimit(options.limit);
|
|
8737
8812
|
const vehicles = await vehiclesCommand2(limit);
|
package/package.json
CHANGED