fluncle 0.35.0 → 0.37.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 +123 -4
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -3789,7 +3789,7 @@ function parseVersion(version) {
3789
3789
  var currentVersion, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
3790
3790
  var init_version = __esm(() => {
3791
3791
  init_output();
3792
- currentVersion = "0.35.0".trim() ? "0.35.0".trim() : "0.1.0";
3792
+ currentVersion = "0.37.0".trim() ? "0.37.0".trim() : "0.1.0";
3793
3793
  });
3794
3794
 
3795
3795
  // src/commands/track.ts
@@ -3799,6 +3799,7 @@ __export(exports_track, {
3799
3799
  trackUpdateCommand: () => trackUpdateCommand,
3800
3800
  trackSocialUpdateCommand: () => trackSocialUpdateCommand,
3801
3801
  trackSocialShowCommand: () => trackSocialShowCommand,
3802
+ trackObserveCommand: () => trackObserveCommand,
3802
3803
  trackGetCommand: () => trackGetCommand,
3803
3804
  trackDraftCommand: () => trackDraftCommand
3804
3805
  });
@@ -3892,6 +3893,25 @@ async function trackUpdateCommand(trackId, options) {
3892
3893
  }
3893
3894
  return adminApiPatch(`/api/admin/tracks/${encodeURIComponent(trackId)}`, body);
3894
3895
  }
