fluncle 0.50.0 → 0.52.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 +67 -39
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -408,7 +408,7 @@ function compareVersions(left, right) {
408
408
  const leftParts = parseVersion(left);
409
409
  const rightParts = parseVersion(right);
410
410
  for (let index = 0;index < 3; index += 1) {
411
- const difference = leftParts[index] - rightParts[index];
411
+ const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
412
412
  if (difference !== 0) {
413
413
  return difference;
414
414
  }
@@ -417,16 +417,13 @@ function compareVersions(left, right) {
417
417
  }
418
418
  function parseVersion(version) {
419
419
  const parts = version.split(".").map((part) => Number.parseInt(part, 10));
420
- return [
421
- Number.isInteger(parts[0]) ? parts[0] : 0,
422
- Number.isInteger(parts[1]) ? parts[1] : 0,
423
- Number.isInteger(parts[2]) ? parts[2] : 0
424
- ];
420
+ const integerOrZero = (value) => value !== undefined && Number.isInteger(value) ? value : 0;
421
+ return [integerOrZero(parts[0]), integerOrZero(parts[1]), integerOrZero(parts[2])];
425
422
  }
426
423
  var currentVersion;
427
424
  var init_version = __esm(() => {
428
425
  init_output();
429
- currentVersion = "0.50.0".trim() ? "0.50.0".trim() : "0.1.0";
426
+ currentVersion = "0.52.0".trim() ? "0.52.0".trim() : "0.1.0";
430
427
  });
431
428
 
432
429
  // src/update-notifier.ts
@@ -483,6 +480,9 @@ function shouldNotify(args) {
483
480
  function firstPositional(args) {
484
481
  for (let index = 0;index < args.length; index += 1) {
485
482
  const arg = args[index];
483
+ if (arg === undefined) {
484
+ continue;
485
+ }
486
486
  if (arg === "--env") {
487
487
  index += 1;
488
488
  continue;
@@ -1237,6 +1237,9 @@ async function mixtapeDistributeCommand(idOrLogId, options, onProgress = () => {
1237
1237
  }
1238
1238
  const results = [];
1239
1239
  if (doYoutube) {
1240
+ if (!options.video) {
1241
+ throw new CliError2("missing_video", "YouTube distribution needs --video <mp4>");
1242
+ }
1240
1243
  onProgress("YouTube: uploading video…");
1241
1244
  const { distributeYoutube: distributeYoutube2 } = await Promise.resolve().then(() => (init_mixtape_youtube(), exports_mixtape_youtube));
1242
1245
  const result = await distributeYoutube2(mixtapeId, options.video, onProgress);
@@ -1244,6 +1247,9 @@ async function mixtapeDistributeCommand(idOrLogId, options, onProgress = () => {
1244
1247
  onProgress(`YouTube: ${result.url}`);
1245
1248
  }
1246
1249
  if (doMixcloud) {
1250
+ if (!options.audio) {
1251
+ throw new CliError2("missing_audio", "Mixcloud distribution needs --audio <file>");
1252
+ }
1247
1253
  onProgress("Mixcloud: uploading audio…");
1248
1254
  const { distributeMixcloud: distributeMixcloud2 } = await Promise.resolve().then(() => (init_mixtape_mixcloud(), exports_mixtape_mixcloud));
1249
1255
  const result = await distributeMixcloud2(mixtapeId, options.audio, onProgress, options.unlisted);
@@ -1327,11 +1333,15 @@ function parseCueSheet(text) {
1327
1333
  }
1328
1334
  const match = trimmed.match(/^(\d{1,2}:\d{2}(?::\d{2})?)\s+(.+)$/);
1329
1335
  if (match) {
1330
- const startMs = parseDuration(match[1]);
1336
+ const [, time, ref] = match;
1337
+ if (time === undefined || ref === undefined) {
1338
+ continue;
1339
+ }
1340
+ const startMs = parseDuration(time);
1331
1341
  if (startMs === null) {
1332
1342
  continue;
1333
1343
  }
1334
- entries.push({ ref: match[2].trim(), startMs });
1344
+ entries.push({ ref: ref.trim(), startMs });
1335
1345
  } else {
1336
1346
  entries.push({ ref: trimmed });
1337
1347
  }
@@ -1354,12 +1364,18 @@ function parseDuration(input) {
1354
1364
  }
1355
1365
  if (parts.length === 3) {
1356
1366
  const [hours, minutes2, seconds2] = nums;
1367
+ if (hours === undefined || minutes2 === undefined || seconds2 === undefined) {
1368
+ return null;
1369
+ }
1357
1370
  if (minutes2 >= 60 || seconds2 >= 60) {
1358
1371
  return null;
1359
1372
  }
1360
1373
  return Math.round((hours * 3600 + minutes2 * 60 + seconds2) * 1000);
1361
1374
  }
1362
1375
  const [minutes, seconds] = nums;
1376
+ if (minutes === undefined || seconds === undefined) {
1377
+ return null;
1378
+ }
1363
1379
  if (seconds >= 60) {
1364
1380
  return null;
1365
1381
  }
@@ -1881,7 +1897,7 @@ function compareVersions2(left, right) {
1881
1897
  const leftParts = parseVersion2(left);
1882
1898
  const rightParts = parseVersion2(right);
1883
1899
  for (let index = 0;index < 3; index += 1) {
1884
- const difference = leftParts[index] - rightParts[index];
1900
+ const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
1885
1901
  if (difference !== 0) {
1886
1902
  return difference;
1887
1903
  }
@@ -1890,16 +1906,13 @@ function compareVersions2(left, right) {
1890
1906
  }
1891
1907
  function parseVersion2(version) {
1892
1908
  const parts = version.split(".").map((part) => Number.parseInt(part, 10));
1893
- return [
1894
- Number.isInteger(parts[0]) ? parts[0] : 0,
1895
- Number.isInteger(parts[1]) ? parts[1] : 0,
1896
- Number.isInteger(parts[2]) ? parts[2] : 0
1897
- ];
1909
+ const integerOrZero = (value) => value !== undefined && Number.isInteger(value) ? value : 0;
1910
+ return [integerOrZero(parts[0]), integerOrZero(parts[1]), integerOrZero(parts[2])];
1898
1911
  }
1899
1912
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
1900
1913
  var init_version2 = __esm(() => {
1901
1914
  init_output();
1902
- currentVersion2 = "0.50.0".trim() ? "0.50.0".trim() : "0.1.0";
1915
+ currentVersion2 = "0.52.0".trim() ? "0.52.0".trim() : "0.1.0";
1903
1916
  });
1904
1917
 
1905
1918
  // src/commands/track.ts
@@ -2187,7 +2200,7 @@ async function previewArchiveUploadCommand(idOrLogId, options) {
2187
2200
  form.append("preview", Bun.file(options.file), basename(options.file));
2188
2201
  form.append("source", options.source);
2189
2202
  form.append("mime", options.mime);
2190
- return adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/preview-archive`, form);
2203
+ return adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/preview`, form);
2191
2204
  }
2192
2205
  async function previewArchiveBackfillCommand(options) {
2193
2206
  const result = {
@@ -2207,7 +2220,7 @@ async function previewArchiveBackfillCommand(options) {
2207
2220
  result.skipped.push({ reason: "no_log_id", trackId: track.trackId });
2208
2221
  continue;
2209
2222
  }
2210
- const status = await adminApiGet(`/api/admin/tracks/${encodeURIComponent(track.trackId)}/preview-archive`);
2223
+ const status = await adminApiGet(`/api/admin/tracks/${encodeURIComponent(track.trackId)}/preview`);
2211
2224
  if (status.archived) {
2212
2225
  result.skipped.push({ reason: "already_archived", trackId: track.trackId });
2213
2226
  continue;
@@ -2246,7 +2259,7 @@ async function uploadResolvedPreview(trackId, preview) {
2246
2259
  form.append("preview", new Blob([preview.bytes], { type: preview.mime }), `preview.${extension}`);
2247
2260
  form.append("source", preview.source);
2248
2261
  form.append("mime", preview.mime);
2249
- await adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(trackId)}/preview-archive`, form);
2262
+ await adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(trackId)}/preview`, form);
2250
2263
  }
2251
2264
  async function resolvePreview(track) {
2252
2265
  const stored = await downloadPreview(track.previewUrl, "deezer:stored");
@@ -2992,6 +3005,9 @@ async function paginateWithKeyboard(options) {
2992
3005
  const columns = stdout.columns ?? 80;
2993
3006
  const before = pages.slice(0, index).reduce((count, page2) => count + page2.lines.length, 0);
2994
3007
  const page = pages[index];
3008
+ if (page === undefined) {
3009
+ return;
3010
+ }
2995
3011
  const start = before + 1;
2996
3012
  const end = before + page.lines.length;
2997
3013
  const loading = busy ? " · loading…" : "";
@@ -3003,7 +3019,7 @@ async function paginateWithKeyboard(options) {
3003
3019
  `);
3004
3020
  }
3005
3021
  function loadNext() {
3006
- const cursor = pages[index].nextCursor;
3022
+ const cursor = pages[index]?.nextCursor;
3007
3023
  if (cursor === undefined) {
3008
3024
  return;
3009
3025
  }
@@ -5330,8 +5346,8 @@ function addMetaCommands(program2) {
5330
5346
  });
5331
5347
  }
5332
5348
  function addTrackCommands(program2) {
5333
- const track = configureCommand(program2.command("track", { hidden: true }).description("Public track lookups"));
5334
- track.command("get").description("Look up one finding by id or Log ID").argument("[idOrLogId]").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5349
+ const tracks = configureCommand(program2.command("tracks", { hidden: true }).alias("track").description("Public track lookups"));
5350
+ tracks.command("get").description("Look up one finding by id or Log ID").argument("[idOrLogId]").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5335
5351
  const { trackGetCommand: trackGetCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
5336
5352
  await runTrackGet(idOrLogId, options, trackGetCommand2);
5337
5353
  });
@@ -5364,19 +5380,12 @@ function addAdminCommands(program2) {
5364
5380
  const { queueCommand: queueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5365
5381
  await runAdminQueue(options, queueCommand2);
5366
5382
  });
5367
- adminTracks.command("context-queue").description("Findings missing their field notes, oldest first (the context cron's worklist)").option("--limit <limit>", "Number of findings to show", "10").option("--json", "Print JSON", false).action(async (options) => {
5368
- const { contextQueueCommand: contextQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5369
- await runAdminContextQueue(options, contextQueueCommand2);
5370
- });
5371
- adminTracks.command("observe-queue").description("Findings with notes but no observation yet, oldest first (the observe cron's worklist)").option("--limit <limit>", "Number of findings to show", "10").option("--json", "Print JSON", false).action(async (options) => {
5372
- const { observeQueueCommand: observeQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5373
- await runAdminObserveQueue(options, observeQueueCommand2);
5374
- });
5375
- adminTracks.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) => {
5376
- const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5377
- await runAdminEnrichQueue(options, enrichQueueCommand2);
5378
- });
5379
- admin.command("enrich-queue", { hidden: true }).description("Enrich queue (alias of `admin tracks enrich-queue`)").option("--limit <limit>", "Number of findings to show", "10").option("--json", "Print JSON", false).action(async (options) => {
5383
+ adminTracks.command("enrich").description("Enrichment worklist (pending, failed, or stuck processing) \u2014 use --queue").option("--queue", "Show the enrichment worklist, oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--json", "Print JSON", false).action(async (options) => {
5384
+ if (!options.queue) {
5385
+ console.error("`tracks enrich` is a worklist view \u2014 enrichment runs on the on-box `fluncle-enrich` cron.\nUse `tracks enrich --queue` to see findings needing (re-)enrichment.");
5386
+ process.exitCode = 1;
5387
+ return;
5388
+ }
5380
5389
  const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5381
5390
  await runAdminEnrichQueue(options, enrichQueueCommand2);
5382
5391
  });
@@ -5405,15 +5414,25 @@ function addAdminCommands(program2) {
5405
5414
  const { trackSocialShowCommand: trackSocialShowCommand2, trackSocialUpdateCommand: trackSocialUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
5406
5415
  await runTrackSocial(idOrLogId, options, trackSocialShowCommand2, trackSocialUpdateCommand2);
5407
5416
  });
5408
- adminTrack.command("preview").alias("preview-archive").description("Store one official preview at the operator-only archive path for analysis").argument("[idOrLogId]").option("--file <file>", "Preview audio file to archive").option("--mime <mime>", "MIME type of the preview audio").option("--source <source>", "Provenance label for the archived preview").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5417
+ adminTrack.command("preview").description("Store one official preview at the operator-only archive path for analysis").argument("[idOrLogId]").option("--file <file>", "Preview audio file to archive").option("--mime <mime>", "MIME type of the preview audio").option("--source <source>", "Provenance label for the archived preview").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5409
5418
  const { previewArchiveUploadCommand: previewArchiveUploadCommand2 } = await Promise.resolve().then(() => (init_preview_archive(), exports_preview_archive));
5410
5419
  await runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCommand2);
5411
5420
  });
5412
- 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) => {
5421
+ adminTrack.command("observe").description("Render Fluncle's spoken field observation for a track (ElevenLabs, Worker-side)").argument("[idOrLogId]").option("--queue", "Show the observe worklist (notes but no observation yet), oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").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) => {
5422
+ if (options.queue) {
5423
+ const { observeQueueCommand: observeQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5424
+ await runAdminObserveQueue(options, observeQueueCommand2);
5425
+ return;
5426
+ }
5413
5427
  const { trackObserveCommand: trackObserveCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
5414
5428
  await runTrackObserve(idOrLogId, options, trackObserveCommand2);
5415
5429
  });
5416
- adminTrack.command("context").description("Gather the field notes for a finding (facts only; observe speaks from them)").argument("[idOrLogId]").option("--query <text>", "Override the fact-search query (else the Worker builds one)").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5430
+ adminTrack.command("context").description("Gather the field notes for a finding (facts only; observe speaks from them)").argument("[idOrLogId]").option("--queue", "Show the context worklist (findings missing field notes), oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--query <text>", "Override the fact-search query (else the Worker builds one)").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
5431
+ if (options.queue) {
5432
+ const { contextQueueCommand: contextQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
5433
+ await runAdminContextQueue(options, contextQueueCommand2);
5434
+ return;
5435
+ }
5417
5436
  const { trackContextCommand: trackContextCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
5418
5437
  await runTrackContext(idOrLogId, options, trackContextCommand2);
5419
5438
  });
@@ -5784,7 +5803,7 @@ async function runTrackSocial(idOrLogId, options, trackSocialShowCommand2, track
5784
5803
  }
5785
5804
  async function runTrackGet(idOrLogId, options, trackGetCommand2) {
5786
5805
  if (!idOrLogId) {
5787
- throw new Error("Missing id. Usage: fluncle track get <track_id|log_id> [--json]");
5806
+ throw new Error("Missing id. Usage: fluncle tracks get <track_id|log_id> [--json]");
5788
5807
  }
5789
5808
  const result = await trackGetCommand2(idOrLogId);
5790
5809
  if (options.json) {
@@ -6224,6 +6243,9 @@ function assertParseArgsCompatibleOptionValues(args) {
6224
6243
  }
6225
6244
  for (let index = 0;index < args.length; index += 1) {
6226
6245
  const arg = args[index];
6246
+ if (arg === undefined) {
6247
+ continue;
6248
+ }
6227
6249
  if (arg === "--") {
6228
6250
  return;
6229
6251
  }
@@ -6253,6 +6275,9 @@ function positionalArgs(args) {
6253
6275
  const positionals = [];
6254
6276
  for (let index = 0;index < args.length; index += 1) {
6255
6277
  const arg = args[index];
6278
+ if (arg === undefined) {
6279
+ continue;
6280
+ }
6256
6281
  if (arg === "--") {
6257
6282
  positionals.push(...args.slice(index + 1));
6258
6283
  return positionals;
@@ -6281,6 +6306,9 @@ function positionalArgs(args) {
6281
6306
  function topLevelCommand(args) {
6282
6307
  for (let index = 0;index < args.length; index += 1) {
6283
6308
  const arg = args[index];
6309
+ if (arg === undefined) {
6310
+ continue;
6311
+ }
6284
6312
  if (arg === "--env") {
6285
6313
  index += 1;
6286
6314
  continue;
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.50.0"
34
+ "version": "0.52.0"
35
35
  }