fluncle 0.155.0 → 0.157.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 +116 -5
- 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.157.0".trim() ? "0.157.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.157.0".trim() ? "0.157.0".trim() : "0.1.0";
|
|
2193
2193
|
});
|
|
2194
2194
|
|
|
2195
2195
|
// ../../packages/registry/src/index.ts
|
|
@@ -2913,6 +2913,8 @@ var init_src = __esm(() => {
|
|
|
2913
2913
|
name: "cron.enrich",
|
|
2914
2914
|
operatorNotes: "every 5m. Pure compute, zero LLM tokens. Source: docs/agents/hermes/scripts/enrich-sweep.*",
|
|
2915
2915
|
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-enrich", kind: "cron" },
|
|
2916
|
+
statusDescription: "BPM, key, and the spectral fingerprint",
|
|
2917
|
+
title: "Audio enrichment",
|
|
2916
2918
|
weights: { status: "hidden" }
|
|
2917
2919
|
},
|
|
2918
2920
|
{
|
|
@@ -2924,6 +2926,8 @@ var init_src = __esm(() => {
|
|
|
2924
2926
|
name: "cron.embed",
|
|
2925
2927
|
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/track-lifecycle.md.",
|
|
2926
2928
|
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-embed", kind: "cron" },
|
|
2929
|
+
statusDescription: "MuQ vectors for sonic similarity",
|
|
2930
|
+
title: "Audio embeddings",
|
|
2927
2931
|
weights: { status: "hidden" }
|
|
2928
2932
|
},
|
|
2929
2933
|
{
|
|
@@ -2935,6 +2939,8 @@ var init_src = __esm(() => {
|
|
|
2935
2939
|
name: "cron.cluster",
|
|
2936
2940
|
operatorNotes: "nightly (02:20 Amsterdam), run by a rave-02 HOST systemd timer (docs/agents/hermes/cluster-timer/). Assignment-ONLY + idempotent (a no-op on an unchanged corpus): assign each finding to its nearest stored centroid, recompute centroids as members' means, retire an emptied galaxy, consume an operator split_requested_at (a k=2 fit). Zero LLM tokens; sub-second CPU. A full k=9 fit is an OPERATOR act (--cold-start / --remint), never scheduled. Source: docs/agents/hermes/scripts/cluster-sweep.* + cluster.py. See docs/agents/cluster-engine.md.",
|
|
2937
2941
|
probeConfig: { cadenceMs: 24 * 60 * MINUTE_MS, cronName: "fluncle-cluster", kind: "cron" },
|
|
2942
|
+
statusDescription: "groups the archive into sonic galaxies",
|
|
2943
|
+
title: "Sonic galaxies",
|
|
2938
2944
|
weights: { status: "hidden" }
|
|
2939
2945
|
},
|
|
2940
2946
|
{
|
|
@@ -2946,6 +2952,8 @@ var init_src = __esm(() => {
|
|
|
2946
2952
|
name: "cron.crawl",
|
|
2947
2953
|
operatorNotes: "every 10m, run by a rave-02 HOST systemd timer (docs/agents/hermes/crawl-timer/). METADATA ONLY — it writes a `tracks` row with no `findings` row, so it certifies nothing (no Log ID, no note, no video, no public surface) and it captures no audio. Worker-paced (the box holds no MusicBrainz budget): one bounded pass per tick over the durable `crawl_frontier`, so a crawl is a marathon the SCHEDULE finishes and a reboot mid-label costs one node, not one crawl. The boundary gate is the operator's seed-label allowlist + graph distance (hop 0-2), never genre inference; a label the walk discovers enters `undecided` and is NOT crawled until he rules on it. Zero LLM tokens. Source: docs/agents/hermes/scripts/crawl-sweep.*. See docs/catalogue-crawler.md.",
|
|
2948
2954
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-crawl", kind: "cron" },
|
|
2955
|
+
statusDescription: "charts new tracks from the wider label graph",
|
|
2956
|
+
title: "Track crawler",
|
|
2949
2957
|
weights: { status: "hidden" }
|
|
2950
2958
|
},
|
|
2951
2959
|
{
|
|
@@ -2957,6 +2965,8 @@ var init_src = __esm(() => {
|
|
|
2957
2965
|
name: "cron.rank",
|
|
2958
2966
|
operatorNotes: "every 30m, run by a rave-02 HOST systemd timer (docs/agents/hermes/rank-timer/). THE EAR's schedule, and it lands with the crawler on purpose: a timer ranking an empty table would be a /status row that means nothing, and the crawler is what creates rows. All the vector arithmetic runs in SQL inside the Worker; the sweep DRAINS (it loops while `remaining > 0` up to a tick budget), so a crawl that just landed 700 rows is ranked by the next tick. Self-healing: staleness is a fingerprint of the finding corpus, so logging or embedding a finding re-ranks the catalogue with no invalidation call. Writes DERIVED columns on catalogue rows only — it cannot certify. Zero LLM tokens. Source: docs/agents/hermes/scripts/rank-sweep.*. See docs/the-ear.md.",
|
|
2959
2967
|
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-rank", kind: "cron" },
|
|
2968
|
+
statusDescription: "ranks unvisited tracks by nearness to the archive",
|
|
2969
|
+
title: "Track ranking",
|
|
2960
2970
|
weights: { status: "hidden" }
|
|
2961
2971
|
},
|
|
2962
2972
|
{
|
|
@@ -2968,6 +2978,8 @@ var init_src = __esm(() => {
|
|
|
2968
2978
|
name: "cron.label-images",
|
|
2969
2979
|
operatorNotes: "every 60m, run by a rave-02 HOST systemd timer (docs/agents/hermes/label-images-timer/). The DURABLE other half of the label entity: the crawler MINTS new labels every few minutes (each `image_state='pending'`) and the one-shot operator backfill only seeded the labels that already existed, so this cron is what gives every freshly-minted label its OWN logo instead of a borrowed album cover. METADATA ONLY — a label logo is internal, reversible, nominative-use trademark (the album-art posture); it certifies nothing and publishes nothing (agent tier, the `backfill_discogs` precedent). Worker-paced (the box holds no Discogs key / MusicBrainz budget): one bounded batch per tick walks each label's MB identity → its curated Discogs/Wikidata url-rels → downloads the logo once into R2, up the ladder Discogs → Wikidata → none (the freshest-cover floor). The `labels` row carries the durable reliability state (image_state/image_attempted_at/image_failures), so a resolved/none label is terminal and a vendor throttle just circuit-breaks and resumes next tick. Zero LLM tokens. Source: docs/agents/hermes/scripts/label-images-sweep.*. See docs/label-entity.md.",
|
|
2970
2980
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-label-images", kind: "cron" },
|
|
2981
|
+
statusDescription: "resolves each label's own mark",
|
|
2982
|
+
title: "Label logos",
|
|
2971
2983
|
weights: { status: "hidden" }
|
|
2972
2984
|
},
|
|
2973
2985
|
{
|
|
@@ -2979,6 +2991,8 @@ var init_src = __esm(() => {
|
|
|
2979
2991
|
name: "cron.cover-masters",
|
|
2980
2992
|
operatorNotes: "every 60m, run by a rave-02 HOST systemd timer (docs/agents/hermes/cover-masters-timer/). The DURABLE other half of the album/artist cover (RFC musickit-second-authority U3b): the publish path + catalogue crawl MINT albums/artists (each `image_state='pending'`) and this cron gives each its OWN ≤1200²-capped cover derivative in R2 (found.fluncle.com, `albums/<slug>.<ext>` / `artists/<slug>.<ext>`) instead of hotlinking a third party — the label-logo posture, two entities over. IMAGE ONLY — a downscaled display derivative, reversible, the REF-05-conscious 1200 line (docs/album-artwork.md); it certifies nothing and publishes nothing (agent tier, the `backfill_label_images` precedent). Worker-paced: one bounded batch of albums (Apple template → Cover Art Archive → Spotify floor), then one of artists (Spotify floor), per tick; every rung requests a ≤1200 rendition and a byte read enforces the cap before the R2 put, so no un-downscaled original is ever stored. The `albums`/`artists` row carries the durable reliability state (image_state/image_attempted_at/image_failures), so a resolved/none entity is terminal. Served via Cloudflare Images `/cdn-cgi/image/…` (decision B). Zero LLM tokens. Source: docs/agents/hermes/scripts/cover-masters-sweep.*. See docs/album-artwork.md.",
|
|
2981
2993
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-cover-masters", kind: "cron" },
|
|
2994
|
+
statusDescription: "owns each album and artist its cover master",
|
|
2995
|
+
title: "Cover masters",
|
|
2982
2996
|
weights: { status: "hidden" }
|
|
2983
2997
|
},
|
|
2984
2998
|
{
|
|
@@ -2990,6 +3004,8 @@ var init_src = __esm(() => {
|
|
|
2990
3004
|
name: "cron.capture",
|
|
2991
3005
|
operatorNotes: "every 5m, run by a rave-02 HOST systemd timer (docs/agents/hermes/capture-timer/) — NOT a gateway cron: a proxied yt-dlp fetch has an unbounded tail that would starve the 5-min enrich/context/note sweeps on the shared serial runner. A NON-BLOCKING side-channel (never gates enrich/embed). Runs yt-dlp through a residential proxy on a per-track sticky session, duration-guards the match, stores the full song in the PRIVATE fluncle-source-audio bucket (S3-direct), and writes back via the agent-tier update_track (with per-finding backoff). yt-dlp + ffprobe are a box deploy prereq. Newest-first so a fresh add jumps the backfill. Source: docs/agents/hermes/scripts/capture-sweep.*. See docs/track-lifecycle.md.",
|
|
2992
3006
|
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-capture", kind: "cron" },
|
|
3007
|
+
statusDescription: "captures each finding's full song once",
|
|
3008
|
+
title: "Full-song capture",
|
|
2993
3009
|
weights: { status: "hidden" }
|
|
2994
3010
|
},
|
|
2995
3011
|
{
|
|
@@ -3001,6 +3017,8 @@ var init_src = __esm(() => {
|
|
|
3001
3017
|
name: "cron.verify-captures",
|
|
3002
3018
|
operatorNotes: "every 30m, run by a rave-02 HOST systemd timer (docs/agents/hermes/verify-captures-timer/). The HISTORIC half of the capture verification gate (docs/the-ear.md § Wrong audio): the capture sweep verifies every NEW download at ingest, and this sweep walks every capture that landed before the gate existed and gives each the same Chromaprint check against the track's ISRC-resolved official preview. The box only MEASURES (fpcalc + the sliding-window match) and reports a plain verdict; the Worker ROUTES it — match/no-preview stamp `capture_verification`, a CATALOGUE mismatch quarantines for re-capture, a FINDING mismatch only raises the `capture-suspect` /admin attention item (a machine never rewinds a public finding; the operator rules with flag_wrong_audio). Resumable by construction (a stamped row leaves the worklist); degrades honestly without fpcalc (pre-rebake: `fpcalc_missing`, nothing stamped). Zero LLM tokens. Source: docs/agents/hermes/scripts/verify-captures.* + fingerprint-match.ts. See docs/the-ear.md.",
|
|
3003
3019
|
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-verify-captures", kind: "cron" },
|
|
3020
|
+
statusDescription: "checks each captured song against its official preview",
|
|
3021
|
+
title: "Capture verification",
|
|
3004
3022
|
weights: { status: "hidden" }
|
|
3005
3023
|
},
|
|
3006
3024
|
{
|
|
@@ -3012,6 +3030,8 @@ var init_src = __esm(() => {
|
|
|
3012
3030
|
name: "cron.context-note",
|
|
3013
3031
|
operatorNotes: "every 5m. --no-agent trigger; the Worker does the Firecrawl + Haiku distill. Zero on-box tokens.",
|
|
3014
3032
|
probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-context-note", kind: "cron" },
|
|
3033
|
+
statusDescription: "distills the facts behind each finding",
|
|
3034
|
+
title: "Context notes",
|
|
3015
3035
|
weights: { status: "hidden" }
|
|
3016
3036
|
},
|
|
3017
3037
|
{
|
|
@@ -3023,6 +3043,34 @@ var init_src = __esm(() => {
|
|
|
3023
3043
|
name: "cron.note",
|
|
3024
3044
|
operatorNotes: "every 10m. Hybrid --no-agent; one claude -p authors the line. Never clobbers an operator note.",
|
|
3025
3045
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-note", kind: "cron" },
|
|
3046
|
+
statusDescription: "writes each finding's editorial note",
|
|
3047
|
+
title: "Editorial notes",
|
|
3048
|
+
weights: { status: "hidden" }
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
command: "fluncle admin artists describe --queue",
|
|
3052
|
+
exposedContent: [
|
|
3053
|
+
"auto-author the /artist/<slug> voiced bio, fill-empty-only (hybrid: one claude -p call)"
|
|
3054
|
+
],
|
|
3055
|
+
kind: "cron",
|
|
3056
|
+
name: "cron.artist-bio",
|
|
3057
|
+
operatorNotes: "every 30m. Hybrid --no-agent; one claude -p authors the paragraph, its grounding assembled WORKER-side (Firecrawl facts + the artist's finding titles) via the draft-bio op. Never clobbers an operator bio. Live. Source: docs/agents/hermes/scripts/{entity-bio-sweep.ts,artist-bio-sweep.sh}.",
|
|
3058
|
+
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-artist-bio", kind: "cron" },
|
|
3059
|
+
statusDescription: "writes each artist's voiced bio",
|
|
3060
|
+
title: "Artist bios",
|
|
3061
|
+
weights: { status: "hidden" }
|
|
3062
|
+
},
|
|
3063
|
+
{
|
|
3064
|
+
command: "fluncle admin labels describe --queue",
|
|
3065
|
+
exposedContent: [
|
|
3066
|
+
"auto-author the /label/<slug> voiced bio, fill-empty-only (hybrid: one claude -p call)"
|
|
3067
|
+
],
|
|
3068
|
+
kind: "cron",
|
|
3069
|
+
name: "cron.label-bio",
|
|
3070
|
+
operatorNotes: "every 30m. Hybrid --no-agent; one claude -p authors the paragraph, its grounding assembled WORKER-side (Firecrawl facts + the label's finding titles) via the draft-bio op. Never clobbers an operator bio. Live. Source: docs/agents/hermes/scripts/{entity-bio-sweep.ts,label-bio-sweep.sh}.",
|
|
3071
|
+
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-label-bio", kind: "cron" },
|
|
3072
|
+
statusDescription: "writes each label's voiced bio",
|
|
3073
|
+
title: "Label bios",
|
|
3026
3074
|
weights: { status: "hidden" }
|
|
3027
3075
|
},
|
|
3028
3076
|
{
|
|
@@ -3034,6 +3082,8 @@ var init_src = __esm(() => {
|
|
|
3034
3082
|
name: "cron.triage",
|
|
3035
3083
|
operatorNotes: "every 15m. Hybrid --no-agent; a deterministic archive dedupe + DnB-plausibility heuristic feeds one claude -p phrasing, length-gated. Writes the verdict onto a PENDING submission so it lands in the /admin attention queue already assessed; approve/reject stays operator tier. Source: docs/agents/hermes/scripts/triage-sweep.{sh,ts}.",
|
|
3036
3084
|
probeConfig: { cadenceMs: 15 * MINUTE_MS, cronName: "fluncle-triage", kind: "cron" },
|
|
3085
|
+
statusDescription: "pre-chews each crew submission's verdict",
|
|
3086
|
+
title: "Submission triage",
|
|
3037
3087
|
weights: { status: "hidden" }
|
|
3038
3088
|
},
|
|
3039
3089
|
{
|
|
@@ -3050,6 +3100,8 @@ var init_src = __esm(() => {
|
|
|
3050
3100
|
kind: "cron",
|
|
3051
3101
|
schedule: { time: "00:40", tz: "Europe/Amsterdam" }
|
|
3052
3102
|
},
|
|
3103
|
+
statusDescription: "writes each sector-day up in the Logbook",
|
|
3104
|
+
title: "Logbook author",
|
|
3053
3105
|
weights: { status: "hidden" }
|
|
3054
3106
|
},
|
|
3055
3107
|
{
|
|
@@ -3061,6 +3113,8 @@ var init_src = __esm(() => {
|
|
|
3061
3113
|
name: "cron.observation",
|
|
3062
3114
|
operatorNotes: "every 60m. Hybrid --no-agent; one claude -p authors the script, the Worker voice-gates + renders.",
|
|
3063
3115
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-observation", kind: "cron" },
|
|
3116
|
+
statusDescription: "Fluncle's spoken field observations",
|
|
3117
|
+
title: "Audio observations",
|
|
3064
3118
|
weights: { status: "hidden" }
|
|
3065
3119
|
},
|
|
3066
3120
|
{
|
|
@@ -3070,6 +3124,8 @@ var init_src = __esm(() => {
|
|
|
3070
3124
|
name: "cron.backfill",
|
|
3071
3125
|
operatorNotes: "every 30m. Pure HTTP driving, zero LLM tokens. Agent tier.",
|
|
3072
3126
|
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-backfill", kind: "cron" },
|
|
3127
|
+
statusDescription: "repairs Discogs ids and Last.fm loves",
|
|
3128
|
+
title: "Metadata backfill",
|
|
3073
3129
|
weights: { status: "hidden" }
|
|
3074
3130
|
},
|
|
3075
3131
|
{
|
|
@@ -3080,6 +3136,8 @@ var init_src = __esm(() => {
|
|
|
3080
3136
|
name: "cron.artist-sweep",
|
|
3081
3137
|
operatorNotes: "every 60m. --no-agent trigger; the Worker does the MB walk + Firecrawl /v2/extract + YouTube channel resolution. Zero on-box tokens. MB rows land as status=auto (trusted); Firecrawl rows as status=candidate (operator-confirm before public). Source: docs/agents/hermes/scripts/artist-sweep.*",
|
|
3082
3138
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-artist-sweep", kind: "cron" },
|
|
3139
|
+
statusDescription: "resolves each artist's socials and identity links",
|
|
3140
|
+
title: "Artist resolution",
|
|
3083
3141
|
weights: { status: "hidden" }
|
|
3084
3142
|
},
|
|
3085
3143
|
{
|
|
@@ -3091,6 +3149,8 @@ var init_src = __esm(() => {
|
|
|
3091
3149
|
name: "cron.social-capture",
|
|
3092
3150
|
operatorNotes: "every 10m. Pure HTTP trigger, zero LLM tokens. Agent tier (fills the public URL only — publishes nothing). The box's baked CLI predates the `--capture` verb, so the cron curls POST /api/admin/social/posts/capture directly; the Worker polls Postiz and writes back. Source: docs/agents/hermes/scripts/social-capture-sweep.sh. Probed on /status as cron.social-capture.",
|
|
3093
3151
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-social-capture", kind: "cron" },
|
|
3152
|
+
statusDescription: "the live YouTube and TikTok URLs for each posted video",
|
|
3153
|
+
title: "Social links",
|
|
3094
3154
|
weights: { status: "hidden" }
|
|
3095
3155
|
},
|
|
3096
3156
|
{
|
|
@@ -3101,6 +3161,8 @@ var init_src = __esm(() => {
|
|
|
3101
3161
|
name: "cron.studio-clip",
|
|
3102
3162
|
operatorNotes: "every 15m. Pure-trigger, zero LLM tokens: a deterministic ffmpeg cut driven by the fluncle CLI (`admin clips list` → `admin clips cut`), agent-scoped (list_clips + the agent-tier presign_clip_upload / finalize_clip_cut). Cuts the operator-framed 9:16 clips from the /admin/studio editor (keyed by Log ID) out of the set video → ships `<clipId>/footage.mp4` to R2 for the /admin/clips library + the clip drip-feed. The box runs the standalone bun BINARY (the npm thin client can't spawn ffmpeg). Source: docs/agents/hermes/scripts/clip-sweep.sh. Probed on /status as cron.studio-clip.",
|
|
3103
3163
|
probeConfig: { cadenceMs: 15 * MINUTE_MS, cronName: "fluncle-studio-clip", kind: "cron" },
|
|
3164
|
+
statusDescription: "cuts set videos into 9:16 clips",
|
|
3165
|
+
title: "Studio clips",
|
|
3104
3166
|
weights: { status: "hidden" }
|
|
3105
3167
|
},
|
|
3106
3168
|
{
|
|
@@ -3112,6 +3174,8 @@ var init_src = __esm(() => {
|
|
|
3112
3174
|
name: "cron.clip-drip",
|
|
3113
3175
|
operatorNotes: "every 20m. Pure HTTP trigger, zero LLM tokens. Admin tier (needs the Worker's Postiz key, which the box never sees — the box only triggers; the `finalize_clip_cut` / `record_health` precedent). The Worker checks the global kill switch FIRST (paused → no-op), then posts due clips bounded by a per-tick cap AND a rolling-24h IG cap. Every clip auto-enters the schedule at a jittered ~23-25h after the queue tail. The operator pauses/resumes with `fluncle admin clips drip-pause` / `drip-resume`. Source: docs/agents/hermes/scripts/clip-drip-sweep.sh. Probed on /status as cron.clip-drip.",
|
|
3114
3176
|
probeConfig: { cadenceMs: 20 * MINUTE_MS, cronName: "fluncle-clip-drip", kind: "cron" },
|
|
3177
|
+
statusDescription: "drip-feeds set clips to Instagram",
|
|
3178
|
+
title: "Clip drip-feed",
|
|
3115
3179
|
weights: { status: "hidden" }
|
|
3116
3180
|
},
|
|
3117
3181
|
{
|
|
@@ -3123,6 +3187,8 @@ var init_src = __esm(() => {
|
|
|
3123
3187
|
name: "cron.publish-advance",
|
|
3124
3188
|
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.",
|
|
3125
3189
|
probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-publish-advance", kind: "cron" },
|
|
3190
|
+
statusDescription: "advances the publish queue on his own clock",
|
|
3191
|
+
title: "Publish advance",
|
|
3126
3192
|
weights: { status: "hidden" }
|
|
3127
3193
|
},
|
|
3128
3194
|
{
|
|
@@ -3134,6 +3200,8 @@ var init_src = __esm(() => {
|
|
|
3134
3200
|
name: "cron.render",
|
|
3135
3201
|
operatorNotes: "every 60m. A conductor: triggers a detached @fluncle-video render on a scale-to-zero box.ascii box (rave-03). Never posts to social (operator-tier 403). Probed on /status as service `cron.render` (its own last-run freshness); the box's reachability is the SEPARATE `render-box` probe (the conductor state file).",
|
|
3136
3202
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-render", kind: "cron" },
|
|
3203
|
+
statusDescription: "the conductor's last run",
|
|
3204
|
+
title: "Render cron",
|
|
3137
3205
|
weights: { status: "hidden" }
|
|
3138
3206
|
},
|
|
3139
3207
|
{
|
|
@@ -3144,6 +3212,8 @@ var init_src = __esm(() => {
|
|
|
3144
3212
|
name: "cron.healthcheck",
|
|
3145
3213
|
operatorNotes: "every 10m, run by a rave-02 host systemd timer (docs/agents/hermes/healthcheck-timer/) — decoupled from the Hermes cron gateway so the prober isn't starved by the scheduler it monitors. Pure probing, zero LLM tokens. POSTs to the agent-tier record_health op that /status reads.",
|
|
3146
3214
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-healthcheck", kind: "cron" },
|
|
3215
|
+
statusDescription: "the prober behind this very page",
|
|
3216
|
+
title: "Healthcheck prober",
|
|
3147
3217
|
weights: { status: "hidden" }
|
|
3148
3218
|
},
|
|
3149
3219
|
{
|
|
@@ -3160,6 +3230,8 @@ var init_src = __esm(() => {
|
|
|
3160
3230
|
kind: "cron",
|
|
3161
3231
|
schedule: { time: "15:00", tz: "Europe/Amsterdam", weekday: 5 }
|
|
3162
3232
|
},
|
|
3233
|
+
statusDescription: "drafts the Friday edition",
|
|
3234
|
+
title: "Weekly newsletter",
|
|
3163
3235
|
weights: { status: "secondary" }
|
|
3164
3236
|
},
|
|
3165
3237
|
{
|
|
@@ -3175,6 +3247,8 @@ var init_src = __esm(() => {
|
|
|
3175
3247
|
kind: "cron",
|
|
3176
3248
|
schedule: { time: "03:00", tz: "Europe/Amsterdam" }
|
|
3177
3249
|
},
|
|
3250
|
+
statusDescription: "a daily off-site snapshot of the archive",
|
|
3251
|
+
title: "Database backup",
|
|
3178
3252
|
weights: { status: "secondary" }
|
|
3179
3253
|
},
|
|
3180
3254
|
{
|
|
@@ -3191,6 +3265,8 @@ var init_src = __esm(() => {
|
|
|
3191
3265
|
kind: "cron",
|
|
3192
3266
|
schedule: { time: "04:00", tz: "Europe/Amsterdam" }
|
|
3193
3267
|
},
|
|
3268
|
+
statusDescription: "counts the crew and how far the probes reached",
|
|
3269
|
+
title: "Reach snapshot",
|
|
3194
3270
|
weights: { status: "secondary" }
|
|
3195
3271
|
},
|
|
3196
3272
|
{
|
|
@@ -3206,6 +3282,8 @@ var init_src = __esm(() => {
|
|
|
3206
3282
|
kind: "cron",
|
|
3207
3283
|
schedule: { time: "01:00", tz: "Europe/Amsterdam" }
|
|
3208
3284
|
},
|
|
3285
|
+
statusDescription: "nightly one-domain codebase audit → a PR",
|
|
3286
|
+
title: "Nightly audit",
|
|
3209
3287
|
weights: { status: "secondary" }
|
|
3210
3288
|
},
|
|
3211
3289
|
{
|
|
@@ -3221,6 +3299,8 @@ var init_src = __esm(() => {
|
|
|
3221
3299
|
kind: "cron",
|
|
3222
3300
|
schedule: { time: "05:00", tz: "Europe/Amsterdam" }
|
|
3223
3301
|
},
|
|
3302
|
+
statusDescription: "reviews + merges the nightly audit PR",
|
|
3303
|
+
title: "Audit reviewer",
|
|
3224
3304
|
weights: { status: "secondary" }
|
|
3225
3305
|
}
|
|
3226
3306
|
];
|
|
@@ -5810,7 +5890,7 @@ __export(exports_admin_catalogue, {
|
|
|
5810
5890
|
async function catalogueRankCommand(options) {
|
|
5811
5891
|
const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
|
|
5812
5892
|
const response = await adminApiPost("/api/admin/catalogue/rank", limit ? { limit } : {});
|
|
5813
|
-
return response.summary;
|
|
5893
|
+
return { summary: response.summary, telescope: response.telescope };
|
|
5814
5894
|
}
|
|
5815
5895
|
async function catalogueListCommand(options) {
|
|
5816
5896
|
const params = new URLSearchParams;
|
|
@@ -5896,6 +5976,7 @@ var exports_admin_artists = {};
|
|
|
5896
5976
|
__export(exports_admin_artists, {
|
|
5897
5977
|
resolveArtistCommand: () => resolveArtistCommand,
|
|
5898
5978
|
listArtistsCommand: () => listArtistsCommand,
|
|
5979
|
+
draftArtistBioCommand: () => draftArtistBioCommand,
|
|
5899
5980
|
describeArtistCommand: () => describeArtistCommand,
|
|
5900
5981
|
buildBioBody: () => buildBioBody,
|
|
5901
5982
|
backfillArtistsCommand: () => backfillArtistsCommand,
|
|
@@ -5915,6 +5996,9 @@ function buildBioBody(options) {
|
|
|
5915
5996
|
async function describeArtistCommand(slug, options) {
|
|
5916
5997
|
return adminApiPost(`/api/admin/artists/${encodeURIComponent(slug)}/bio`, buildBioBody(options));
|
|
5917
5998
|
}
|
|
5999
|
+
async function draftArtistBioCommand(slug) {
|
|
6000
|
+
return adminApiGet(`/api/admin/artists/${encodeURIComponent(slug)}/bio-draft`);
|
|
6001
|
+
}
|
|
5918
6002
|
async function artistsBioQueueCommand(limit) {
|
|
5919
6003
|
const response = await adminApiGet(`/api/admin/artists/bio-queue?limit=${limit}`);
|
|
5920
6004
|
return response.artists;
|
|
@@ -5966,12 +6050,16 @@ var init_admin_artists2 = __esm(() => {
|
|
|
5966
6050
|
var exports_admin_labels = {};
|
|
5967
6051
|
__export(exports_admin_labels, {
|
|
5968
6052
|
labelsBioQueueCommand: () => labelsBioQueueCommand,
|
|
6053
|
+
draftLabelBioCommand: () => draftLabelBioCommand,
|
|
5969
6054
|
describeLabelCommand: () => describeLabelCommand,
|
|
5970
6055
|
backfillLabelImagesCommand: () => backfillLabelImagesCommand
|
|
5971
6056
|
});
|
|
5972
6057
|
async function describeLabelCommand(slug, options) {
|
|
5973
6058
|
return adminApiPost(`/api/admin/labels/${encodeURIComponent(slug)}/bio`, buildBioBody2(options));
|
|
5974
6059
|
}
|
|
6060
|
+
async function draftLabelBioCommand(slug) {
|
|
6061
|
+
return adminApiGet(`/api/admin/labels/${encodeURIComponent(slug)}/bio-draft`);
|
|
6062
|
+
}
|
|
5975
6063
|
async function labelsBioQueueCommand(limit) {
|
|
5976
6064
|
const response = await adminApiGet(`/api/admin/labels/bio-queue?limit=${limit}`);
|
|
5977
6065
|
return response.labels;
|
|
@@ -9175,6 +9263,10 @@ function addAdminCommands(program2) {
|
|
|
9175
9263
|
const { describeArtistCommand: describeArtistCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
9176
9264
|
await runEntityDescribe("artist", slug, options, describeArtistCommand2);
|
|
9177
9265
|
});
|
|
9266
|
+
artists.command("draft-bio").description("Assemble the Worker-grounded bio prompt for an artist (the sweep's trigger)").argument("<slug>").option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
9267
|
+
const { draftArtistBioCommand: draftArtistBioCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
9268
|
+
await runEntityBioDraft("artist", slug, options, draftArtistBioCommand2);
|
|
9269
|
+
});
|
|
9178
9270
|
const labels = configureCommand(admin.command("labels").description("Label entity commands (the voiced bio)"));
|
|
9179
9271
|
labels.action(() => {
|
|
9180
9272
|
labels.outputHelp();
|
|
@@ -9188,6 +9280,10 @@ function addAdminCommands(program2) {
|
|
|
9188
9280
|
const { describeLabelCommand: describeLabelCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
|
|
9189
9281
|
await runEntityDescribe("label", slug, options, describeLabelCommand2);
|
|
9190
9282
|
});
|
|
9283
|
+
labels.command("draft-bio").description("Assemble the Worker-grounded bio prompt for a label (the sweep's trigger)").argument("<slug>").option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
|
|
9284
|
+
const { draftLabelBioCommand: draftLabelBioCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
|
|
9285
|
+
await runEntityBioDraft("label", slug, options, draftLabelBioCommand2);
|
|
9286
|
+
});
|
|
9191
9287
|
const migrations = configureCommand(admin.command("migrations").description("One-off operator data migrations"));
|
|
9192
9288
|
migrations.action(() => {
|
|
9193
9289
|
migrations.outputHelp();
|
|
@@ -9352,6 +9448,18 @@ async function runEntityBioQueue(kind, options, queueCommand2) {
|
|
|
9352
9448
|
console.log(` ${item.slug} ${item.name}`);
|
|
9353
9449
|
}
|
|
9354
9450
|
}
|
|
9451
|
+
async function runEntityBioDraft(kind, slug, options, draftCommand) {
|
|
9452
|
+
const draft = await draftCommand(slug);
|
|
9453
|
+
if (options.json) {
|
|
9454
|
+
printJson(draft);
|
|
9455
|
+
return;
|
|
9456
|
+
}
|
|
9457
|
+
if (!draft.found) {
|
|
9458
|
+
console.log(`No ${kind} with slug ${slug}.`);
|
|
9459
|
+
return;
|
|
9460
|
+
}
|
|
9461
|
+
console.log(`Drafted a bio prompt for ${draft.name} (${draft.findingCount} finding(s), facts: ${draft.hasFacts ? "yes" : "none"}, prompt v${draft.promptVersion}).`);
|
|
9462
|
+
}
|
|
9355
9463
|
async function runTrackSimilar(idOrLogId, options, trackSimilarCommand2) {
|
|
9356
9464
|
if (!idOrLogId) {
|
|
9357
9465
|
throw new Error("Missing id. Usage: fluncle tracks similar <track_id|log_id> [--limit <n>] [--json]");
|
|
@@ -10841,14 +10949,17 @@ async function runGalaxies(slug, options, commands) {
|
|
|
10841
10949
|
}
|
|
10842
10950
|
}
|
|
10843
10951
|
async function runCatalogueRank(options, catalogueRankCommand2) {
|
|
10844
|
-
const summary = await catalogueRankCommand2({ limit: options.limit });
|
|
10952
|
+
const { summary, telescope } = await catalogueRankCommand2({ limit: options.limit });
|
|
10845
10953
|
if (options.json) {
|
|
10846
|
-
printJson({ ok: true, summary });
|
|
10954
|
+
printJson({ ok: true, summary, telescope });
|
|
10847
10955
|
return;
|
|
10848
10956
|
}
|
|
10849
10957
|
const quarantine = summary.quarantined > 0 ? ` Quarantined ${summary.quarantined} as wrong audio.` : "";
|
|
10850
10958
|
const deduped = summary.catalogueDuplicates > 0 ? ` Marked ${summary.catalogueDuplicates} as catalogue duplicates.` : "";
|
|
10851
10959
|
console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture.${quarantine}${deduped} ${summary.remaining} still stale.`);
|
|
10960
|
+
if (telescope) {
|
|
10961
|
+
console.log(telescope.ok ? `Telescope: ${telescope.size} aboard${telescope.changed ? " (updated)" : ""}.` : `Telescope: sync failed \u2014 ${telescope.reason}`);
|
|
10962
|
+
}
|
|
10852
10963
|
}
|
|
10853
10964
|
async function runCatalogueList(options, catalogueListCommand2) {
|
|
10854
10965
|
const { summary, tracks } = await catalogueListCommand2({
|
package/package.json
CHANGED