fluncle 0.206.0 → 0.208.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 +40 -12
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -610,7 +610,7 @@ function parseVersion(version) {
|
|
|
610
610
|
var currentVersion;
|
|
611
611
|
var init_version = __esm(() => {
|
|
612
612
|
init_output();
|
|
613
|
-
currentVersion = "0.
|
|
613
|
+
currentVersion = "0.208.0".trim() ? "0.208.0".trim() : "0.1.0";
|
|
614
614
|
});
|
|
615
615
|
|
|
616
616
|
// src/update-notifier.ts
|
|
@@ -2452,7 +2452,7 @@ function parseVersion2(version) {
|
|
|
2452
2452
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2453
2453
|
var init_version2 = __esm(() => {
|
|
2454
2454
|
init_output();
|
|
2455
|
-
currentVersion2 = "0.
|
|
2455
|
+
currentVersion2 = "0.208.0".trim() ? "0.208.0".trim() : "0.1.0";
|
|
2456
2456
|
});
|
|
2457
2457
|
|
|
2458
2458
|
// ../../packages/registry/src/index.ts
|
|
@@ -2677,6 +2677,20 @@ var init_src = __esm(() => {
|
|
|
2677
2677
|
url: `${SITE}/galaxies`,
|
|
2678
2678
|
weights: { ssh: "secondary", web: "secondary" }
|
|
2679
2679
|
},
|
|
2680
|
+
{
|
|
2681
|
+
discoveryUrl: `${SITE}/llms.txt`,
|
|
2682
|
+
exposedContent: [
|
|
2683
|
+
"/identity — look a recording up by ISRC, MusicBrainz recording id, or Log ID",
|
|
2684
|
+
"/identity/:key — one recording's identifiers and platform links, each carrying whether Fluncle found it, looked and found nothing, will not look, or hands out no such link"
|
|
2685
|
+
],
|
|
2686
|
+
kind: "web_route",
|
|
2687
|
+
name: "web.identity",
|
|
2688
|
+
operatorNotes: "The public face of the identity envelope (lib/server/identity-envelope.ts); the machine twin is `get_track`'s identity projection (?identity= / ?isrc= / ?mbid=), and both read the same module so page and API cannot diverge — except on Apple Music, the one audience-scoped field: the page reads `first-party` and renders an Apple link as /log does, the API answers `unsupported` because Apple's terms bar passing those links on. The DOOR is probeable (always 200, and a bare GET with no ?key= renders the explainer); the keyed page is not, and it renders `noindex, follow` on purpose — one recording is reachable under up to three identifiers, so indexing them would put three near-identical URLs in front of one answer at catalogue scale. Only the door is in the sitemap. Both the page's server fn and the op charge the SAME two dials (identity-dials.ts: 30/min + 1,000/day per caller), so the meter cannot be dodged by switching doors; a spent dial renders as a calm page state, never a fault. The keyed page answers 200 with an honest 'nothing under this identifier' rather than 404ing, because an unknown key is a real question with a real answer.",
|
|
2689
|
+
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
2690
|
+
route: "/identity",
|
|
2691
|
+
url: `${SITE}/identity`,
|
|
2692
|
+
weights: { web: "tertiary" }
|
|
2693
|
+
},
|
|
2680
2694
|
{
|
|
2681
2695
|
exposedContent: [
|
|
2682
2696
|
"the signed-in account door: a listener's Galaxy progress, the findings and sets they saved, the tracks they submitted, and their account settings"
|
|
@@ -3439,9 +3453,8 @@ var init_src = __esm(() => {
|
|
|
3439
3453
|
],
|
|
3440
3454
|
kind: "app",
|
|
3441
3455
|
name: "app.ios",
|
|
3442
|
-
operatorNotes: "Fluncle for iOS (apps/mobile)
|
|
3443
|
-
|
|
3444
|
-
url: "https://apps.apple.com/search?term=fluncle",
|
|
3456
|
+
operatorNotes: "Fluncle for iOS (apps/mobile), LIVE on the App Store (approved 2026-07-29). Store listing reachability is Apple's, not ours, so it is not on the /status board — the extension.lens ruling: a vendor store would bot-block or redirect a bare GET and read back as a false 'down'.",
|
|
3457
|
+
url: "https://apps.apple.com/app/id6790080540",
|
|
3445
3458
|
weights: { web: "secondary" }
|
|
3446
3459
|
},
|
|
3447
3460
|
{
|
|
@@ -3768,13 +3781,15 @@ var init_src = __esm(() => {
|
|
|
3768
3781
|
weights: { status: "hidden" }
|
|
3769
3782
|
},
|
|
3770
3783
|
{
|
|
3771
|
-
command: "fluncle admin backfills discogs && fluncle admin backfills lastfm",
|
|
3772
|
-
exposedContent: [
|
|
3784
|
+
command: "fluncle admin backfills discogs && fluncle admin backfills lastfm && fluncle admin backfills apple-music && fluncle admin backfills apple-catalogue",
|
|
3785
|
+
exposedContent: [
|
|
3786
|
+
"Discogs id + Last.fm love + Apple Music link repair, findings then catalogue (--no-agent, Worker HTTP)"
|
|
3787
|
+
],
|
|
3773
3788
|
kind: "cron",
|
|
3774
3789
|
name: "cron.backfill",
|
|
3775
3790
|
operatorNotes: "every 30m. Pure HTTP driving, zero LLM tokens. Agent tier.",
|
|
3776
3791
|
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-backfill", kind: "cron" },
|
|
3777
|
-
statusDescription: "repairs Discogs ids
|
|
3792
|
+
statusDescription: "repairs Discogs ids, Last.fm loves, and Apple Music links",
|
|
3778
3793
|
title: "Metadata backfill",
|
|
3779
3794
|
weights: { status: "hidden" }
|
|
3780
3795
|
},
|
|
@@ -6836,6 +6851,9 @@ function buildBioBody(options) {
|
|
|
6836
6851
|
if (options.dryRun) {
|
|
6837
6852
|
body.dryRun = true;
|
|
6838
6853
|
}
|
|
6854
|
+
if (options.finalAttempt) {
|
|
6855
|
+
body.finalAttempt = true;
|
|
6856
|
+
}
|
|
6839
6857
|
if (typeof options.promptVersion === "number") {
|
|
6840
6858
|
body.promptVersion = options.promptVersion;
|
|
6841
6859
|
}
|
|
@@ -6890,6 +6908,9 @@ function buildBioBody2(options) {
|
|
|
6890
6908
|
if (options.dryRun) {
|
|
6891
6909
|
body.dryRun = true;
|
|
6892
6910
|
}
|
|
6911
|
+
if (options.finalAttempt) {
|
|
6912
|
+
body.finalAttempt = true;
|
|
6913
|
+
}
|
|
6893
6914
|
if (typeof options.promptVersion === "number") {
|
|
6894
6915
|
body.promptVersion = options.promptVersion;
|
|
6895
6916
|
}
|
|
@@ -10325,7 +10346,7 @@ function addAdminCommands(program2) {
|
|
|
10325
10346
|
const { resolveArtistCommand: resolveArtistCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
10326
10347
|
await runArtistResolve(artistId, options, resolveArtistCommand2);
|
|
10327
10348
|
});
|
|
10328
|
-
artists.command("describe").description("Author the voiced bio for an artist (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (artists with findings but no bio), oldest first", false).option("--limit <limit>", "Number of artists to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10349
|
+
artists.command("describe").description("Author the voiced bio for an artist (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (artists with findings but no bio), oldest first", false).option("--limit <limit>", "Number of artists to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--final-attempt", "The sweep's third and last authoring pass: store the draft even if the voice scan refuses it (the length bounds still apply, and the write is flagged for review)", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10329
10350
|
if (options.queue) {
|
|
10330
10351
|
const { artistsBioQueueCommand: artistsBioQueueCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
10331
10352
|
await runEntityBioQueue("artist", options, artistsBioQueueCommand2);
|
|
@@ -10346,7 +10367,7 @@ function addAdminCommands(program2) {
|
|
|
10346
10367
|
labels.action(() => {
|
|
10347
10368
|
labels.outputHelp();
|
|
10348
10369
|
});
|
|
10349
|
-
labels.command("describe").description("Author the voiced bio for a label (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (labels with findings but no bio), oldest first", false).option("--limit <limit>", "Number of labels to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10370
|
+
labels.command("describe").description("Author the voiced bio for a label (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (labels with findings but no bio), oldest first", false).option("--limit <limit>", "Number of labels to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--final-attempt", "The sweep's third and last authoring pass: store the draft even if the voice scan refuses it (the length bounds still apply, and the write is flagged for review)", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10350
10371
|
if (options.queue) {
|
|
10351
10372
|
const { labelsBioQueueCommand: labelsBioQueueCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
|
|
10352
10373
|
await runEntityBioQueue("label", options, labelsBioQueueCommand2);
|
|
@@ -10398,7 +10419,7 @@ function addAdminCommands(program2) {
|
|
|
10398
10419
|
albums.action(() => {
|
|
10399
10420
|
albums.outputHelp();
|
|
10400
10421
|
});
|
|
10401
|
-
albums.command("describe").description("Author the voiced bio for an album (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (albums with findings but no bio), oldest first", false).option("--limit <limit>", "Number of albums to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10422
|
+
albums.command("describe").description("Author the voiced bio for an album (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (albums with findings but no bio), oldest first", false).option("--limit <limit>", "Number of albums to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--final-attempt", "The sweep's third and last authoring pass: store the draft even if the voice scan refuses it (the length bounds still apply, and the write is flagged for review)", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
10402
10423
|
if (options.queue) {
|
|
10403
10424
|
const { albumsBioQueueCommand: albumsBioQueueCommand2 } = await Promise.resolve().then(() => (init_admin_albums(), exports_admin_albums));
|
|
10404
10425
|
await runEntityBioQueue("album", options, albumsBioQueueCommand2);
|
|
@@ -10535,11 +10556,12 @@ async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
|
|
|
10535
10556
|
async function runEntityDescribe(kind, slug, options, describeCommand) {
|
|
10536
10557
|
const bio = options.bioFile ? readFileSync7(options.bioFile, "utf8") : options.bio;
|
|
10537
10558
|
if (!slug || !bio || !bio.trim()) {
|
|
10538
|
-
throw new Error(`Usage: fluncle admin ${kind}s describe <slug> (--bio <text> | --bio-file <file>) [--dry-run] [--json]`);
|
|
10559
|
+
throw new Error(`Usage: fluncle admin ${kind}s describe <slug> (--bio <text> | --bio-file <file>) [--dry-run] [--final-attempt] [--json]`);
|
|
10539
10560
|
}
|
|
10540
10561
|
const result = await describeCommand(slug, {
|
|
10541
10562
|
bio: bio.trim(),
|
|
10542
10563
|
dryRun: options.dryRun,
|
|
10564
|
+
finalAttempt: options.finalAttempt,
|
|
10543
10565
|
promptVersion: parsePromptVersion(options.promptVersion)
|
|
10544
10566
|
});
|
|
10545
10567
|
if (options.json) {
|
|
@@ -10557,6 +10579,12 @@ async function runEntityDescribe(kind, slug, options, describeCommand) {
|
|
|
10557
10579
|
}
|
|
10558
10580
|
console.log(`Authored the bio for ${result.slug}:`);
|
|
10559
10581
|
console.log(` ${result.bio}`);
|
|
10582
|
+
if (result.gateBypassed) {
|
|
10583
|
+
console.log(" FINAL ATTEMPT \u2014 the voice gate refused this bio and it was stored anyway:");
|
|
10584
|
+
for (const violation of result.voiceViolations ?? []) {
|
|
10585
|
+
console.log(` - ${violation}`);
|
|
10586
|
+
}
|
|
10587
|
+
}
|
|
10560
10588
|
}
|
|
10561
10589
|
async function runEntityBioQueue(kind, options, queueCommand2) {
|
|
10562
10590
|
const limit = options.limit ? Number.parseInt(options.limit, 10) : 50;
|
package/package.json
CHANGED