fluncle 0.128.0 → 0.130.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 +158 -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.
|
|
564
|
+
currentVersion = "0.130.0".trim() ? "0.130.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.
|
|
2192
|
+
currentVersion2 = "0.130.0".trim() ? "0.130.0".trim() : "0.1.0";
|
|
2193
2193
|
});
|
|
2194
2194
|
|
|
2195
2195
|
// ../../packages/registry/src/index.ts
|
|
@@ -2346,6 +2346,34 @@ var init_src = __esm(() => {
|
|
|
2346
2346
|
url: `${SITE}/artist`,
|
|
2347
2347
|
weights: { ssh: "secondary", web: "secondary" }
|
|
2348
2348
|
},
|
|
2349
|
+
{
|
|
2350
|
+
discoveryUrl: `${SITE}/llms.txt`,
|
|
2351
|
+
exposedContent: [
|
|
2352
|
+
"/labels — every record label Fluncle has found a banger on",
|
|
2353
|
+
"/label/:slug — one label: its findings, the artists on it, and the rest of its catalogue"
|
|
2354
|
+
],
|
|
2355
|
+
kind: "web_route",
|
|
2356
|
+
name: "web.labels",
|
|
2357
|
+
operatorNotes: "The label half of the graph (log ↔ artist ↔ label ↔ album). The INDEX is probeable (always 200); the slug page is not, so the probe targets /labels. A label below the renderable-track floor renders noindex + stays out of the sitemap (the ARTIST_INDEX_MIN_FINDINGS precedent). The page is BLIND to the label's crawl seed_state — that is crawl scope, never storage (docs/label-entity.md); the operator's ruling station is /admin/labels.",
|
|
2358
|
+
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
2359
|
+
route: "/labels",
|
|
2360
|
+
url: `${SITE}/labels`,
|
|
2361
|
+
weights: { web: "secondary" }
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
discoveryUrl: `${SITE}/llms.txt`,
|
|
2365
|
+
exposedContent: [
|
|
2366
|
+
"/albums — every record Fluncle has found a banger on",
|
|
2367
|
+
"/album/:slug — one record: its findings, its artists, its label, and the rest of its tracklist"
|
|
2368
|
+
],
|
|
2369
|
+
kind: "web_route",
|
|
2370
|
+
name: "web.albums",
|
|
2371
|
+
operatorNotes: "The album half of the graph, and the node that closes it: the album page carries the album → label edge (a link, plus `albumRelease.recordLabel` in its MusicAlbum JSON-LD). The INDEX is probeable (always 200); the slug page is not. Bounded by the archive, not the catalogue — an album earns a page by carrying a finding (docs/album-entity.md).",
|
|
2372
|
+
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
2373
|
+
route: "/albums",
|
|
2374
|
+
url: `${SITE}/albums`,
|
|
2375
|
+
weights: { web: "secondary" }
|
|
2376
|
+
},
|
|
2349
2377
|
{
|
|
2350
2378
|
exposedContent: [
|
|
2351
2379
|
"/galaxies — the browse-by-feel lens: the archive grouped into operator-named sonic galaxies (k-means over the MuQ audio embedding space)",
|
|
@@ -2982,6 +3010,17 @@ var init_src = __esm(() => {
|
|
|
2982
3010
|
probeConfig: { cadenceMs: 20 * MINUTE_MS, cronName: "fluncle-clip-drip", kind: "cron" },
|
|
2983
3011
|
weights: { status: "hidden" }
|
|
2984
3012
|
},
|
|
3013
|
+
{
|
|
3014
|
+
command: "fluncle admin publish pause",
|
|
3015
|
+
exposedContent: [
|
|
3016
|
+
"advance one freshly-rendered finding into the publish push — YouTube Short + TikTok inbox draft (--no-agent, Worker HTTP)"
|
|
3017
|
+
],
|
|
3018
|
+
kind: "cron",
|
|
3019
|
+
name: "cron.publish-advance",
|
|
3020
|
+
operatorNotes: "every 30m. Pure HTTP trigger, zero LLM tokens. The last autonomy gap: render finishes → this pushes, with no operator beat between. Admin tier (needs the Worker's Postiz key, which the box never sees — the box only triggers; the `drip_clips` / `capture_post_urls` precedent). SHIPS DARK: the Worker's kill switch is DEFAULT-DENY (only an explicit `false` in the `publish_advance_paused` setting runs it), so the timer ticks and posts nothing until `fluncle admin publish resume`. The Worker reads the switch FIRST, then advances at most ONE ready finding — both masters finalized, 15m settled, the whole bundle served on R2, the (track, platform) row CLAIMED atomically before any Postiz call, a rolling-24h cap of 6 pushes. A failed push is left `failed` for the operator and never auto-retried. Source: docs/agents/hermes/scripts/publish-advance-sweep.sh. Probed on /status as cron.publish-advance.",
|
|
3021
|
+
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-publish-advance", kind: "cron" },
|
|
3022
|
+
weights: { status: "hidden" }
|
|
3023
|
+
},
|
|
2985
3024
|
{
|
|
2986
3025
|
command: "fluncle admin tracks queue",
|
|
2987
3026
|
exposedContent: [
|
|
@@ -3531,6 +3570,7 @@ var init_track2 = __esm(() => {
|
|
|
3531
3570
|
var exports_admin_tracks = {};
|
|
3532
3571
|
__export(exports_admin_tracks, {
|
|
3533
3572
|
vehiclesCommand: () => vehiclesCommand,
|
|
3573
|
+
trackWorkCommand: () => trackWorkCommand,
|
|
3534
3574
|
requeueAnalysisCommand: () => requeueAnalysisCommand,
|
|
3535
3575
|
queueCommand: () => queueCommand,
|
|
3536
3576
|
observeQueueCommand: () => observeQueueCommand,
|
|
@@ -3632,6 +3672,15 @@ async function embedQueueCommand(limit) {
|
|
|
3632
3672
|
async function captureQueueCommand(limit) {
|
|
3633
3673
|
return fetchAdminTracks({ captureQueue: true, max: limit, order: "desc" });
|
|
3634
3674
|
}
|
|
3675
|
+
async function trackWorkCommand(options) {
|
|
3676
|
+
const params = new URLSearchParams({
|
|
3677
|
+
kind: options.kind,
|
|
3678
|
+
limit: String(Math.min(Math.max(1, options.limit), 200)),
|
|
3679
|
+
scope: options.scope
|
|
3680
|
+
});
|
|
3681
|
+
const response = await adminApiGet(`/api/admin/tracks/work?${params.toString()}`);
|
|
3682
|
+
return response.tracks ?? [];
|
|
3683
|
+
}
|
|
3635
3684
|
async function requeueAnalysisCommand(options) {
|
|
3636
3685
|
const findings = await fetchAdminTracks({ max: options.max, order: "asc" });
|
|
3637
3686
|
const stale = findings.filter((track) => track.analyzedFrom !== "full");
|
|
@@ -4097,6 +4146,23 @@ var init_mixtape_youtube2 = __esm(() => {
|
|
|
4097
4146
|
init_output();
|
|
4098
4147
|
});
|
|
4099
4148
|
|
|
4149
|
+
// src/commands/publish.ts
|
|
4150
|
+
var exports_publish = {};
|
|
4151
|
+
__export(exports_publish, {
|
|
4152
|
+
publishAdvancePauseCommand: () => publishAdvancePauseCommand,
|
|
4153
|
+
publishAdvanceCommand: () => publishAdvanceCommand
|
|
4154
|
+
});
|
|
4155
|
+
async function publishAdvanceCommand() {
|
|
4156
|
+
return adminApiPost("/api/admin/social/publish/advance", {});
|
|
4157
|
+
}
|
|
4158
|
+
async function publishAdvancePauseCommand(paused) {
|
|
4159
|
+
const response = await adminApiPut("/api/admin/social/publish/advance/state", { paused });
|
|
4160
|
+
return response.paused;
|
|
4161
|
+
}
|
|
4162
|
+
var init_publish = __esm(() => {
|
|
4163
|
+
init_api();
|
|
4164
|
+
});
|
|
4165
|
+
|
|
4100
4166
|
// ../../packages/contracts/src/util/multipart.ts
|
|
4101
4167
|
function planMultipart(contentLength, partSize = DEFAULT_PART_SIZE) {
|
|
4102
4168
|
if (!Number.isInteger(contentLength) || contentLength <= 0) {
|
|
@@ -7960,6 +8026,10 @@ function addAdminCommands(program2) {
|
|
|
7960
8026
|
const { captureQueueCommand: captureQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7961
8027
|
await runAdminCaptureQueue(options, captureQueueCommand2);
|
|
7962
8028
|
});
|
|
8029
|
+
adminTracks.command("work").description("The audio pipeline's worklist for one stage, in capture-priority order").requiredOption("--kind <kind>", "Pipeline stage: analyze | capture | embed").option("--scope <scope>", "Which half of the archive: all | catalogue | findings", "all").option("--limit <limit>", "Rows to show", "10").option("--json", "Print JSON", false).action(async (options) => {
|
|
8030
|
+
const { trackWorkCommand: trackWorkCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
8031
|
+
await runAdminTrackWork(options, trackWorkCommand2);
|
|
8032
|
+
});
|
|
7963
8033
|
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) => {
|
|
7964
8034
|
const { requeueAnalysisCommand: requeueAnalysisCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7965
8035
|
await runAdminRequeueAnalysis(options, requeueAnalysisCommand2);
|
|
@@ -8065,6 +8135,22 @@ function addAdminCommands(program2) {
|
|
|
8065
8135
|
const { mixtapeResyncCommand: mixtapeResyncCommand2 } = await Promise.resolve().then(() => (init_mixtapes(), exports_mixtapes));
|
|
8066
8136
|
await runMixtapeResync(idOrLogId, options, mixtapeResyncCommand2);
|
|
8067
8137
|
});
|
|
8138
|
+
const adminPublish = configureCommand(admin.command("publish").description("Render \u2192 publish auto-advance commands"));
|
|
8139
|
+
adminPublish.action(() => {
|
|
8140
|
+
adminPublish.outputHelp();
|
|
8141
|
+
});
|
|
8142
|
+
adminPublish.command("advance").description("Run one tick of the render \u2192 publish auto-advance (kill-switch aware)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
|
|
8143
|
+
const { publishAdvanceCommand: publishAdvanceCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8144
|
+
await runPublishAdvance(options, publishAdvanceCommand2);
|
|
8145
|
+
});
|
|
8146
|
+
adminPublish.command("pause").description("Pause the render \u2192 publish auto-advance, the kill switch (operator)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
|
|
8147
|
+
const { publishAdvancePauseCommand: publishAdvancePauseCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8148
|
+
await runPublishAdvancePause(true, options, publishAdvancePauseCommand2);
|
|
8149
|
+
});
|
|
8150
|
+
adminPublish.command("resume").description("Resume the render \u2192 publish auto-advance, clear the kill switch (operator)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
|
|
8151
|
+
const { publishAdvancePauseCommand: publishAdvancePauseCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8152
|
+
await runPublishAdvancePause(false, options, publishAdvancePauseCommand2);
|
|
8153
|
+
});
|
|
8068
8154
|
const adminClips = configureCommand(admin.command("clips").description("Mixtape clip (Fluncle Studio) commands"));
|
|
8069
8155
|
adminClips.action(() => {
|
|
8070
8156
|
adminClips.outputHelp();
|
|
@@ -9116,6 +9202,39 @@ async function runClipsDripPause(paused, options, clipDripPauseCommand2) {
|
|
|
9116
9202
|
}
|
|
9117
9203
|
console.log(result ? "The drip-feed is paused." : "The drip-feed is running.");
|
|
9118
9204
|
}
|
|
9205
|
+
async function runPublishAdvance(options, publishAdvanceCommand2) {
|
|
9206
|
+
const result = await publishAdvanceCommand2();
|
|
9207
|
+
if (options.json) {
|
|
9208
|
+
printJson(result);
|
|
9209
|
+
return;
|
|
9210
|
+
}
|
|
9211
|
+
if (result.paused) {
|
|
9212
|
+
console.log("Auto-advance is paused \u2014 nothing pushed.");
|
|
9213
|
+
return;
|
|
9214
|
+
}
|
|
9215
|
+
if (result.pushed.length === 0 && result.held.length === 0 && result.failed.length === 0) {
|
|
9216
|
+
console.log("Nothing ready to advance.");
|
|
9217
|
+
return;
|
|
9218
|
+
}
|
|
9219
|
+
for (const push of result.pushed) {
|
|
9220
|
+
console.log(`Pushed ${push.logId} to ${push.platform} (${push.status}).`);
|
|
9221
|
+
}
|
|
9222
|
+
for (const held of result.held) {
|
|
9223
|
+
const detail = held.missing?.length ? ` (${held.missing.join(", ")})` : "";
|
|
9224
|
+
console.log(`Held ${held.platform} for ${held.trackId}: ${held.reason}${detail}.`);
|
|
9225
|
+
}
|
|
9226
|
+
for (const failure of result.failed) {
|
|
9227
|
+
console.log(`Failed ${failure.platform} for ${failure.trackId} \u2014 left for the operator.`);
|
|
9228
|
+
}
|
|
9229
|
+
}
|
|
9230
|
+
async function runPublishAdvancePause(paused, options, publishAdvancePauseCommand2) {
|
|
9231
|
+
const result = await publishAdvancePauseCommand2(paused);
|
|
9232
|
+
if (options.json) {
|
|
9233
|
+
printJson({ ok: true, paused: result });
|
|
9234
|
+
return;
|
|
9235
|
+
}
|
|
9236
|
+
console.log(result ? "The publish auto-advance is paused." : "The publish auto-advance is running.");
|
|
9237
|
+
}
|
|
9119
9238
|
async function runMixtapeGet(idOrLogId, options, mixtapeGetCommand2) {
|
|
9120
9239
|
if (!idOrLogId) {
|
|
9121
9240
|
throw new Error("Missing mixtape id or log id. Usage: fluncle admin mixtapes get <id|logId>");
|
|
@@ -9629,6 +9748,42 @@ async function runAdminCaptureQueue(options, captureQueueCommand2) {
|
|
|
9629
9748
|
console.log(trackRows2(tracks).join(`
|
|
9630
9749
|
`));
|
|
9631
9750
|
}
|
|
9751
|
+
var TRACK_WORK_KINDS = new Set(["analyze", "capture", "embed"]);
|
|
9752
|
+
var TRACK_WORK_SCOPES = new Set(["all", "catalogue", "findings"]);
|
|
9753
|
+
async function runAdminTrackWork(options, trackWorkCommand2) {
|
|
9754
|
+
const kind = options.kind?.trim().toLowerCase() ?? "";
|
|
9755
|
+
const scope = options.scope?.trim().toLowerCase() ?? "all";
|
|
9756
|
+
if (!TRACK_WORK_KINDS.has(kind)) {
|
|
9757
|
+
console.error(`--kind must be one of: analyze, capture, embed (got "${options.kind}")`);
|
|
9758
|
+
process.exitCode = 1;
|
|
9759
|
+
return;
|
|
9760
|
+
}
|
|
9761
|
+
if (!TRACK_WORK_SCOPES.has(scope)) {
|
|
9762
|
+
console.error(`--scope must be one of: all, catalogue, findings (got "${options.scope}")`);
|
|
9763
|
+
process.exitCode = 1;
|
|
9764
|
+
return;
|
|
9765
|
+
}
|
|
9766
|
+
const tracks = await trackWorkCommand2({
|
|
9767
|
+
kind,
|
|
9768
|
+
limit: parseListLimit(options.limit),
|
|
9769
|
+
scope
|
|
9770
|
+
});
|
|
9771
|
+
if (options.json) {
|
|
9772
|
+
printJson({ ok: true, tracks });
|
|
9773
|
+
return;
|
|
9774
|
+
}
|
|
9775
|
+
if (tracks.length === 0) {
|
|
9776
|
+
console.log(`Nothing to ${kind}. The ${scope} queue is drained.`);
|
|
9777
|
+
return;
|
|
9778
|
+
}
|
|
9779
|
+
const noun = tracks.length === 1 ? "track" : "tracks";
|
|
9780
|
+
console.log(`${tracks.length} ${noun} to ${kind} (${scope}), in drain order:`);
|
|
9781
|
+
for (const track of tracks) {
|
|
9782
|
+
const who = track.logId ?? `${track.trackId} \xB7 catalogue`;
|
|
9783
|
+
const rung = track.capturePriority === null ? "" : ` \xB7 p${track.capturePriority}`;
|
|
9784
|
+
console.log(` ${who}${rung} \u2014 ${track.artists.join(", ")} \u2014 ${track.title}`);
|
|
9785
|
+
}
|
|
9786
|
+
}
|
|
9632
9787
|
var REQUEUE_ANALYSIS_ARCHIVE_CAP = 1e6;
|
|
9633
9788
|
function parseRequeueAnalysisLimit(value) {
|
|
9634
9789
|
if (value === undefined) {
|
|
@@ -9970,6 +10125,7 @@ var stringOptions = new Set([
|
|
|
9970
10125
|
"--scheduled-for",
|
|
9971
10126
|
"--script",
|
|
9972
10127
|
"--script-file",
|
|
10128
|
+
"--scope",
|
|
9973
10129
|
"--seed",
|
|
9974
10130
|
"--soundcloud-url",
|
|
9975
10131
|
"--source",
|
package/package.json
CHANGED