fluncle 0.131.0 → 0.133.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 +163 -6
  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.131.0".trim() ? "0.131.0".trim() : "0.1.0";
564
+ currentVersion = "0.133.0".trim() ? "0.133.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.131.0".trim() ? "0.131.0".trim() : "0.1.0";
2192
+ currentVersion2 = "0.133.0".trim() ? "0.133.0".trim() : "0.1.0";
2193
2193
  });
2194
2194
 
2195
2195
  // ../../packages/registry/src/index.ts
@@ -2651,14 +2651,25 @@ var init_src = __esm(() => {
2651
2651
  },
2652
2652
  {
2653
2653
  apiFormat: "application/xml",
2654
- exposedContent: ["the XML sitemap of every public page"],
2654
+ exposedContent: ["the XML sitemap index of every public page"],
2655
2655
  kind: "discovery",
2656
2656
  name: "discovery.sitemap",
2657
+ operatorNotes: "A sitemap INDEX, not a flat urlset: the URLs live in children at /sitemap/<kind>-<n>.xml (pages/findings/graph/logbook), each auto-paged under Google's 50,000-URL ceiling so a breach cannot happen rather than merely not having happened yet. robots.txt still names this one URL — a crawler discovers the children from here. Splitting by kind is also the diagnostic: Search Console reports coverage PER sitemap.",
2657
2658
  probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2658
2659
  route: "/sitemap.xml",
2659
2660
  url: `${SITE}/sitemap.xml`,
2660
2661
  weights: { web: "secondary" }
2661
2662
  },
2663
+ {
2664
+ apiFormat: "application/xml",
2665
+ exposedContent: ["one child sitemap: the pages / findings / graph / logbook URLs, auto-paged"],
2666
+ kind: "discovery",
2667
+ name: "discovery.sitemap-shard",
2668
+ probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
2669
+ route: "/sitemap/$shard",
2670
+ url: `${SITE}/sitemap/pages-1.xml`,
2671
+ weights: {}
2672
+ },
2662
2673
  {
2663
2674
  apiFormat: "text/plain",
2664
2675
  exposedContent: [
@@ -2836,6 +2847,15 @@ var init_src = __esm(() => {
2836
2847
  name: "cli.tracks-get",
2837
2848
  weights: { cli: "tertiary" }
2838
2849
  },
2850
+ {
2851
+ command: "fluncle tracks similar",
2852
+ exposedContent: [
2853
+ "the findings that sound nearest to one (the sonic neighbourhood, off the MuQ audio embedding), each with its note"
2854
+ ],
2855
+ kind: "cli",
2856
+ name: "cli.tracks-similar",
2857
+ weights: { cli: "tertiary" }
2858
+ },
2839
2859
  {
2840
2860
  command: "fluncle about",
2841
2861
  exposedContent: ["Fluncle, and where to find him"],
@@ -3221,6 +3241,7 @@ __export(exports_track, {
3221
3241
  trackSocialUpdateCommand: () => trackSocialUpdateCommand,
3222
3242
  trackSocialShowCommand: () => trackSocialShowCommand,
3223
3243
  trackSocialCaptureCommand: () => trackSocialCaptureCommand,
3244
+ trackSimilarCommand: () => trackSimilarCommand,
3224
3245
  trackRequeueVideoCommand: () => trackRequeueVideoCommand,
3225
3246
  trackPurgeVideoCommand: () => trackPurgeVideoCommand,
3226
3247
  trackObserveCommand: () => trackObserveCommand,
@@ -3235,6 +3256,10 @@ __export(exports_track, {
3235
3256
  async function trackGetCommand(idOrLogId) {
3236
3257
  return publicApiGet(`/api/tracks/${encodeURIComponent(idOrLogId)}`);
3237
3258
  }
3259
+ async function trackSimilarCommand(idOrLogId, limit) {
3260
+ const query = limit === undefined ? "" : `?limit=${encodeURIComponent(String(limit))}`;
3261
+ return publicApiGet(`/api/tracks/${encodeURIComponent(idOrLogId)}/similar${query}`);
3262
+ }
3238
3263
  async function trackGetAdminCommand(idOrLogId) {
3239
3264
  return adminApiGet(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}`);
3240
3265
  }
@@ -3438,6 +3463,9 @@ async function trackContextCommand(idOrLogId, options = {}) {
3438
3463
  }
3439
3464
  async function trackNoteCommand(idOrLogId, options) {
3440
3465
  const body = { note: options.note };
3466
+ if (options.dryRun) {
3467
+ body.dryRun = true;
3468
+ }
3441
3469
  return adminApiPost(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/note`, body);
3442
3470
  }
3443
3471
  var DEFAULT_VIDEO_MODEL = "anthropic/claude-opus-4-8", DEFAULT_VIDEO_REASONING = "high", FOUND_BASE = "https://found.fluncle.com", VIDEO_FIELDS, RERENDER_CONTRACT_FIELDS, RERENDER_ADVISORY_FIELDS, FOOTAGE_FIELDS, PLATE_FIELDS, NON_FILE_OPTIONS;
@@ -4200,6 +4228,22 @@ var init_publish = __esm(() => {
4200
4228
  init_api();
4201
4229
  });
4202
4230
 
4231
+ // src/commands/capture.ts
4232
+ var exports_capture = {};
4233
+ __export(exports_capture, {
4234
+ setCaptureBudgetCommand: () => setCaptureBudgetCommand,
4235
+ captureBudgetCommand: () => captureBudgetCommand
4236
+ });
4237
+ async function captureBudgetCommand() {
4238
+ return adminApiGet("/api/admin/catalogue/capture-budget");
4239
+ }
4240
+ async function setCaptureBudgetCommand(input) {
4241
+ return adminApiPut("/api/admin/catalogue/capture-budget", input);
4242
+ }
4243
+ var init_capture = __esm(() => {
4244
+ init_api();
4245
+ });
4246
+
4203
4247
  // ../../packages/contracts/src/util/multipart.ts
4204
4248
  function planMultipart(contentLength, partSize = DEFAULT_PART_SIZE) {
4205
4249
  if (!Number.isInteger(contentLength) || contentLength <= 0) {
@@ -8019,6 +8063,10 @@ function addTrackCommands(program2) {
8019
8063
  const { trackGetCommand: trackGetCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
8020
8064
  await runTrackGet(idOrLogId, options, trackGetCommand2);
8021
8065
  });
8066
+ tracks.command("similar").description("The findings that sound nearest to one (the sonic neighbourhood)").argument("[idOrLogId]").option("--limit <limit>", "Number of neighbours to show", "6").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
8067
+ const { trackSimilarCommand: trackSimilarCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
8068
+ await runTrackSimilar(idOrLogId, options, trackSimilarCommand2);
8069
+ });
8022
8070
  }
8023
8071
  function addAdminCommands(program2) {
8024
8072
  const admin = configureCommand(program2.command("admin", { hidden: true }).description("Operator commands"));
@@ -8147,7 +8195,7 @@ function addAdminCommands(program2) {
8147
8195
  const { trackContextCommand: trackContextCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
8148
8196
  await runTrackContext(idOrLogId, options, trackContextCommand2);
8149
8197
  });
8150
- adminTrack.command("note").description("Author the editorial note for a finding (fills an empty note only)").argument("[idOrLogId]").option("--queue", "Show the note worklist (context'd findings with no note yet), oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--script <text>", "The voice-gated editorial note").option("--script-file <file>", "Read the editorial note from a file").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
8198
+ adminTrack.command("note").description("Author the editorial note for a finding (fills an empty note only)").argument("[idOrLogId]").option("--queue", "Show the note worklist (context'd findings with no note yet), oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--script <text>", "The voice-gated editorial note").option("--script-file <file>", "Read the editorial note from a file").option("--dry-run", "Run the voice + echo gates and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
8151
8199
  if (options.queue) {
8152
8200
  const { noteQueueCommand: noteQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
8153
8201
  await runAdminNoteQueue(options, noteQueueCommand2);
@@ -8200,6 +8248,26 @@ function addAdminCommands(program2) {
8200
8248
  const { publishAdvancePauseCommand: publishAdvancePauseCommand2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
8201
8249
  await runPublishAdvancePause(false, options, publishAdvancePauseCommand2);
8202
8250
  });
8251
+ const adminCapture = configureCommand(admin.command("capture").description("The metered audio-capture budget and its kill switch"));
8252
+ adminCapture.action(() => {
8253
+ adminCapture.outputHelp();
8254
+ });
8255
+ adminCapture.command("budget").description("What catalogue capture spent in the last 24h, and how much budget is left").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
8256
+ const { captureBudgetCommand: captureBudgetCommand2 } = await Promise.resolve().then(() => (init_capture(), exports_capture));
8257
+ await runCaptureBudget(options, captureBudgetCommand2);
8258
+ });
8259
+ adminCapture.command("pause").description("Stop all catalogue audio capture \u2014 the kill switch (operator)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
8260
+ const { setCaptureBudgetCommand: setCaptureBudgetCommand2 } = await Promise.resolve().then(() => (init_capture(), exports_capture));
8261
+ await runSetCaptureBudget({ paused: true }, options, setCaptureBudgetCommand2);
8262
+ });
8263
+ adminCapture.command("resume").description("Let catalogue audio capture spend again, up to its budget (operator)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
8264
+ const { setCaptureBudgetCommand: setCaptureBudgetCommand2 } = await Promise.resolve().then(() => (init_capture(), exports_capture));
8265
+ await runSetCaptureBudget({ paused: false }, options, setCaptureBudgetCommand2);
8266
+ });
8267
+ adminCapture.command("set").description("Set the rolling-24h capture caps (operator)").option("--tracks <n>", "Max catalogue downloads per rolling 24h").option("--gb <n>", "Max GB downloaded per rolling 24h").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
8268
+ const { setCaptureBudgetCommand: setCaptureBudgetCommand2 } = await Promise.resolve().then(() => (init_capture(), exports_capture));
8269
+ await runSetCaptureCaps(options, setCaptureBudgetCommand2);
8270
+ });
8203
8271
  const adminClips = configureCommand(admin.command("clips").description("Mixtape clip (Fluncle Studio) commands"));
8204
8272
  adminClips.action(() => {
8205
8273
  adminClips.outputHelp();
@@ -8509,9 +8577,12 @@ async function runTrackContext(idOrLogId, options, trackContextCommand2) {
8509
8577
  async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
8510
8578
  const note = options.scriptFile ? readFileSync6(options.scriptFile, "utf8") : options.script;
8511
8579
  if (!idOrLogId || !note || !note.trim()) {
8512
- throw new Error("Usage: fluncle admin tracks note <track_id|log_id> (--script <text> | --script-file <file>) [--json]");
8580
+ throw new Error("Usage: fluncle admin tracks note <track_id|log_id> (--script <text> | --script-file <file>) [--dry-run] [--json]");
8513
8581
  }
8514
- const result = await trackNoteCommand2(idOrLogId, { note: note.trim() });
8582
+ const result = await trackNoteCommand2(idOrLogId, {
8583
+ dryRun: options.dryRun,
8584
+ note: note.trim()
8585
+ });
8515
8586
  if (options.json) {
8516
8587
  printJson(result);
8517
8588
  return;
@@ -8520,9 +8591,39 @@ async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
8520
8591
  console.log(`A note is already on file for ${result.logId}. The operator's note stands.`);
8521
8592
  return;
8522
8593
  }
8594
+ if (result.dryRun) {
8595
+ console.log(`Both gates passed for ${result.logId}. Nothing was stored (dry run).`);
8596
+ console.log(` ${result.note}`);
8597
+ if (result.echo?.logId) {
8598
+ const reading = result.echo.phrase ? `lifts "${result.echo.phrase}" from ${result.echo.logId}` : `${Math.round(result.echo.overlap * 100)}% word overlap with ${result.echo.logId}`;
8599
+ console.log(` echo: ${reading}`);
8600
+ }
8601
+ return;
8602
+ }
8523
8603
  console.log(`Authored the note for ${result.logId}:`);
8524
8604
  console.log(` ${result.note}`);
8525
8605
  }
8606
+ async function runTrackSimilar(idOrLogId, options, trackSimilarCommand2) {
8607
+ if (!idOrLogId) {
8608
+ throw new Error("Missing id. Usage: fluncle tracks similar <track_id|log_id> [--limit <n>] [--json]");
8609
+ }
8610
+ const limit = options.limit ? Number.parseInt(options.limit, 10) : undefined;
8611
+ const result = await trackSimilarCommand2(idOrLogId, limit !== undefined && Number.isFinite(limit) ? limit : undefined);
8612
+ if (options.json) {
8613
+ printJson(result);
8614
+ return;
8615
+ }
8616
+ if (result.findings.length === 0) {
8617
+ console.log("Nothing sounds near this one yet. It may still be waiting on its embedding.");
8618
+ return;
8619
+ }
8620
+ for (const finding of result.findings) {
8621
+ console.log(`${finding.logId ?? "\u2014"} ${finding.artists.join(", ")} \u2014 ${finding.title}`);
8622
+ if (finding.note) {
8623
+ console.log(` ${finding.note}`);
8624
+ }
8625
+ }
8626
+ }
8526
8627
  function printSweepJson(payload, failedCount) {
8527
8628
  printJson({ ...payload, failedCount, ok: failedCount === 0 });
8528
8629
  if (failedCount > 0) {
@@ -9319,6 +9420,60 @@ async function runPublishAdvance(options, publishAdvanceCommand2) {
9319
9420
  console.log(`Failed ${failure.platform} for ${failure.trackId} \u2014 left for the operator.`);
9320
9421
  }
9321
9422
  }
9423
+ var GB = 1073741824;
9424
+ function formatGb(bytes) {
9425
+ return `${(bytes / GB).toFixed(2)} GB`;
9426
+ }
9427
+ function printCaptureBudget(state) {
9428
+ const reason = state.closedReason === "paused" ? "paused \u2014 you stopped it" : state.closedReason === "tracks_spent" ? "the daily track cap is spent" : state.closedReason === "bytes_spent" ? "the daily GB cap is spent" : "";
9429
+ console.log(state.open ? "Catalogue capture is RUNNING \u2014 it may buy audio, up to the budget below." : `Catalogue capture is STOPPED (${reason}). Findings still capture as normal.`);
9430
+ console.log(` Last ${state.windowHours}h: ${state.spend.tracks} ${state.spend.tracks === 1 ? "track" : "tracks"} \xB7 ${formatGb(state.spend.bytes)}`);
9431
+ console.log(` Budget: ${state.budget.dailyTracks} tracks \xB7 ${formatGb(state.budget.dailyBytes)} per ${state.windowHours}h`);
9432
+ console.log(` Left: ${state.remainingTracks} ${state.remainingTracks === 1 ? "track" : "tracks"} \xB7 ${formatGb(state.remainingBytes)}`);
9433
+ }
9434
+ async function runCaptureBudget(options, captureBudgetCommand2) {
9435
+ const state = await captureBudgetCommand2();
9436
+ if (options.json) {
9437
+ printJson(state);
9438
+ return;
9439
+ }
9440
+ printCaptureBudget(state);
9441
+ }
9442
+ async function runSetCaptureBudget(input, options, setCaptureBudgetCommand2) {
9443
+ const state = await setCaptureBudgetCommand2(input);
9444
+ if (options.json) {
9445
+ printJson(state);
9446
+ return;
9447
+ }
9448
+ printCaptureBudget(state);
9449
+ }
9450
+ async function runSetCaptureCaps(options, setCaptureBudgetCommand2) {
9451
+ const input = {};
9452
+ if (options.tracks !== undefined) {
9453
+ const tracks = Number(options.tracks);
9454
+ if (!Number.isInteger(tracks) || tracks < 0) {
9455
+ console.error("--tracks must be a whole number of tracks (0 or more).");
9456
+ process.exitCode = 1;
9457
+ return;
9458
+ }
9459
+ input.dailyTracks = tracks;
9460
+ }
9461
+ if (options.gb !== undefined) {
9462
+ const gb = Number(options.gb);
9463
+ if (!Number.isFinite(gb) || gb < 0) {
9464
+ console.error("--gb must be a number of gigabytes (0 or more).");
9465
+ process.exitCode = 1;
9466
+ return;
9467
+ }
9468
+ input.dailyBytes = Math.round(gb * GB);
9469
+ }
9470
+ if (input.dailyTracks === undefined && input.dailyBytes === undefined) {
9471
+ console.error("Nothing to set \u2014 pass --tracks and/or --gb.");
9472
+ process.exitCode = 1;
9473
+ return;
9474
+ }
9475
+ await runSetCaptureBudget(input, options, setCaptureBudgetCommand2);
9476
+ }
9322
9477
  async function runPublishAdvancePause(paused, options, publishAdvancePauseCommand2) {
9323
9478
  const result = await publishAdvancePauseCommand2(paused);
9324
9479
  if (options.json) {
@@ -10189,6 +10344,7 @@ var stringOptions = new Set([
10189
10344
  "--footage-social",
10190
10345
  "--from",
10191
10346
  "--galaxy-id",
10347
+ "--gb",
10192
10348
  "--has-key",
10193
10349
  "--intent",
10194
10350
  "--key",
@@ -10227,6 +10383,7 @@ var stringOptions = new Set([
10227
10383
  "--title",
10228
10384
  "--token",
10229
10385
  "--tracklist-file",
10386
+ "--tracks",
10230
10387
  "--url",
10231
10388
  "--verdict",
10232
10389
  "--verdict-file",
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.131.0"
34
+ "version": "0.133.0"
35
35
  }