fluncle 0.62.0 → 0.63.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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +705 -2
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -423,7 +423,7 @@ function parseVersion(version) {
423
423
  var currentVersion;
424
424
  var init_version = __esm(() => {
425
425
  init_output();
426
- currentVersion = "0.62.0".trim() ? "0.62.0".trim() : "0.1.0";
426
+ currentVersion = "0.63.0".trim() ? "0.63.0".trim() : "0.1.0";
427
427
  });
428
428
 
429
429
  // src/update-notifier.ts
@@ -1914,7 +1914,692 @@ function parseVersion2(version) {
1914
1914
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
1915
1915
  var init_version2 = __esm(() => {
1916
1916
  init_output();
1917
- currentVersion2 = "0.62.0".trim() ? "0.62.0".trim() : "0.1.0";
1917
+ currentVersion2 = "0.63.0".trim() ? "0.63.0".trim() : "0.1.0";
1918
+ });
1919
+
1920
+ // ../../packages/registry/src/index.ts
1921
+ var SITE = "https://www.fluncle.com", PROBE_CADENCE_MS, PROBE_TIMEOUT_MS, MINUTE_MS, SURFACES;
1922
+ var init_src = __esm(() => {
1923
+ PROBE_CADENCE_MS = 10 * 60 * 1000;
1924
+ PROBE_TIMEOUT_MS = 10 * 1000;
1925
+ MINUTE_MS = 60 * 1000;
1926
+ SURFACES = [
1927
+ {
1928
+ exposedContent: ["the archive — every certified finding, newest first, cover-led"],
1929
+ kind: "web_route",
1930
+ name: "web.home",
1931
+ operatorNotes: "The Worker root. galaxy./radio. rewrite their root to /galaxy and /radio.",
1932
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1933
+ route: "/",
1934
+ url: `${SITE}/`,
1935
+ weight: "primary"
1936
+ },
1937
+ {
1938
+ exposedContent: [
1939
+ "the log index — every finding's coordinate page",
1940
+ "/log/:logId — one finding's permanent home (the Log ID resolves here)"
1941
+ ],
1942
+ kind: "web_route",
1943
+ name: "web.log",
1944
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1945
+ route: "/log",
1946
+ url: `${SITE}/log`,
1947
+ weight: "primary"
1948
+ },
1949
+ {
1950
+ exposedContent: ["Fluncle's own DJ mixtapes — each a checkpoint set with an F-marked Log ID"],
1951
+ kind: "web_route",
1952
+ name: "web.mixtapes",
1953
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1954
+ route: "/mixtapes",
1955
+ url: `${SITE}/mixtapes`,
1956
+ weight: "primary"
1957
+ },
1958
+ {
1959
+ exposedContent: [
1960
+ "the feed-first Stories surface — full-bleed findings",
1961
+ "/stories/:logId — one finding as a Story"
1962
+ ],
1963
+ kind: "web_route",
1964
+ name: "web.stories",
1965
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1966
+ route: "/stories",
1967
+ url: `${SITE}/stories`,
1968
+ weight: "secondary"
1969
+ },
1970
+ {
1971
+ exposedContent: ["who Fluncle is, what the Galaxy is, how to read a Log ID"],
1972
+ kind: "web_route",
1973
+ name: "web.about",
1974
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1975
+ route: "/about",
1976
+ url: `${SITE}/about`,
1977
+ weight: "secondary"
1978
+ },
1979
+ {
1980
+ exposedContent: [
1981
+ "the newsletter archive — every sent edition",
1982
+ "/newsletter/:number — one past edition rendered on the web"
1983
+ ],
1984
+ kind: "web_route",
1985
+ name: "web.newsletter",
1986
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1987
+ route: "/newsletter",
1988
+ url: `${SITE}/newsletter`,
1989
+ weight: "secondary"
1990
+ },
1991
+ {
1992
+ exposedContent: [
1993
+ "the Fumadocs developer docs",
1994
+ "/docs/api — the embedded Scalar API reference"
1995
+ ],
1996
+ kind: "web_route",
1997
+ name: "web.docs",
1998
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
1999
+ route: "/docs",
2000
+ url: `${SITE}/docs`,
2001
+ weight: "secondary"
2002
+ },
2003
+ {
2004
+ exposedContent: ["the public service-health dashboard — uptime per service, recent events"],
2005
+ kind: "web_route",
2006
+ name: "web.status",
2007
+ operatorNotes: "The future status.fluncle.com host points here. The fluncle-healthcheck cron POSTs its snapshots to the agent-tier record_health op that this page reads.",
2008
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2009
+ route: "/status",
2010
+ url: `${SITE}/status`,
2011
+ weight: "secondary"
2012
+ },
2013
+ {
2014
+ exposedContent: ["the Galaxy game — the 8-bit fly-to-every-banger arcade front door"],
2015
+ kind: "web_route",
2016
+ name: "web.galaxy",
2017
+ operatorNotes: "Reachable at galaxy.fluncle.com, whose root the router rewrites to /galaxy.",
2018
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2019
+ route: "/galaxy",
2020
+ url: `${SITE}/galaxy`,
2021
+ weight: "primary"
2022
+ },
2023
+ {
2024
+ exposedContent: [
2025
+ "the cycling observation station — Fluncle's spoken field observations on a loop"
2026
+ ],
2027
+ kind: "web_route",
2028
+ name: "web.radio",
2029
+ operatorNotes: "Reachable at radio.fluncle.com, whose root the router rewrites to /radio.",
2030
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2031
+ route: "/radio",
2032
+ url: `${SITE}/radio`,
2033
+ weight: "secondary"
2034
+ },
2035
+ {
2036
+ exposedContent: ["the privacy policy"],
2037
+ kind: "web_route",
2038
+ name: "web.privacy",
2039
+ route: "/privacy",
2040
+ url: `${SITE}/privacy`,
2041
+ weight: "tertiary"
2042
+ },
2043
+ {
2044
+ exposedContent: ["the Galaxy game's front door (root rewrites to /galaxy)"],
2045
+ kind: "subdomain",
2046
+ name: "subdomain.galaxy",
2047
+ operatorNotes: "Isomorphic host-rewrite in apps/web router (input/output) so SSR + hydration agree.",
2048
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2049
+ subdomain: "galaxy.fluncle.com",
2050
+ url: "https://galaxy.fluncle.com",
2051
+ weight: "primary"
2052
+ },
2053
+ {
2054
+ exposedContent: ["the observation station (root rewrites to /radio)"],
2055
+ kind: "subdomain",
2056
+ name: "subdomain.radio",
2057
+ operatorNotes: "Isomorphic host-rewrite in apps/web router so SSR + hydration agree.",
2058
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2059
+ subdomain: "radio.fluncle.com",
2060
+ url: "https://radio.fluncle.com",
2061
+ weight: "secondary"
2062
+ },
2063
+ {
2064
+ exposedContent: [
2065
+ "the R2 media zone — each finding's video bundle + mixtape audio",
2066
+ "the /cdn-cgi/media transform base (same zone, no cross-origin)"
2067
+ ],
2068
+ kind: "subdomain",
2069
+ name: "subdomain.found",
2070
+ operatorNotes: "FOUND_BASE in apps/web/src/lib/media.ts. Probed on /status as service `r2`.",
2071
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2072
+ subdomain: "found.fluncle.com",
2073
+ url: "https://found.fluncle.com",
2074
+ weight: "tertiary"
2075
+ },
2076
+ {
2077
+ exposedContent: ["the delegated DNS zone's host label (see the dns surface for the resolver)"],
2078
+ kind: "subdomain",
2079
+ name: "subdomain.dig",
2080
+ operatorNotes: "The zone is served by apps/dns; see the `dns.zone` surface.",
2081
+ subdomain: "dig.fluncle.com",
2082
+ url: "https://dig.fluncle.com",
2083
+ weight: "tertiary"
2084
+ },
2085
+ {
2086
+ exposedContent: ["the planned status host — points at /status"],
2087
+ kind: "subdomain",
2088
+ name: "subdomain.status",
2089
+ operatorNotes: "PLANNED. Not yet wired; the page already lives at www.fluncle.com/status.",
2090
+ subdomain: "status.fluncle.com",
2091
+ url: "https://status.fluncle.com",
2092
+ weight: "tertiary"
2093
+ },
2094
+ {
2095
+ exposedContent: [
2096
+ "the Tor onion mirror of www.fluncle.com — the archive, API, RSS, and MCP over Tor"
2097
+ ],
2098
+ kind: "subdomain",
2099
+ name: "subdomain.onion",
2100
+ operatorNotes: "An onionspray mirror. Advertised via Tor Browser's Onion-Location pill. Probed on /status as service `onion`. Runbook: docs/tor.md.",
2101
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2102
+ subdomain: "p53pc2uzfu2tnih4cd6wd42ok6zup2uttj6xdmjdccy5kqo33fyppkqd.onion",
2103
+ url: "http://p53pc2uzfu2tnih4cd6wd42ok6zup2uttj6xdmjdccy5kqo33fyppkqd.onion",
2104
+ weight: "tertiary"
2105
+ },
2106
+ {
2107
+ apiFormat: "application/json",
2108
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2109
+ exposedContent: ["the archive as JSON, cursor-paginated (limit max 48, cursor)"],
2110
+ kind: "api",
2111
+ name: "api.tracks",
2112
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2113
+ route: "/api/v1/tracks",
2114
+ url: `${SITE}/api/v1/tracks`,
2115
+ weight: "primary"
2116
+ },
2117
+ {
2118
+ apiFormat: "application/json",
2119
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2120
+ exposedContent: ["one finding or mixtape by Spotify id or Log ID"],
2121
+ kind: "api",
2122
+ name: "api.track",
2123
+ route: "/api/v1/tracks/:idOrLogId",
2124
+ url: `${SITE}/api/v1/tracks/:idOrLogId`,
2125
+ weight: "secondary"
2126
+ },
2127
+ {
2128
+ apiFormat: "application/json",
2129
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2130
+ exposedContent: ["one finding at random"],
2131
+ kind: "api",
2132
+ name: "api.tracks.random",
2133
+ route: "/api/v1/tracks/random",
2134
+ url: `${SITE}/api/v1/tracks/random`,
2135
+ weight: "secondary"
2136
+ },
2137
+ {
2138
+ apiFormat: "application/json",
2139
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2140
+ exposedContent: ["published mixtapes as JSON"],
2141
+ kind: "api",
2142
+ name: "api.mixtapes",
2143
+ route: "/api/v1/mixtapes",
2144
+ url: `${SITE}/api/v1/mixtapes`,
2145
+ weight: "secondary"
2146
+ },
2147
+ {
2148
+ apiFormat: "application/json",
2149
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2150
+ exposedContent: ["Spotify search candidates for submitting a track"],
2151
+ kind: "api",
2152
+ name: "api.search",
2153
+ route: "/api/v1/search",
2154
+ url: `${SITE}/api/v1/search`,
2155
+ weight: "secondary"
2156
+ },
2157
+ {
2158
+ apiFormat: "application/json",
2159
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2160
+ exposedContent: ["submit a track for review (POST)"],
2161
+ kind: "api",
2162
+ name: "api.submissions",
2163
+ route: "/api/v1/submissions",
2164
+ url: `${SITE}/api/v1/submissions`,
2165
+ weight: "secondary"
2166
+ },
2167
+ {
2168
+ apiFormat: "application/json",
2169
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2170
+ exposedContent: ["subscribe to the newsletter (POST); the editions archive"],
2171
+ kind: "api",
2172
+ name: "api.newsletter",
2173
+ route: "/api/v1/newsletter",
2174
+ url: `${SITE}/api/v1/newsletter`,
2175
+ weight: "secondary"
2176
+ },
2177
+ {
2178
+ apiFormat: "application/json",
2179
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2180
+ exposedContent: ["the Stories payload as JSON"],
2181
+ kind: "api",
2182
+ name: "api.stories",
2183
+ route: "/api/v1/stories",
2184
+ url: `${SITE}/api/v1/stories`,
2185
+ weight: "tertiary"
2186
+ },
2187
+ {
2188
+ apiFormat: "application/json",
2189
+ discoveryUrl: `${SITE}/api/v1/openapi.json`,
2190
+ exposedContent: ["the radio shared-clock now-playing slot"],
2191
+ kind: "api",
2192
+ name: "api.radio.now-playing",
2193
+ route: "/api/v1/radio/now-playing",
2194
+ url: `${SITE}/api/v1/radio/now-playing`,
2195
+ weight: "tertiary"
2196
+ },
2197
+ {
2198
+ apiFormat: "application/json",
2199
+ exposedContent: ["the liveness probe — the canonical web health check"],
2200
+ kind: "api",
2201
+ name: "api.health",
2202
+ operatorNotes: "Linked as the `status` relation from /.well-known/api-catalog. Service `web` on /status.",
2203
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2204
+ route: "/api/health",
2205
+ url: `${SITE}/api/health`,
2206
+ weight: "tertiary"
2207
+ },
2208
+ {
2209
+ apiFormat: "application/rss+xml",
2210
+ exposedContent: ["the 25 most recent findings and mixtapes"],
2211
+ kind: "feed",
2212
+ name: "feed.rss",
2213
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2214
+ route: "/rss.xml",
2215
+ url: `${SITE}/rss.xml`,
2216
+ weight: "primary"
2217
+ },
2218
+ {
2219
+ apiFormat: "application/atom+xml",
2220
+ exposedContent: ["the recent findings and mixtapes as an Atom feed"],
2221
+ kind: "feed",
2222
+ name: "feed.atom",
2223
+ route: "/atom.xml",
2224
+ url: `${SITE}/atom.xml`,
2225
+ weight: "secondary"
2226
+ },
2227
+ {
2228
+ apiFormat: "application/feed+json",
2229
+ exposedContent: ["the recent findings and mixtapes as a JSON Feed"],
2230
+ kind: "feed",
2231
+ name: "feed.json",
2232
+ route: "/feed.json",
2233
+ url: `${SITE}/feed.json`,
2234
+ weight: "secondary"
2235
+ },
2236
+ {
2237
+ apiFormat: "application/rss+xml",
2238
+ exposedContent: ["the mixtapes as a podcast feed (episode audio on found.fluncle.com)"],
2239
+ kind: "feed",
2240
+ name: "feed.podcast",
2241
+ route: "/podcast.xml",
2242
+ url: `${SITE}/podcast.xml`,
2243
+ weight: "secondary"
2244
+ },
2245
+ {
2246
+ apiFormat: "text/calendar",
2247
+ exposedContent: ["planned events as an iCalendar feed (Twitch-linked VEVENTs)"],
2248
+ kind: "feed",
2249
+ name: "feed.calendar",
2250
+ route: "/calendar.ics",
2251
+ url: `${SITE}/calendar.ics`,
2252
+ weight: "tertiary"
2253
+ },
2254
+ {
2255
+ apiFormat: "application/xml",
2256
+ exposedContent: ["the XML sitemap of every public page"],
2257
+ kind: "discovery",
2258
+ name: "discovery.sitemap",
2259
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2260
+ route: "/sitemap.xml",
2261
+ url: `${SITE}/sitemap.xml`,
2262
+ weight: "secondary"
2263
+ },
2264
+ {
2265
+ apiFormat: "text/plain",
2266
+ exposedContent: [
2267
+ "the crawl policy + Content-Signal (search/AI-input/AI-train all yes) + sitemap link"
2268
+ ],
2269
+ kind: "discovery",
2270
+ name: "discovery.robots",
2271
+ operatorNotes: "Cloudflare's managed robots.txt can prepend directives; this file is the origin's intent.",
2272
+ route: "/robots.txt",
2273
+ url: `${SITE}/robots.txt`,
2274
+ weight: "tertiary"
2275
+ },
2276
+ {
2277
+ apiFormat: "text/markdown",
2278
+ discoveryUrl: `${SITE}/.well-known/api-catalog`,
2279
+ exposedContent: ["the plain-language map of the Galaxy for LLMs"],
2280
+ kind: "discovery",
2281
+ name: "discovery.llms",
2282
+ route: "/llms.txt",
2283
+ url: `${SITE}/llms.txt`,
2284
+ weight: "primary"
2285
+ },
2286
+ {
2287
+ apiFormat: "text/markdown",
2288
+ exposedContent: ["the entire archive in one ingestible markdown document, every finding"],
2289
+ kind: "discovery",
2290
+ name: "discovery.llms-full",
2291
+ route: "/llms-full.txt",
2292
+ url: `${SITE}/llms-full.txt`,
2293
+ weight: "secondary"
2294
+ },
2295
+ {
2296
+ apiFormat: "application/openapi+json",
2297
+ exposedContent: ["the public API as an OpenAPI 3.1 document (admin paths excluded)"],
2298
+ kind: "discovery",
2299
+ name: "discovery.openapi",
2300
+ route: "/api/v1/openapi.json",
2301
+ url: `${SITE}/api/v1/openapi.json`,
2302
+ weight: "secondary"
2303
+ },
2304
+ {
2305
+ apiFormat: "application/json",
2306
+ exposedContent: ["the SEP-2127 discovery card for the MCP endpoint"],
2307
+ kind: "discovery",
2308
+ name: "discovery.mcp-server-card",
2309
+ route: "/.well-known/mcp/server-card.json",
2310
+ url: `${SITE}/.well-known/mcp/server-card.json`,
2311
+ weight: "tertiary"
2312
+ },
2313
+ {
2314
+ apiFormat: "application/linkset+json",
2315
+ exposedContent: ["the RFC 9727 linkset pointing at the machine-readable surfaces"],
2316
+ kind: "discovery",
2317
+ name: "discovery.api-catalog",
2318
+ route: "/.well-known/api-catalog",
2319
+ url: `${SITE}/.well-known/api-catalog`,
2320
+ weight: "tertiary"
2321
+ },
2322
+ {
2323
+ apiFormat: "application/json",
2324
+ exposedContent: ["the fluncle-api agent skill index (with the SKILL.md digest)"],
2325
+ kind: "discovery",
2326
+ name: "discovery.agent-skills",
2327
+ route: "/.well-known/agent-skills/index.json",
2328
+ url: `${SITE}/.well-known/agent-skills/index.json`,
2329
+ weight: "tertiary"
2330
+ },
2331
+ {
2332
+ apiFormat: "application/json",
2333
+ discoveryUrl: `${SITE}/.well-known/mcp/server-card.json`,
2334
+ exposedContent: [
2335
+ "the archive as MCP tools (Streamable HTTP, no auth): list_tracks, get_random_track, search_tracks, submit_track, subscribe_newsletter"
2336
+ ],
2337
+ kind: "mcp",
2338
+ name: "mcp.server",
2339
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2340
+ route: "/mcp",
2341
+ url: `${SITE}/mcp`,
2342
+ weight: "primary"
2343
+ },
2344
+ {
2345
+ command: "dig TXT 004.7.2I.dig.fluncle.com",
2346
+ exposedContent: [
2347
+ "a finding's coordinate as a TXT record (e.g. 004.7.2I.dig.fluncle.com)",
2348
+ "the special labels `random` and `latest`"
2349
+ ],
2350
+ kind: "dns",
2351
+ name: "dns.zone",
2352
+ operatorNotes: "apps/dns — a tiny authoritative server for dig.fluncle.com, reads the public API and renders a finding as TXT. Not recursive (out-of-zone is REFUSED). Probed on /status as service `dns`.",
2353
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2354
+ subdomain: "dig.fluncle.com",
2355
+ weight: "tertiary"
2356
+ },
2357
+ {
2358
+ command: "ssh rave.fluncle.com",
2359
+ exposedContent: [
2360
+ "the rave terminal TUI: Enter the Galaxy, Latest bangers, Mixtape archive, Submit, Subscribe, Install CLI, About",
2361
+ "deep-register one-shots: `ssh rave.fluncle.com latest|random`"
2362
+ ],
2363
+ kind: "ssh",
2364
+ name: "ssh.rave",
2365
+ operatorNotes: "apps/ssh (Go Wish/Bubble Tea). Probed on /status as service `ssh`.",
2366
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2367
+ subdomain: "rave.fluncle.com",
2368
+ weight: "primary"
2369
+ },
2370
+ {
2371
+ command: "fluncle recent",
2372
+ exposedContent: ["the latest bangers, newest first (alias `list`)"],
2373
+ kind: "cli",
2374
+ name: "cli.recent",
2375
+ weight: "primary"
2376
+ },
2377
+ {
2378
+ command: "fluncle mixtapes",
2379
+ exposedContent: ["Fluncle's checkpoint sets"],
2380
+ kind: "cli",
2381
+ name: "cli.mixtapes",
2382
+ weight: "secondary"
2383
+ },
2384
+ {
2385
+ command: "fluncle open",
2386
+ exposedContent: ["pick a track, open it in Spotify"],
2387
+ kind: "cli",
2388
+ name: "cli.open",
2389
+ weight: "secondary"
2390
+ },
2391
+ {
2392
+ command: "fluncle random",
2393
+ exposedContent: ["the archive throws one back"],
2394
+ kind: "cli",
2395
+ name: "cli.random",
2396
+ weight: "secondary"
2397
+ },
2398
+ {
2399
+ command: "fluncle subscribe",
2400
+ exposedContent: ["subscribe to the Friday newsletter"],
2401
+ kind: "cli",
2402
+ name: "cli.subscribe",
2403
+ weight: "secondary"
2404
+ },
2405
+ {
2406
+ command: "fluncle submit",
2407
+ exposedContent: ["send a track for review"],
2408
+ kind: "cli",
2409
+ name: "cli.submit",
2410
+ weight: "secondary"
2411
+ },
2412
+ {
2413
+ command: "fluncle tracks get",
2414
+ exposedContent: ["look up one finding by id or Log ID (group alias `track`)"],
2415
+ kind: "cli",
2416
+ name: "cli.tracks-get",
2417
+ weight: "tertiary"
2418
+ },
2419
+ {
2420
+ command: "fluncle about",
2421
+ exposedContent: ["Fluncle, and where to find him"],
2422
+ kind: "cli",
2423
+ name: "cli.about",
2424
+ weight: "tertiary"
2425
+ },
2426
+ {
2427
+ command: "fluncle version",
2428
+ exposedContent: ["print or check the version (--check hits the latest GitHub release)"],
2429
+ kind: "cli",
2430
+ name: "cli.version",
2431
+ weight: "tertiary"
2432
+ },
2433
+ {
2434
+ command: "fluncle admin",
2435
+ exposedContent: [
2436
+ "the operator/agent command group (hidden): tracks publish|update|enrich|video|draft|social|preview|observe|context|note, mixtapes create|update|members|publish|delete, newsletter draft|update|send|list, backfills, auth"
2437
+ ],
2438
+ kind: "cli",
2439
+ name: "cli.admin",
2440
+ operatorNotes: "Authenticated admin/agent tier. The enrichment crons drive a subset of these.",
2441
+ weight: "hidden"
2442
+ },
2443
+ {
2444
+ command: "fluncle admin tracks enrich --queue",
2445
+ exposedContent: [
2446
+ "BPM / key / spectral analysis on the box, write-back (--no-agent, on-box DSP)"
2447
+ ],
2448
+ kind: "cron",
2449
+ name: "cron.enrich",
2450
+ operatorNotes: "every 5m. Pure compute, zero LLM tokens. Source: docs/agents/hermes/scripts/enrich-sweep.*",
2451
+ probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-enrich", kind: "cron" },
2452
+ weight: "hidden"
2453
+ },
2454
+ {
2455
+ command: "fluncle admin tracks context --queue",
2456
+ exposedContent: [
2457
+ "Firecrawl facts → distilled context_note + a Texture: line (Worker-side Haiku)"
2458
+ ],
2459
+ kind: "cron",
2460
+ name: "cron.context-note",
2461
+ operatorNotes: "every 5m. --no-agent trigger; the Worker does the Firecrawl + Haiku distill. Zero on-box tokens.",
2462
+ probeConfig: { cadenceMs: 5 * MINUTE_MS, cronName: "fluncle-context-note", kind: "cron" },
2463
+ weight: "hidden"
2464
+ },
2465
+ {
2466
+ command: "fluncle admin tracks note --queue",
2467
+ exposedContent: [
2468
+ "auto-author the editorial /log note, fill-empty-only (hybrid: one claude -p call)"
2469
+ ],
2470
+ kind: "cron",
2471
+ name: "cron.note",
2472
+ operatorNotes: "every 10m. Hybrid --no-agent; one claude -p authors the line. Never clobbers an operator note.",
2473
+ probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-note", kind: "cron" },
2474
+ weight: "hidden"
2475
+ },
2476
+ {
2477
+ command: "fluncle admin tracks observe --queue",
2478
+ exposedContent: [
2479
+ "author the recovered-audio script → Worker Cartesia render (hybrid: one claude -p call)"
2480
+ ],
2481
+ kind: "cron",
2482
+ name: "cron.observation",
2483
+ operatorNotes: "every 60m. Hybrid --no-agent; one claude -p authors the script, the Worker voice-gates + renders.",
2484
+ probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-observation", kind: "cron" },
2485
+ weight: "hidden"
2486
+ },
2487
+ {
2488
+ command: "fluncle admin backfills discogs && fluncle admin backfills lastfm",
2489
+ exposedContent: ["Discogs id + Last.fm love catalogue repair (--no-agent, Worker HTTP)"],
2490
+ kind: "cron",
2491
+ name: "cron.backfill",
2492
+ operatorNotes: "every 30m. Pure HTTP driving, zero LLM tokens. Agent tier.",
2493
+ probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-backfill", kind: "cron" },
2494
+ weight: "hidden"
2495
+ },
2496
+ {
2497
+ command: "fluncle admin tracks queue",
2498
+ exposedContent: [
2499
+ "wake the rave-03 render box → render + ship one finding's video → park (conductor)"
2500
+ ],
2501
+ kind: "cron",
2502
+ name: "cron.render",
2503
+ 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 `render-box`.",
2504
+ probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-render", kind: "cron" },
2505
+ weight: "hidden"
2506
+ },
2507
+ {
2508
+ exposedContent: [
2509
+ "probe each service → Discord-ping on a status flip → POST the /status snapshot (--no-agent)"
2510
+ ],
2511
+ kind: "cron",
2512
+ name: "cron.healthcheck",
2513
+ operatorNotes: "every 10m. Pure probing, zero LLM tokens. POSTs to the agent-tier record_health op that /status reads.",
2514
+ probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-healthcheck", kind: "cron" },
2515
+ weight: "hidden"
2516
+ },
2517
+ {
2518
+ command: "fluncle admin newsletter draft",
2519
+ exposedContent: [
2520
+ "draft + persist the weekly edition, then offer the operator a Discord Send button (the only agent cron)"
2521
+ ],
2522
+ kind: "cron",
2523
+ name: "cron.newsletter",
2524
+ operatorNotes: "Fri 15:00 Amsterdam (cron `0 15 * * 5`, box pinned Europe/Amsterdam for DST). The only full-agent cron. Send is operator-gated (agent token 403s send_edition). Source: docs/agents/hermes/cron/jobs.json.",
2525
+ probeConfig: {
2526
+ cadenceMs: 7 * 24 * 60 * MINUTE_MS,
2527
+ cronName: "fluncle-newsletter",
2528
+ kind: "cron"
2529
+ },
2530
+ weight: "secondary"
2531
+ }
2532
+ ];
2533
+ });
2534
+
2535
+ // src/commands/status.ts
2536
+ var exports_status = {};
2537
+ __export(exports_status, {
2538
+ statusLines: () => statusLines,
2539
+ statusCommand: () => statusCommand
2540
+ });
2541
+ async function statusCommand() {
2542
+ return publicApiGet("/api/status");
2543
+ }
2544
+ function ago(seconds) {
2545
+ if (seconds < 60) {
2546
+ return `${seconds}s ago`;
2547
+ }
2548
+ if (seconds < 3600) {
2549
+ return `${Math.round(seconds / 60)}m ago`;
2550
+ }
2551
+ if (seconds < 86400) {
2552
+ return `${Math.round(seconds / 3600)}h ago`;
2553
+ }
2554
+ return `${Math.round(seconds / 86400)}d ago`;
2555
+ }
2556
+ function statusLines(snapshot) {
2557
+ const { secondsSinceFreshestReport, services } = snapshot;
2558
+ if (services.length === 0) {
2559
+ return ["No service reports yet. The healthcheck hasn't called in."];
2560
+ }
2561
+ const down = services.filter((service) => service.status === "down").length;
2562
+ const degraded = services.filter((service) => service.status === "degraded").length;
2563
+ const headline = down > 0 ? `${down} service${down === 1 ? "" : "s"} down.` : degraded > 0 ? `${degraded} service${degraded === 1 ? "" : "s"} limping. The rest holds.` : "All services up. The Galaxy holds.";
2564
+ const nameWidth = services.reduce((width, service) => Math.max(width, service.service.length), 0);
2565
+ const stateWidth = services.reduce((width, service) => Math.max(width, service.status.length), 0);
2566
+ const lines = [headline, ""];
2567
+ for (const service of services) {
2568
+ const note = service.message ?? serviceLabels.get(service.service) ?? "";
2569
+ const row = [
2570
+ statusMarks[service.status],
2571
+ service.service.padEnd(nameWidth),
2572
+ service.status.padEnd(stateWidth),
2573
+ note
2574
+ ].join(" ").trimEnd();
2575
+ lines.push(row);
2576
+ }
2577
+ if (secondsSinceFreshestReport !== null) {
2578
+ lines.push("", `Last checked ${ago(secondsSinceFreshestReport)}.`);
2579
+ }
2580
+ return lines;
2581
+ }
2582
+ var serviceLabels, statusMarks;
2583
+ var init_status = __esm(() => {
2584
+ init_src();
2585
+ init_api();
2586
+ serviceLabels = (() => {
2587
+ const labels = new Map;
2588
+ for (const surface of SURFACES) {
2589
+ const match = surface.operatorNotes?.match(/service `([a-z0-9-]+)`/);
2590
+ const id = match?.[1];
2591
+ const label = surface.exposedContent[0];
2592
+ if (id !== undefined && label !== undefined && !labels.has(id)) {
2593
+ labels.set(id, label);
2594
+ }
2595
+ }
2596
+ return labels;
2597
+ })();
2598
+ statusMarks = {
2599
+ degraded: "~",
2600
+ down: "x",
2601
+ ok: "+"
2602
+ };
1918
2603
  });
1919
2604
 
1920
2605
  // src/commands/track.ts
@@ -5431,6 +6116,10 @@ function addMetaCommands(program2) {
5431
6116
  json: options.json
5432
6117
  });
5433
6118
  });