3896
+ async function trackObserveCommand(idOrLogId, options) {
3897
+ const body = { script: options.script };
3898
+ if (options.voiceId !== undefined) {
3899
+ body.voiceId = options.voiceId;
3900
+ }
3901
+ if (options.model !== undefined) {
3902
+ body.model = options.model;
3903
+ }
3904
+ if (options.durationMs !== undefined) {
3905
+ body.durationMs = options.durationMs;
3906
+ }
3907
+ if (options.durationTargetSec !== undefined) {
3908
+ body.durationTargetSec = options.durationTargetSec;
3909
+ }
3910
+ if (options.contextNote !== undefined) {
3911
+ body.contextNote = options.contextNote;
3912
+ }
3913
+ return adminApiPost(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/observe`, body);
3914
+ }
3895
3915
  var DEFAULT_VIDEO_MODEL = "anthropic/claude-opus-4-8", DEFAULT_VIDEO_REASONING = "high", FOUND_BASE = "https://found.fluncle.com", VIDEO_FIELDS;
3896
3916
  var init_track = __esm(() => {
3897
3917
  init_api();
@@ -3938,10 +3958,12 @@ var init_add = __esm(() => {
3938
3958
  var exports_admin_tracks = {};
3939
3959
  __export(exports_admin_tracks, {
3940
3960
  vehiclesCommand: () => vehiclesCommand,
3941
- queueCommand: () => queueCommand
3961
+ queueCommand: () => queueCommand,
3962
+ enrichSweepCommand: () => enrichSweepCommand,
3963
+ enrichQueueCommand: () => enrichQueueCommand
3942
3964
  });
3943
3965
  async function fetchAdminTracks(options) {
3944
- const { hasVideo, max, order } = options;
3966
+ const { hasVideo, max, order, status } = options;
3945
3967
  const results = [];
3946
3968
  let cursor;
3947
3969
  do {
@@ -3949,6 +3971,9 @@ async function fetchAdminTracks(options) {
3949
3971
  if (hasVideo !== undefined) {
3950
3972
  params.set("hasVideo", String(hasVideo));
3951
3973
  }
3974
+ if (status !== undefined) {
3975
+ params.set("status", status);
3976
+ }
3952
3977
  if (cursor) {
3953
3978
  params.set("cursor", cursor);
3954
3979
  }
@@ -3970,6 +3995,12 @@ async function fetchAdminTracks(options) {
3970
3995
  async function queueCommand(limit) {
3971
3996
  return fetchAdminTracks({ hasVideo: false, max: limit, order: "asc" });
3972
3997
  }
3998
+ async function enrichQueueCommand(limit) {
3999
+ return fetchAdminTracks({ max: limit, order: "asc", status: "queue" });
4000
+ }
4001
+ async function enrichSweepCommand(limit) {
4002
+ return adminApiPost(`/api/admin/enrich-sweep?limit=${limit}`);
4003
+ }
3973
4004
  async function vehiclesCommand(limit) {
3974
4005
  const tracks = await fetchAdminTracks({ hasVideo: true, max: limit, order: "desc" });
3975
4006
  return tracks.map((track) => ({
@@ -4929,7 +4960,7 @@ function trackDetailLines(track) {
4929
4960
  var COORD_FALLBACK2 = "—";
4930
4961
 
4931
4962
  // src/cli.ts
4932
- import { existsSync as existsSync2 } from "fs";
4963
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
4933
4964
  import path from "path";
4934
4965
 
4935
4966
  // ../../node_modules/.bun/commander@14.0.3/node_modules/commander/esm.mjs
@@ -5127,6 +5158,14 @@ function addAdminCommands(program2) {
5127
5158
  const { queueCommand: queueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5128
5159
  await runAdminQueue(options, queueCommand2);
5129
5160
  });
5161
+ admin.command("enrich-queue").description("Findings needing (re-)enrichment: pending, failed, or stuck processing").option("--limit <limit>", "Number of findings to show", "10").option("--json", "Print JSON", false).action(async (options) => {
5162
+ const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5163
+ await runAdminEnrichQueue(options, enrichQueueCommand2);
5164
+ });
5165
+ admin.command("enrich-sweep").description("Re-fire enrichment for everything in the enrich-queue (idempotent self-heal)").option("--limit <limit>", "Max findings to re-trigger", "25").option("--json", "Print JSON", false).action(async (options) => {
5166
+ const { enrichSweepCommand: enrichSweepCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5167
+ await runAdminEnrichSweep(options, enrichSweepCommand2);
5168
+ });
5130
5169
  admin.command("vehicles").description("Recent video vehicles, newest first (the style ledger for diversity)").option("--limit <limit>", "Number of vehicles to show", "10").option("--json", "Print JSON", false).action(async (options) => {
5131
5170
  const { vehiclesCommand: vehiclesCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5132
5171
  await runAdminVehicles(options, vehiclesCommand2);
@@ -5155,6 +5194,10 @@ function addAdminCommands(program2) {
5155
5194
  const { previewArchiveUploadCommand: previewArchiveUploadCommand2 } = await Promise.resolve().then(() => (init_preview_archive(), exports_preview_archive));
5156
5195
  await runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCommand2);
5157
5196
  });
5197
+ adminTrack.command("observe").description("Render Fluncle's spoken field observation for a track (ElevenLabs, Worker-side)").argument("[idOrLogId]").option("--script <text>", "The voice-gated observation script (the spoken text)").option("--script-file <file>", "Read the observation script from a file (e.g. observation.txt)").option("--voice-id <id>", "Override the configured ElevenLabs voice id").option("--model <model>", "TTS model (eleven_multilingual_v2 | eleven_v3)").option("--duration-ms <ms>", "Probed audio duration in ms (the agent runs ffprobe)").option("--duration-target-sec <sec>", "Target observation length in seconds (20\u201345)").option("--context-note <text>", "Pre-fetched factual context (else the Worker firecrawls)").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5198
+ const { trackObserveCommand: trackObserveCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
5199
+ await runTrackObserve(idOrLogId, options, trackObserveCommand2);
5200
+ });
5158
5201
  const adminMixtapes = configureCommand(admin.command("mixtapes").description("Mixtape admin commands"));
5159
5202
  adminMixtapes.action(() => {
5160
5203
  adminMixtapes.outputHelp();
@@ -5265,6 +5308,36 @@ async function runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCo
5265
5308
  console.log(` source: ${result.source}`);
5266
5309
  console.log(` mime: ${result.mime}`);
5267
5310
  }
5311
+ async function runTrackObserve(idOrLogId, options, trackObserveCommand2) {
5312
+ const script = options.scriptFile ? readFileSync2(options.scriptFile, "utf8") : options.script;
5313
+ if (!idOrLogId || !script || !script.trim()) {
5314
+ throw new Error("Usage: fluncle admin track observe <track_id|log_id> (--script <text> | --script-file <file>) [--voice-id <id>] [--model <model>] [--duration-ms <ms>] [--context-note <text>] [--json]");
5315
+ }
5316
+ const durationMs = options.durationMs === undefined ? undefined : Number.parseInt(options.durationMs, 10);
5317
+ if (durationMs !== undefined && (!Number.isInteger(durationMs) || durationMs < 1)) {
5318
+ throw new Error("--duration-ms must be a positive integer");
5319
+ }
5320
+ const durationTargetSec = options.durationTargetSec === undefined ? undefined : Number.parseInt(options.durationTargetSec, 10);
5321
+ if (durationTargetSec !== undefined && (!Number.isInteger(durationTargetSec) || durationTargetSec < 1)) {
5322
+ throw new Error("--duration-target-sec must be a positive integer");
5323
+ }
5324
+ const result = await trackObserveCommand2(idOrLogId, {
5325
+ contextNote: options.contextNote,
5326
+ durationMs,
5327
+ durationTargetSec,
5328
+ model: options.model,
5329
+ script: script.trim(),
5330
+ voiceId: options.voiceId
5331
+ });
5332
+ if (options.json) {
5333
+ printJson(result);
5334
+ return;
5335
+ }
5336
+ console.log(`Recorded observation for ${result.logId}`);
5337
+ console.log(` audio: ${result.audioUrl}`);
5338
+ console.log(` length: ${Math.round(result.durationMs / 1000)}s`);
5339
+ console.log(` voice: ${result.voiceId}`);
5340
+ }
5268
5341
  async function runPreviewArchiveBackfill(options, previewArchiveBackfillCommand2) {
5269
5342
  const limit = options.limit === undefined ? undefined : Number.parseInt(options.limit, 10);
5270
5343
  if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) {
@@ -5653,6 +5726,52 @@ async function runAdminQueue(options, queueCommand2) {
5653
5726
  console.log(trackRows2(tracks).join(`
5654
5727
  `));
5655
5728
  }
5729
+ async function runAdminEnrichQueue(options, enrichQueueCommand2) {
5730
+ const limit = parseListLimit(options.limit);
5731
+ const tracks = await enrichQueueCommand2(limit);
5732
+ if (options.json) {
5733
+ printJson({
5734
+ ok: true,
5735
+ tracks
5736
+ });
5737
+ return;
5738
+ }
5739
+ if (tracks.length === 0) {
5740
+ console.log("Nothing awaiting enrichment. Every finding is enriched.");
5741
+ return;
5742
+ }
5743
+ const { trackRows: trackRows2 } = await Promise.resolve().then(() => exports_format);
5744
+ const noun = tracks.length === 1 ? "finding" : "findings";
5745
+ console.log(`${tracks.length} ${noun} needing (re-)enrichment, oldest first:`);
5746
+ console.log(trackRows2(tracks).join(`
5747
+ `));
5748
+ }
5749
+ async function runAdminEnrichSweep(options, enrichSweepCommand2) {
5750
+ const limit = parseListLimit(options.limit);
5751
+ const result = await enrichSweepCommand2(limit);
5752
+ if (options.json) {
5753
+ printJson({
5754
+ ok: true,
5755
+ reEnriched: result.reEnriched,
5756
+ reEnrichedCount: result.reEnrichedCount,
5757
+ skipped: result.skipped,
5758
+ skippedCount: result.skippedCount
5759
+ });
5760
+ return;
5761
+ }
5762
+ if (result.reEnrichedCount === 0 && result.skippedCount === 0) {
5763
+ console.log("Nothing to sweep. The enrich-queue is empty.");
5764
+ return;
5765
+ }
5766
+ const noun = result.reEnrichedCount === 1 ? "finding" : "findings";
5767
+ console.log(`Re-fired enrichment for ${result.reEnrichedCount} ${noun}.`);
5768
+ for (const entry of result.reEnriched) {
5769
+ console.log(` ${entry.logId || entry.trackId} (was ${entry.status})`);
5770
+ }
5771
+ if (result.skippedCount > 0) {
5772
+ console.log(`Skipped ${result.skippedCount} with no Log ID yet.`);
5773
+ }
5774
+ }
5656
5775
  async function runAdminVehicles(options, vehiclesCommand2) {
5657
5776
  const limit = parseListLimit(options.limit);
5658
5777
  const vehicles = await vehiclesCommand2(limit);
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.35.0"
34
+ "version": "0.37.0"
35
35
  }