fluncle 0.127.0 → 0.129.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 +178 -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.129.0".trim() ? "0.129.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.129.0".trim() ? "0.129.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: [
|
|
@@ -4097,6 +4136,23 @@ var init_mixtape_youtube2 = __esm(() => {
|
|
|
4097
4136
|
init_output();
|
|
4098
4137
|
});
|
|
4099
4138
|
|
|
4139
|
+
// src/commands/publish.ts
|
|
4140
|
+
var exports_publish = {};
|
|
4141
|
+
__export(exports_publish, {
|
|
4142
|
+
publishAdvancePauseCommand: () => publishAdvancePauseCommand,
|
|
4143
|
+
publishAdvanceCommand: () => publishAdvanceCommand
|
|
4144
|
+
});
|
|
4145
|
+
async function publishAdvanceCommand() {
|
|
4146
|
+
return adminApiPost("/api/admin/social/publish/advance", {});
|
|
4147
|
+
}
|
|
4148
|
+
async function publishAdvancePauseCommand(paused) {
|
|
4149
|
+
const response = await adminApiPut("/api/admin/social/publish/advance/state", { paused });
|
|
4150
|
+
return response.paused;
|
|
4151
|
+
}
|
|
4152
|
+
var init_publish = __esm(() => {
|
|
4153
|
+
init_api();
|
|
4154
|
+
});
|
|
4155
|
+
|
|
4100
4156
|
// ../../packages/contracts/src/util/multipart.ts
|
|
4101
4157
|
function planMultipart(contentLength, partSize = DEFAULT_PART_SIZE) {
|
|
4102
4158
|
if (!Number.isInteger(contentLength) || contentLength <= 0) {
|
|
@@ -5295,6 +5351,29 @@ var init_auth_lastfm = __esm(() => {
|
|
|
5295
5351
|
init_api();
|
|
5296
5352
|
});
|
|
5297
5353
|
|
|
5354
|
+
// src/commands/admin-catalogue.ts
|
|
5355
|
+
var exports_admin_catalogue = {};
|
|
5356
|
+
__export(exports_admin_catalogue, {
|
|
5357
|
+
catalogueRankCommand: () => catalogueRankCommand,
|
|
5358
|
+
catalogueListCommand: () => catalogueListCommand
|
|
5359
|
+
});
|
|
5360
|
+
async function catalogueRankCommand(options) {
|
|
5361
|
+
const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
|
|
5362
|
+
const response = await adminApiPost("/api/admin/catalogue/rank", limit ? { limit } : {});
|
|
5363
|
+
return response.summary;
|
|
5364
|
+
}
|
|
5365
|
+
async function catalogueListCommand(options) {
|
|
5366
|
+
const params = new URLSearchParams;
|
|
5367
|
+
params.set("lens", options.lens === "capture" ? "capture" : "ear");
|
|
5368
|
+
if (options.limit) {
|
|
5369
|
+
params.set("limit", options.limit);
|
|
5370
|
+
}
|
|
5371
|
+
return adminApiGet(`/api/admin/catalogue?${params.toString()}`);
|
|
5372
|
+
}
|
|
5373
|
+
var init_admin_catalogue = __esm(() => {
|
|
5374
|
+
init_api();
|
|
5375
|
+
});
|
|
5376
|
+
|
|
5298
5377
|
// src/commands/admin-artists.ts
|
|
5299
5378
|
var exports_admin_artists = {};
|
|
5300
5379
|
__export(exports_admin_artists, {
|
|
@@ -8042,6 +8121,22 @@ function addAdminCommands(program2) {
|
|
|
8042
8121
|
const { mixtapeResyncCommand: mixtapeResyncCommand2 } = await Promise.resolve().then(() => (init_mixtapes(), exports_mixtapes));
|
|
8043
8122
|
await runMixtapeResync(idOrLogId, options, mixtapeResyncCommand2);
|
|
8044
8123
|
});
|
|
8124
|
+
const adminPublish = configureCommand(admin.command("publish").description("Render \u2192 publish auto-advance commands"));
|
|
8125
|
+
adminPublish.action(() => {
|
|
8126
|
+
adminPublish.outputHelp();
|
|
8127
|
+
});
|
|
8128
|
+
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) => {
|
|
8129
|
+
const { publishAdvanceCommand: publishAdvanceCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8130
|
+
await runPublishAdvance(options, publishAdvanceCommand2);
|
|
8131
|
+
});
|
|
8132
|
+
adminPublish.command("pause").description("Pause the render \u2192 publish auto-advance, the kill switch (operator)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
|
|
8133
|
+
const { publishAdvancePauseCommand: publishAdvancePauseCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8134
|
+
await runPublishAdvancePause(true, options, publishAdvancePauseCommand2);
|
|
8135
|
+
});
|
|
8136
|
+
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) => {
|
|
8137
|
+
const { publishAdvancePauseCommand: publishAdvancePauseCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
8138
|
+
await runPublishAdvancePause(false, options, publishAdvancePauseCommand2);
|
|
8139
|
+
});
|
|
8045
8140
|
const adminClips = configureCommand(admin.command("clips").description("Mixtape clip (Fluncle Studio) commands"));
|
|
8046
8141
|
adminClips.action(() => {
|
|
8047
8142
|
adminClips.outputHelp();
|
|
@@ -8212,6 +8307,18 @@ function addAdminCommands(program2) {
|
|
|
8212
8307
|
const { galaxyMapWriteCommand: galaxyMapWriteCommand2 } = await Promise.resolve().then(() => (init_galaxies(), exports_galaxies));
|
|
8213
8308
|
await runGalaxyMapWrite(options, galaxyMapWriteCommand2);
|
|
8214
8309
|
});
|
|
8310
|
+
const catalogue = configureCommand(admin.command("catalogue").description("The catalogue (uncertified tracks) + its ranking"));
|
|
8311
|
+
catalogue.action(() => {
|
|
8312
|
+
catalogue.outputHelp();
|
|
8313
|
+
});
|
|
8314
|
+
catalogue.command("rank").description("Run one tick of the ranking sweep (nearest finding + capture priority)").option("--limit <limit>", "Candidates per tick (default 250)").option("--json", "Print JSON", false).action(async (options) => {
|
|
8315
|
+
const { catalogueRankCommand: catalogueRankCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8316
|
+
await runCatalogueRank(options, catalogueRankCommand2);
|
|
8317
|
+
});
|
|
8318
|
+
catalogue.command("list").description("The ranked catalogue: closest to a finding (ear), or next to capture").option("--lens <lens>", "ear (default) or capture", "ear").option("--limit <limit>", "Rows (default 50, max 200)").option("--json", "Print JSON", false).action(async (options) => {
|
|
8319
|
+
const { catalogueListCommand: catalogueListCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8320
|
+
await runCatalogueList(options, catalogueListCommand2);
|
|
8321
|
+
});
|
|
8215
8322
|
const backfill = configureCommand(admin.command("backfills").description("Backfill operator-only archives"));
|
|
8216
8323
|
backfill.action(() => {
|
|
8217
8324
|
backfill.outputHelp();
|
|
@@ -9081,6 +9188,39 @@ async function runClipsDripPause(paused, options, clipDripPauseCommand2) {
|
|
|
9081
9188
|
}
|
|
9082
9189
|
console.log(result ? "The drip-feed is paused." : "The drip-feed is running.");
|
|
9083
9190
|
}
|
|
9191
|
+
async function runPublishAdvance(options, publishAdvanceCommand2) {
|
|
9192
|
+
const result = await publishAdvanceCommand2();
|
|
9193
|
+
if (options.json) {
|
|
9194
|
+
printJson(result);
|
|
9195
|
+
return;
|
|
9196
|
+
}
|
|
9197
|
+
if (result.paused) {
|
|
9198
|
+
console.log("Auto-advance is paused \u2014 nothing pushed.");
|
|
9199
|
+
return;
|
|
9200
|
+
}
|
|
9201
|
+
if (result.pushed.length === 0 && result.held.length === 0 && result.failed.length === 0) {
|
|
9202
|
+
console.log("Nothing ready to advance.");
|
|
9203
|
+
return;
|
|
9204
|
+
}
|
|
9205
|
+
for (const push of result.pushed) {
|
|
9206
|
+
console.log(`Pushed ${push.logId} to ${push.platform} (${push.status}).`);
|
|
9207
|
+
}
|
|
9208
|
+
for (const held of result.held) {
|
|
9209
|
+
const detail = held.missing?.length ? ` (${held.missing.join(", ")})` : "";
|
|
9210
|
+
console.log(`Held ${held.platform} for ${held.trackId}: ${held.reason}${detail}.`);
|
|
9211
|
+
}
|
|
9212
|
+
for (const failure of result.failed) {
|
|
9213
|
+
console.log(`Failed ${failure.platform} for ${failure.trackId} \u2014 left for the operator.`);
|
|
9214
|
+
}
|
|
9215
|
+
}
|
|
9216
|
+
async function runPublishAdvancePause(paused, options, publishAdvancePauseCommand2) {
|
|
9217
|
+
const result = await publishAdvancePauseCommand2(paused);
|
|
9218
|
+
if (options.json) {
|
|
9219
|
+
printJson({ ok: true, paused: result });
|
|
9220
|
+
return;
|
|
9221
|
+
}
|
|
9222
|
+
console.log(result ? "The publish auto-advance is paused." : "The publish auto-advance is running.");
|
|
9223
|
+
}
|
|
9084
9224
|
async function runMixtapeGet(idOrLogId, options, mixtapeGetCommand2) {
|
|
9085
9225
|
if (!idOrLogId) {
|
|
9086
9226
|
throw new Error("Missing mixtape id or log id. Usage: fluncle admin mixtapes get <id|logId>");
|
|
@@ -9314,6 +9454,41 @@ async function runGalaxies(slug, options, commands) {
|
|
|
9314
9454
|
console.log(`${galaxy.name.padEnd(40)} ${String(galaxy.memberCount).padStart(3)} finding${galaxy.memberCount === 1 ? "" : "s"}`);
|
|
9315
9455
|
}
|
|
9316
9456
|
}
|
|
9457
|
+
async function runCatalogueRank(options, catalogueRankCommand2) {
|
|
9458
|
+
const summary = await catalogueRankCommand2({ limit: options.limit });
|
|
9459
|
+
if (options.json) {
|
|
9460
|
+
printJson({ ok: true, summary });
|
|
9461
|
+
return;
|
|
9462
|
+
}
|
|
9463
|
+
console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture. ${summary.remaining} still stale.`);
|
|
9464
|
+
}
|
|
9465
|
+
async function runCatalogueList(options, catalogueListCommand2) {
|
|
9466
|
+
const { summary, tracks } = await catalogueListCommand2({
|
|
9467
|
+
lens: options.lens,
|
|
9468
|
+
limit: options.limit
|
|
9469
|
+
});
|
|
9470
|
+
if (options.json) {
|
|
9471
|
+
printJson({ ok: true, summary, tracks });
|
|
9472
|
+
return;
|
|
9473
|
+
}
|
|
9474
|
+
if (tracks.length === 0) {
|
|
9475
|
+
console.log("Nothing out there yet.");
|
|
9476
|
+
return;
|
|
9477
|
+
}
|
|
9478
|
+
for (const track of tracks) {
|
|
9479
|
+
const identity = `${track.artists.join(", ")} \u2014 ${track.title}`;
|
|
9480
|
+
if (options.lens === "capture") {
|
|
9481
|
+
const reason = track.captureReason;
|
|
9482
|
+
const why2 = reason?.kind === "artist" ? `${reason.name} is already in the archive` : reason?.kind === "label" ? `${reason.name} already carries a finding` : reason?.kind === "seed-label" ? `${reason.name} is a label the crawler digs from` : reason?.kind === "skipped-label" ? `${reason.name} is not your lane \u2014 ranked last, kept anyway` : "nothing ties it to the archive yet";
|
|
9483
|
+
console.log(`${String(track.capturePriority ?? 0)} ${identity.padEnd(52)} ${why2}`);
|
|
9484
|
+
continue;
|
|
9485
|
+
}
|
|
9486
|
+
const match = track.nearestFinding;
|
|
9487
|
+
const score = track.nearestFindingScore?.toFixed(2) ?? "\u2014";
|
|
9488
|
+
const why = match ? `closest to ${match.logId ? `${match.logId} ` : ""}${match.artists.join(", ")} \u2014 ${match.title}` : "nothing to compare it to yet";
|
|
9489
|
+
console.log(`${score} ${identity.padEnd(52)} ${why}`);
|
|
9490
|
+
}
|
|
9491
|
+
}
|
|
9317
9492
|
async function runGalaxyMapRead(options, galaxyMapReadCommand2) {
|
|
9318
9493
|
const galaxies = await galaxyMapReadCommand2();
|
|
9319
9494
|
if (options.json) {
|
|
@@ -9878,6 +10053,7 @@ var stringOptions = new Set([
|
|
|
9878
10053
|
"--key-confidence",
|
|
9879
10054
|
"--key-source",
|
|
9880
10055
|
"--kind",
|
|
10056
|
+
"--lens",
|
|
9881
10057
|
"--limit",
|
|
9882
10058
|
"--metrics",
|
|
9883
10059
|
"--mime",
|
package/package.json
CHANGED