6119
+ program2.command("status").description("How Fluncle's services are holding up").option("--json", "Print JSON", false).action(async (options) => {
6120
+ const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), exports_status));
6121
+ await runStatus(options, statusCommand2);
6122
+ });
5434
6123
  }
5435
6124
  function addTrackCommands(program2) {
5436
6125
  const tracks = configureCommand(program2.command("tracks", { hidden: true }).alias("track").description("Public track lookups"));
@@ -6446,6 +7135,19 @@ async function runRandom(options, randomCommand2) {
6446
7135
  console.log(`Log: ${track.logPageUrl}`);
6447
7136
  }
6448
7137
  }
7138
+ async function runStatus(options, statusCommand2) {
7139
+ const snapshot = await statusCommand2();
7140
+ if (options.json) {
7141
+ printJson({
7142
+ ok: true,
7143
+ ...snapshot
7144
+ });
7145
+ return;
7146
+ }
7147
+ const { statusLines: statusLines2 } = await Promise.resolve().then(() => (init_status(), exports_status));
7148
+ console.log(statusLines2(snapshot).join(`
7149
+ `));
7150
+ }
6449
7151
  function rejectUnexpectedPositionals(positionals) {
6450
7152
  if (positionals.length === 0) {
6451
7153
  return;
@@ -6619,6 +7321,7 @@ Share:
6619
7321
 
6620
7322
  Meta:
6621
7323
  fluncle about Fluncle, and where to find him
7324
+ fluncle status [--json] How Fluncle's services are holding up
6622
7325
  fluncle version [--check] [--json] Print or check the version
6623
7326
 
6624
7327
  Fluncle elsewhere:
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.62.0"
34
+ "version": "0.63.0"
35
35
  }