fluncle 0.110.0 → 0.112.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 +293 -8
- 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.112.0".trim() ? "0.112.0".trim() : "0.1.0";
|
|
561
561
|
});
|
|
562
562
|
|
|
563
563
|
// src/update-notifier.ts
|
|
@@ -961,6 +961,7 @@ function mapTrack(track) {
|
|
|
961
961
|
addedToSpotify: track.addedToSpotify,
|
|
962
962
|
album: track.album,
|
|
963
963
|
albumImageUrl: track.albumImageUrl,
|
|
964
|
+
analyzedFrom: track.analyzedFrom,
|
|
964
965
|
artists: track.artists,
|
|
965
966
|
bpm: track.bpm,
|
|
966
967
|
durationMs: track.durationMs,
|
|
@@ -1673,6 +1674,7 @@ function mapTrack2(track) {
|
|
|
1673
1674
|
addedToSpotify: track.addedToSpotify,
|
|
1674
1675
|
album: track.album,
|
|
1675
1676
|
albumImageUrl: track.albumImageUrl,
|
|
1677
|
+
analyzedFrom: track.analyzedFrom,
|
|
1676
1678
|
artists: track.artists,
|
|
1677
1679
|
bpm: track.bpm,
|
|
1678
1680
|
durationMs: track.durationMs,
|
|
@@ -2207,7 +2209,7 @@ function parseVersion2(version) {
|
|
|
2207
2209
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2208
2210
|
var init_version2 = __esm(() => {
|
|
2209
2211
|
init_output();
|
|
2210
|
-
currentVersion2 = "0.
|
|
2212
|
+
currentVersion2 = "0.112.0".trim() ? "0.112.0".trim() : "0.1.0";
|
|
2211
2213
|
});
|
|
2212
2214
|
|
|
2213
2215
|
// ../../packages/registry/src/index.ts
|
|
@@ -2650,11 +2652,26 @@ var init_src = __esm(() => {
|
|
|
2650
2652
|
url: `${SITE}/.well-known/agent-skills/index.json`,
|
|
2651
2653
|
weights: { web: "tertiary" }
|
|
2652
2654
|
},
|
|
2655
|
+
{
|
|
2656
|
+
apiFormat: "application/json+oembed",
|
|
2657
|
+
exposedContent: [
|
|
2658
|
+
"the oEmbed 1.0 provider — a pasted /log, /mixtapes, or /artist link unfurls as a rich finding card (Discord/Notion/WordPress/Ghost/…)",
|
|
2659
|
+
"html iframes the self-contained /embed/<logId> card; thumbnail is the finding's OG image"
|
|
2660
|
+
],
|
|
2661
|
+
kind: "discovery",
|
|
2662
|
+
name: "discovery.oembed",
|
|
2663
|
+
operatorNotes: 'GET /oembed?url=<a fluncle.com /log|/mixtapes|/artist URL>&format=json. Discovered via the `<link rel="alternate" type="application/json+oembed">` on the /log, mixtape, and /artist heads. XML → 501 (JSON only). No probeConfig — a bare GET without a valid `url` param is a 404, so there is no fixed URL to health-probe (like web.artist). The `rich` html frames /embed/<logId> (apps/web/src/routes/embed.$logId.ts), a standalone dark card served with a permissive `frame-ancestors *` CSP scoped to that route. Source: apps/web/src/routes/oembed.ts + src/lib/oembed.ts.',
|
|
2664
|
+
route: "/oembed",
|
|
2665
|
+
url: `${SITE}/oembed`,
|
|
2666
|
+
weights: { web: "tertiary" }
|
|
2667
|
+
},
|
|
2653
2668
|
{
|
|
2654
2669
|
apiFormat: "application/json",
|
|
2655
2670
|
discoveryUrl: `${SITE}/.well-known/mcp/server-card.json`,
|
|
2656
2671
|
exposedContent: [
|
|
2657
|
-
"the archive as MCP tools (Streamable HTTP, no auth): list_tracks, get_random_track, get_status, search_tracks, submit_track, subscribe_newsletter"
|
|
2672
|
+
"the archive as MCP tools (Streamable HTTP, no auth): list_tracks, get_track, get_random_track, get_status, search_tracks, submit_track, subscribe_newsletter",
|
|
2673
|
+
"the archive as MCP resources: each finding/mixtape at fluncle://finding/<logId> or fluncle://mixtape/<logId> (its public record)",
|
|
2674
|
+
"Fluncle-voiced MCP prompts: recommend_finding, walk_recent_night, decode_coordinate"
|
|
2658
2675
|
],
|
|
2659
2676
|
kind: "mcp",
|
|
2660
2677
|
name: "mcp.server",
|
|
@@ -3216,6 +3233,24 @@ async function trackUpdateCommand(trackId, options) {
|
|
|
3216
3233
|
if (options.note !== undefined) {
|
|
3217
3234
|
body.note = options.note;
|
|
3218
3235
|
}
|
|
3236
|
+
if (options.bpmSource !== undefined) {
|
|
3237
|
+
body.bpmSource = options.bpmSource;
|
|
3238
|
+
}
|
|
3239
|
+
if (options.bpmConfidence !== undefined) {
|
|
3240
|
+
body.bpmConfidence = options.bpmConfidence;
|
|
3241
|
+
}
|
|
3242
|
+
if (options.keySource !== undefined) {
|
|
3243
|
+
body.keySource = options.keySource;
|
|
3244
|
+
}
|
|
3245
|
+
if (options.keyConfidence !== undefined) {
|
|
3246
|
+
body.keyConfidence = options.keyConfidence;
|
|
3247
|
+
}
|
|
3248
|
+
if (options.analyzedFrom !== undefined) {
|
|
3249
|
+
body.analyzedFrom = options.analyzedFrom;
|
|
3250
|
+
}
|
|
3251
|
+
if (options.analyzedAt !== undefined) {
|
|
3252
|
+
body.analyzedAt = options.analyzedAt;
|
|
3253
|
+
}
|
|
3219
3254
|
return adminApiPatch(`/api/admin/tracks/${encodeURIComponent(trackId)}`, body);
|
|
3220
3255
|
}
|
|
3221
3256
|
async function trackRequeueVideoCommand(idOrLogId) {
|
|
@@ -3300,6 +3335,46 @@ var init_track = __esm(() => {
|
|
|
3300
3335
|
NON_FILE_OPTIONS = ["model", "reasoning"];
|
|
3301
3336
|
});
|
|
3302
3337
|
|
|
3338
|
+
// src/commands/admin-attention.ts
|
|
3339
|
+
var exports_admin_attention = {};
|
|
3340
|
+
__export(exports_admin_attention, {
|
|
3341
|
+
attentionQueueLines: () => attentionQueueLines,
|
|
3342
|
+
attentionQueueCommand: () => attentionQueueCommand
|
|
3343
|
+
});
|
|
3344
|
+
async function attentionQueueCommand() {
|
|
3345
|
+
const response = await adminApiGet("/api/admin/attention");
|
|
3346
|
+
return response.attention;
|
|
3347
|
+
}
|
|
3348
|
+
function attentionQueueLines(queue) {
|
|
3349
|
+
const lines = [queue.brief];
|
|
3350
|
+
if (queue.rows.length > 0) {
|
|
3351
|
+
const width = queue.rows.reduce((max, row) => Math.max(max, SOURCE_LABELS[row.source].length), 0);
|
|
3352
|
+
lines.push("");
|
|
3353
|
+
for (const row of queue.rows) {
|
|
3354
|
+
lines.push(` ${SOURCE_LABELS[row.source].padEnd(width)} ${row.title}`);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (queue.renderQueueDepth > 0) {
|
|
3358
|
+
const noun = queue.renderQueueDepth === 1 ? "finding" : "findings";
|
|
3359
|
+
lines.push("", `${queue.renderQueueDepth} ${noun} in the render queue.`);
|
|
3360
|
+
}
|
|
3361
|
+
return lines;
|
|
3362
|
+
}
|
|
3363
|
+
var SOURCE_LABELS;
|
|
3364
|
+
var init_admin_attention = __esm(() => {
|
|
3365
|
+
init_api();
|
|
3366
|
+
SOURCE_LABELS = {
|
|
3367
|
+
"artist-review": "artist links",
|
|
3368
|
+
"attach-cues": "cues",
|
|
3369
|
+
distribute: "distribute",
|
|
3370
|
+
"drip-empty": "clip drip",
|
|
3371
|
+
"post-tiktok": "tiktok",
|
|
3372
|
+
"post-youtube": "youtube",
|
|
3373
|
+
submission: "submission",
|
|
3374
|
+
"tiktok-draft": "tiktok draft"
|
|
3375
|
+
};
|
|
3376
|
+
});
|
|
3377
|
+
|
|
3303
3378
|
// src/commands/add.ts
|
|
3304
3379
|
var exports_add = {};
|
|
3305
3380
|
__export(exports_add, {
|
|
@@ -3326,10 +3401,60 @@ var init_add = __esm(() => {
|
|
|
3326
3401
|
init_api();
|
|
3327
3402
|
});
|
|
3328
3403
|
|
|
3404
|
+
// src/commands/track.ts
|
|
3405
|
+
async function trackUpdateCommand2(trackId, options) {
|
|
3406
|
+
const body = {};
|
|
3407
|
+
if (options.bpm !== undefined) {
|
|
3408
|
+
body.bpm = options.bpm;
|
|
3409
|
+
}
|
|
3410
|
+
if (options.key !== undefined) {
|
|
3411
|
+
body.key = options.key;
|
|
3412
|
+
}
|
|
3413
|
+
if (options.videoUrl !== undefined) {
|
|
3414
|
+
body.videoUrl = options.videoUrl;
|
|
3415
|
+
}
|
|
3416
|
+
if (options.status !== undefined) {
|
|
3417
|
+
body.enrichmentStatus = options.status;
|
|
3418
|
+
}
|
|
3419
|
+
if (options.features !== undefined) {
|
|
3420
|
+
body.features = options.features;
|
|
3421
|
+
}
|
|
3422
|
+
if (options.embedding !== undefined) {
|
|
3423
|
+
body.embedding = options.embedding;
|
|
3424
|
+
}
|
|
3425
|
+
if (options.note !== undefined) {
|
|
3426
|
+
body.note = options.note;
|
|
3427
|
+
}
|
|
3428
|
+
if (options.bpmSource !== undefined) {
|
|
3429
|
+
body.bpmSource = options.bpmSource;
|
|
3430
|
+
}
|
|
3431
|
+
if (options.bpmConfidence !== undefined) {
|
|
3432
|
+
body.bpmConfidence = options.bpmConfidence;
|
|
3433
|
+
}
|
|
3434
|
+
if (options.keySource !== undefined) {
|
|
3435
|
+
body.keySource = options.keySource;
|
|
3436
|
+
}
|
|
3437
|
+
if (options.keyConfidence !== undefined) {
|
|
3438
|
+
body.keyConfidence = options.keyConfidence;
|
|
3439
|
+
}
|
|
3440
|
+
if (options.analyzedFrom !== undefined) {
|
|
3441
|
+
body.analyzedFrom = options.analyzedFrom;
|
|
3442
|
+
}
|
|
3443
|
+
if (options.analyzedAt !== undefined) {
|
|
3444
|
+
body.analyzedAt = options.analyzedAt;
|
|
3445
|
+
}
|
|
3446
|
+
return adminApiPatch(`/api/admin/tracks/${encodeURIComponent(trackId)}`, body);
|
|
3447
|
+
}
|
|
3448
|
+
var init_track2 = __esm(() => {
|
|
3449
|
+
init_api();
|
|
3450
|
+
init_output();
|
|
3451
|
+
});
|
|
3452
|
+
|
|
3329
3453
|
// src/commands/admin-tracks.ts
|
|
3330
3454
|
var exports_admin_tracks = {};
|
|
3331
3455
|
__export(exports_admin_tracks, {
|
|
3332
3456
|
vehiclesCommand: () => vehiclesCommand,
|
|
3457
|
+
requeueAnalysisCommand: () => requeueAnalysisCommand,
|
|
3333
3458
|
queueCommand: () => queueCommand,
|
|
3334
3459
|
observeQueueCommand: () => observeQueueCommand,
|
|
3335
3460
|
noteQueueCommand: () => noteQueueCommand,
|
|
@@ -3429,6 +3554,44 @@ async function embedQueueCommand(limit) {
|
|
|
3429
3554
|
async function captureQueueCommand(limit) {
|
|
3430
3555
|
return fetchAdminTracks({ captureQueue: true, max: limit, order: "desc" });
|
|
3431
3556
|
}
|
|
3557
|
+
async function requeueAnalysisCommand(options) {
|
|
3558
|
+
const findings = await fetchAdminTracks({ max: options.max, order: "asc" });
|
|
3559
|
+
const stale = findings.filter((track) => track.analyzedFrom !== "full");
|
|
3560
|
+
const rows = stale.map((track) => ({
|
|
3561
|
+
analyzedFrom: track.analyzedFrom,
|
|
3562
|
+
bpm: track.bpm,
|
|
3563
|
+
hasSourceAudio: Boolean(track.sourceAudioKey),
|
|
3564
|
+
key: track.key,
|
|
3565
|
+
logId: track.logId,
|
|
3566
|
+
title: track.title,
|
|
3567
|
+
trackId: track.trackId
|
|
3568
|
+
}));
|
|
3569
|
+
const withSourceAudio = rows.filter((row) => row.hasSourceAudio);
|
|
3570
|
+
const withoutSourceAudio = rows.filter((row) => !row.hasSourceAudio);
|
|
3571
|
+
const requeued = [];
|
|
3572
|
+
const failed = [];
|
|
3573
|
+
if (options.apply) {
|
|
3574
|
+
for (const row of rows) {
|
|
3575
|
+
try {
|
|
3576
|
+
await trackUpdateCommand2(row.trackId, { status: "pending" });
|
|
3577
|
+
requeued.push(row.trackId);
|
|
3578
|
+
} catch (error) {
|
|
3579
|
+
failed.push({
|
|
3580
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3581
|
+
trackId: row.trackId
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
return {
|
|
3587
|
+
applied: options.apply,
|
|
3588
|
+
failed,
|
|
3589
|
+
requeued,
|
|
3590
|
+
scanned: findings.length,
|
|
3591
|
+
withSourceAudio,
|
|
3592
|
+
withoutSourceAudio
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3432
3595
|
async function contextQueueCommand(limit, retryEmptyContext = false) {
|
|
3433
3596
|
return fetchAdminTracks({ hasContext: false, max: limit, order: "asc", retryEmptyContext });
|
|
3434
3597
|
}
|
|
@@ -3478,6 +3641,7 @@ var pageSize3 = 48;
|
|
|
3478
3641
|
var init_admin_tracks = __esm(() => {
|
|
3479
3642
|
init_api();
|
|
3480
3643
|
init_recent2();
|
|
3644
|
+
init_track2();
|
|
3481
3645
|
});
|
|
3482
3646
|
|
|
3483
3647
|
// src/commands/preview-archive.ts
|
|
@@ -4682,6 +4846,7 @@ var init_add2 = __esm(() => {
|
|
|
4682
4846
|
// src/commands/submissions.ts
|
|
4683
4847
|
var exports_submissions = {};
|
|
4684
4848
|
__export(exports_submissions, {
|
|
4849
|
+
triageSubmissionCommand: () => triageSubmissionCommand,
|
|
4685
4850
|
reviewSubmissionCommand: () => reviewSubmissionCommand,
|
|
4686
4851
|
rejectSubmissionCommand: () => rejectSubmissionCommand,
|
|
4687
4852
|
listSubmissionsCommand: () => listSubmissionsCommand,
|
|
@@ -4743,6 +4908,14 @@ async function approveSubmissionCommand(submissionId, options = {}) {
|
|
|
4743
4908
|
}
|
|
4744
4909
|
console.log(`Approved ${formatTrackLine(submission)}.`);
|
|
4745
4910
|
}
|
|
4911
|
+
async function triageSubmissionCommand(submissionId, verdict, options = {}) {
|
|
4912
|
+
const response = await adminApiPost(`/api/admin/submissions/${encodeURIComponent(submissionId)}/triage`, { verdict });
|
|
4913
|
+
if (options.json) {
|
|
4914
|
+
printJson2({ ok: true, submission: response.submission });
|
|
4915
|
+
return;
|
|
4916
|
+
}
|
|
4917
|
+
console.log(`Triaged ${formatTrackLine(response.submission)}: ${response.submission.triageVerdict ?? ""}`);
|
|
4918
|
+
}
|
|
4746
4919
|
async function fetchSubmission(submissionId) {
|
|
4747
4920
|
const response = await adminApiGet(`/api/admin/submissions/${encodeURIComponent(submissionId)}`);
|
|
4748
4921
|
return response.submission;
|
|
@@ -7568,6 +7741,10 @@ function addAdminCommands(program2) {
|
|
|
7568
7741
|
admin.command("help", { hidden: true }).description("display help for command").action(() => {
|
|
7569
7742
|
admin.outputHelp();
|
|
7570
7743
|
});
|
|
7744
|
+
admin.command("queue").description("The attention queue: what's waiting, and the day's dispatch").option("--json", "Print JSON", false).action(async (options) => {
|
|
7745
|
+
const { attentionQueueCommand: attentionQueueCommand2 } = await Promise.resolve().then(() => (init_admin_attention(), exports_admin_attention));
|
|
7746
|
+
await runAdminAttention(options, attentionQueueCommand2);
|
|
7747
|
+
});
|
|
7571
7748
|
const adminTracks = configureCommand(admin.command("tracks").description("Track admin commands"));
|
|
7572
7749
|
adminTracks.action(() => {
|
|
7573
7750
|
adminTracks.outputHelp();
|
|
@@ -7611,6 +7788,10 @@ function addAdminCommands(program2) {
|
|
|
7611
7788
|
const { captureQueueCommand: captureQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7612
7789
|
await runAdminCaptureQueue(options, captureQueueCommand2);
|
|
7613
7790
|
});
|
|
7791
|
+
adminTracks.command("requeue-analysis").description("Re-queue findings whose BPM/key are preview-grade (dry-run; --apply to flip)").option("--apply", "Actually flip statuses (default is a dry-run preview)", false).option("--limit <limit>", "Cap the archive walk (default: the whole archive)").option("--json", "Print JSON", false).action(async (options) => {
|
|
7792
|
+
const { requeueAnalysisCommand: requeueAnalysisCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7793
|
+
await runAdminRequeueAnalysis(options, requeueAnalysisCommand2);
|
|
7794
|
+
});
|
|
7614
7795
|
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) => {
|
|
7615
7796
|
const { vehiclesCommand: vehiclesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7616
7797
|
await runAdminVehicles(options, vehiclesCommand2);
|
|
@@ -7620,9 +7801,9 @@ function addAdminCommands(program2) {
|
|
|
7620
7801
|
const { trackGetAdminCommand: trackGetAdminCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7621
7802
|
await runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2);
|
|
7622
7803
|
});
|
|
7623
|
-
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) => {
|
|
7624
|
-
const { trackUpdateCommand:
|
|
7625
|
-
await runTrackUpdate(trackId, options,
|
|
7804
|
+
adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--analyzed-at <iso>", "Analysis-write ISO timestamp (BPM/key provenance)").option("--analyzed-from <class>", "Audio class BPM/key were analyzed from: full or preview").option("--bpm <number>", "Track BPM").option("--bpm-confidence <number>", "Analyzer confidence in the BPM (0..1)").option("--bpm-source <source>", "Where the BPM came from (analyzer bpmSource)").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("--key-confidence <number>", "Analyzer confidence in the key (0..1)").option("--key-source <source>", "Where the key came from (analyzer keySource)").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
|
|
7805
|
+
const { trackUpdateCommand: trackUpdateCommand3 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7806
|
+
await runTrackUpdate(trackId, options, trackUpdateCommand3);
|
|
7626
7807
|
});
|
|
7627
7808
|
adminTrack.command("video").description("Upload a track's video bundle to R2 and link it").argument("[idOrLogId]").option("--allow-partial", "Allow an intentionally partial upload (skip the re-render-contract check; e.g. poster-only)", false).option("--composition <file>", "Composition source file").option("--cover <file>", "Cover image").option("--dir <dir>", "Bundle directory").option("--footage <file>", "Video footage (square crop source)").option("--footage-landscape <file>", "Clean landscape cut (optional escape hatch)").option("--footage-landscape-social <file>", "Landscape cut with baked text (optional)").option("--footage-notext <file>", "Portrait cut without the type layer (optional)").option("--footage-social <file>", "Portrait social cut (baked text)").option("--intent <file>", "Render-intent JSON (optional)").option("--json", "Print JSON", false).option("--metrics <file>", "Gate metrics JSON (optional)").option("--model <model>", "Authoring AI model (<provider>/<model>)").option("--note <file>", "Note file").option("--plate <file>", "Plate-lane photographic plate (plate.png; uploadable pre-render)").option("--plate-background <file>", "The plate's subject-removed background (plate.background.png, optional)").option("--poster <file>", "Poster image").option("--props <file>", "Render props JSON").option("--reasoning <level>", "Authoring model reasoning effort (e.g. high)").option("--render <file>", "Render metadata JSON").option("--scene <file>", "Scene replay manifest JSON (fluncle.scene/1, optional)").allowExcessArguments().action(async (idOrLogId, options) => {
|
|
7628
7809
|
const { trackVideoCommand: trackVideoCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
@@ -7814,6 +7995,17 @@ function addAdminCommands(program2) {
|
|
|
7814
7995
|
const { approveSubmissionCommand: approveSubmissionCommand2 } = await Promise.resolve().then(() => (init_submissions(), exports_submissions));
|
|
7815
7996
|
await approveSubmissionCommand2(submissionId, options);
|
|
7816
7997
|
});
|
|
7998
|
+
submissions.command("triage").description("Write the pre-chew triage verdict onto a pending submission").argument("[submissionId]").option("--verdict <text>", "The triage verdict one-liner").option("--verdict-file <file>", "Read the verdict from a file").option("--json", "Print JSON", false).action(async (submissionId, options) => {
|
|
7999
|
+
if (!submissionId) {
|
|
8000
|
+
throw new Error("Missing submission id for: triage");
|
|
8001
|
+
}
|
|
8002
|
+
const verdict = options.verdictFile ? readFileSync5(options.verdictFile, "utf8") : options.verdict;
|
|
8003
|
+
if (!verdict || !verdict.trim()) {
|
|
8004
|
+
throw new Error("Usage: fluncle admin submissions triage <submissionId> (--verdict <text> | --verdict-file <file>) [--json]");
|
|
8005
|
+
}
|
|
8006
|
+
const { triageSubmissionCommand: triageSubmissionCommand2 } = await Promise.resolve().then(() => (init_submissions(), exports_submissions));
|
|
8007
|
+
await triageSubmissionCommand2(submissionId, verdict, { json: options.json });
|
|
8008
|
+
});
|
|
7817
8009
|
const auth = configureCommand(admin.command("auth").description("Authentication commands"));
|
|
7818
8010
|
auth.command("spotify").description("Authorize Spotify access").action(async () => {
|
|
7819
8011
|
const { authSpotifyCommand: authSpotifyCommand2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
|
|
@@ -8437,7 +8629,7 @@ function parseEmbeddingArg(raw) {
|
|
|
8437
8629
|
}
|
|
8438
8630
|
return parsed;
|
|
8439
8631
|
}
|
|
8440
|
-
async function runTrackUpdate(trackId, options,
|
|
8632
|
+
async function runTrackUpdate(trackId, options, trackUpdateCommand3) {
|
|
8441
8633
|
if (!trackId) {
|
|
8442
8634
|
throw new Error("Missing track id. Usage: fluncle admin tracks update <track_id>");
|
|
8443
8635
|
}
|
|
@@ -8445,13 +8637,30 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
|
|
|
8445
8637
|
if (bpm !== undefined && !Number.isFinite(bpm)) {
|
|
8446
8638
|
throw new Error(`Invalid --bpm: ${options.bpm}`);
|
|
8447
8639
|
}
|
|
8640
|
+
const bpmConfidence = options.bpmConfidence === undefined ? undefined : Number(options.bpmConfidence);
|
|
8641
|
+
if (bpmConfidence !== undefined && !Number.isFinite(bpmConfidence)) {
|
|
8642
|
+
throw new Error(`Invalid --bpm-confidence: ${options.bpmConfidence}`);
|
|
8643
|
+
}
|
|
8644
|
+
const keyConfidence = options.keyConfidence === undefined ? undefined : Number(options.keyConfidence);
|
|
8645
|
+
if (keyConfidence !== undefined && !Number.isFinite(keyConfidence)) {
|
|
8646
|
+
throw new Error(`Invalid --key-confidence: ${options.keyConfidence}`);
|
|
8647
|
+
}
|
|
8648
|
+
if (options.analyzedFrom !== undefined && options.analyzedFrom !== "full" && options.analyzedFrom !== "preview") {
|
|
8649
|
+
throw new Error(`Invalid --analyzed-from: ${options.analyzedFrom} (expected full or preview)`);
|
|
8650
|
+
}
|
|
8448
8651
|
const embeddingRaw = options.embeddingFile ? readFileSync5(options.embeddingFile, "utf8") : options.embedding;
|
|
8449
8652
|
const embedding = parseEmbeddingArg(embeddingRaw);
|
|
8450
|
-
const result = await
|
|
8653
|
+
const result = await trackUpdateCommand3(trackId, {
|
|
8654
|
+
analyzedAt: options.analyzedAt,
|
|
8655
|
+
analyzedFrom: options.analyzedFrom,
|
|
8451
8656
|
bpm,
|
|
8657
|
+
bpmConfidence,
|
|
8658
|
+
bpmSource: options.bpmSource,
|
|
8452
8659
|
embedding,
|
|
8453
8660
|
features: options.features,
|
|
8454
8661
|
key: options.key,
|
|
8662
|
+
keyConfidence,
|
|
8663
|
+
keySource: options.keySource,
|
|
8455
8664
|
note: options.note,
|
|
8456
8665
|
status: options.status,
|
|
8457
8666
|
videoUrl: options.videoUrl
|
|
@@ -8835,6 +9044,16 @@ async function runAdminTracksList(options, listCommand2) {
|
|
|
8835
9044
|
console.log(trackRows2(tracks).join(`
|
|
8836
9045
|
`));
|
|
8837
9046
|
}
|
|
9047
|
+
async function runAdminAttention(options, attentionQueueCommand2) {
|
|
9048
|
+
const queue = await attentionQueueCommand2();
|
|
9049
|
+
if (options.json) {
|
|
9050
|
+
printJson({ attention: queue, ok: true });
|
|
9051
|
+
return;
|
|
9052
|
+
}
|
|
9053
|
+
const { attentionQueueLines: attentionQueueLines2 } = await Promise.resolve().then(() => (init_admin_attention(), exports_admin_attention));
|
|
9054
|
+
console.log(attentionQueueLines2(queue).join(`
|
|
9055
|
+
`));
|
|
9056
|
+
}
|
|
8838
9057
|
async function runAdminQueue(options, queueCommand2) {
|
|
8839
9058
|
const limit = parseListLimit(options.limit);
|
|
8840
9059
|
const tracks = await queueCommand2(limit, {
|
|
@@ -8979,6 +9198,66 @@ async function runAdminCaptureQueue(options, captureQueueCommand2) {
|
|
|
8979
9198
|
console.log(trackRows2(tracks).join(`
|
|
8980
9199
|
`));
|
|
8981
9200
|
}
|
|
9201
|
+
var REQUEUE_ANALYSIS_ARCHIVE_CAP = 1e6;
|
|
9202
|
+
function parseRequeueAnalysisLimit(value) {
|
|
9203
|
+
if (value === undefined) {
|
|
9204
|
+
return REQUEUE_ANALYSIS_ARCHIVE_CAP;
|
|
9205
|
+
}
|
|
9206
|
+
const limit = Number.parseInt(value, 10);
|
|
9207
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
9208
|
+
throw new Error("Limit must be a positive integer");
|
|
9209
|
+
}
|
|
9210
|
+
return limit;
|
|
9211
|
+
}
|
|
9212
|
+
async function runAdminRequeueAnalysis(options, requeueAnalysisCommand2) {
|
|
9213
|
+
const max = parseRequeueAnalysisLimit(options.limit);
|
|
9214
|
+
const result = await requeueAnalysisCommand2({ apply: options.apply === true, max });
|
|
9215
|
+
if (options.json) {
|
|
9216
|
+
printJson({ ok: true, ...result });
|
|
9217
|
+
return;
|
|
9218
|
+
}
|
|
9219
|
+
const { coordinate: coordinate3 } = await Promise.resolve().then(() => (init_format2(), exports_format));
|
|
9220
|
+
const staleCount = result.withSourceAudio.length + result.withoutSourceAudio.length;
|
|
9221
|
+
if (staleCount === 0) {
|
|
9222
|
+
console.log("Nothing to re-queue. Every finding's BPM/key was analyzed from full audio.");
|
|
9223
|
+
return;
|
|
9224
|
+
}
|
|
9225
|
+
const describe = (row) => {
|
|
9226
|
+
const coord = coordinate3({ logId: row.logId });
|
|
9227
|
+
const bpm = row.bpm ? `${row.bpm} bpm` : "no bpm";
|
|
9228
|
+
const key = row.key ?? "no key";
|
|
9229
|
+
const from = row.analyzedFrom ?? "legacy/null";
|
|
9230
|
+
return ` ${coord} ${row.trackId} ${row.title} \u2014 ${bpm}, ${key} (from ${from})`;
|
|
9231
|
+
};
|
|
9232
|
+
console.log(`${staleCount} finding${staleCount === 1 ? "" : "s"} with preview-grade BPM/key (scanned ${result.scanned}):`);
|
|
9233
|
+
if (result.withSourceAudio.length > 0) {
|
|
9234
|
+
console.log(`
|
|
9235
|
+
Captured full song on file \u2014 re-derives from full audio (${result.withSourceAudio.length}):`);
|
|
9236
|
+
console.log(result.withSourceAudio.map(describe).join(`
|
|
9237
|
+
`));
|
|
9238
|
+
}
|
|
9239
|
+
if (result.withoutSourceAudio.length > 0) {
|
|
9240
|
+
console.log(`
|
|
9241
|
+
No captured full song \u2014 re-derives from the 30s preview, still an upgrade with the fixed estimator (${result.withoutSourceAudio.length}):`);
|
|
9242
|
+
console.log(result.withoutSourceAudio.map(describe).join(`
|
|
9243
|
+
`));
|
|
9244
|
+
}
|
|
9245
|
+
console.log(`
|
|
9246
|
+
Caveat: keys backfilled from Rekordbox are indistinguishable from DSP keys (no legacy` + " provenance) and a re-enrichment may overwrite them. This dry-run diff is the gate \u2014" + " review it before applying.");
|
|
9247
|
+
if (!result.applied) {
|
|
9248
|
+
console.log(`
|
|
9249
|
+
Dry-run \u2014 nothing flipped. Re-run with --apply to re-queue all ${staleCount}.`);
|
|
9250
|
+
return;
|
|
9251
|
+
}
|
|
9252
|
+
console.log(`
|
|
9253
|
+
Applied \u2014 re-queued ${result.requeued.length} of ${staleCount}.`);
|
|
9254
|
+
if (result.failed.length > 0) {
|
|
9255
|
+
console.log(`${result.failed.length} failed:`);
|
|
9256
|
+
console.log(result.failed.map((entry) => ` ${entry.trackId}: ${entry.error}`).join(`
|
|
9257
|
+
`));
|
|
9258
|
+
process.exitCode = 1;
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
8982
9261
|
async function runAdminVehicles(options, vehiclesCommand2) {
|
|
8983
9262
|
const limit = parseListLimit(options.limit);
|
|
8984
9263
|
const vehicles = await vehiclesCommand2(limit);
|
|
@@ -9203,7 +9482,11 @@ function normalizeCommanderError(error) {
|
|
|
9203
9482
|
return new Error(message);
|
|
9204
9483
|
}
|
|
9205
9484
|
var stringOptions = new Set([
|
|
9485
|
+
"--analyzed-at",
|
|
9486
|
+
"--analyzed-from",
|
|
9206
9487
|
"--bpm",
|
|
9488
|
+
"--bpm-confidence",
|
|
9489
|
+
"--bpm-source",
|
|
9207
9490
|
"--composition",
|
|
9208
9491
|
"--cover",
|
|
9209
9492
|
"--dir",
|
|
@@ -9218,6 +9501,8 @@ var stringOptions = new Set([
|
|
|
9218
9501
|
"--from",
|
|
9219
9502
|
"--intent",
|
|
9220
9503
|
"--key",
|
|
9504
|
+
"--key-confidence",
|
|
9505
|
+
"--key-source",
|
|
9221
9506
|
"--limit",
|
|
9222
9507
|
"--metrics",
|
|
9223
9508
|
"--mime",
|
package/package.json
CHANGED