fluncle 0.52.0 → 0.53.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 +128 -5
  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.52.0".trim() ? "0.52.0".trim() : "0.1.0";
426
+ currentVersion = "0.53.0".trim() ? "0.53.0".trim() : "0.1.0";
427
427
  });
428
428
 
429
429
  // src/update-notifier.ts
@@ -1912,7 +1912,7 @@ function parseVersion2(version) {
1912
1912
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
1913
1913
  var init_version2 = __esm(() => {
1914
1914
  init_output();
1915
- currentVersion2 = "0.52.0".trim() ? "0.52.0".trim() : "0.1.0";
1915
+ currentVersion2 = "0.53.0".trim() ? "0.53.0".trim() : "0.1.0";
1916
1916
  });
1917
1917
 
1918
1918
  // src/commands/track.ts
@@ -2540,6 +2540,62 @@ var init_mixtape_youtube2 = __esm(() => {
2540
2540
  init_output();
2541
2541
  });
2542
2542
 
2543
+ // src/commands/newsletter.ts
2544
+ var exports_newsletter = {};
2545
+ __export(exports_newsletter, {
2546
+ newsletterUpdateCommand: () => newsletterUpdateCommand,
2547
+ newsletterSendCommand: () => newsletterSendCommand,
2548
+ newsletterListCommand: () => newsletterListCommand,
2549
+ newsletterDraftCommand: () => newsletterDraftCommand
2550
+ });
2551
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
2552
+ function buildBody2(options, { requireContent }) {
2553
+ const body = {};
2554
+ if (options.contentFile !== undefined) {
2555
+ body.contentJson = readContentFile(options.contentFile);
2556
+ } else if (requireContent) {
2557
+ throw new CliError2("missing_content", "A draft needs the structured content payload via --content-file <edition.json>");
2558
+ }
2559
+ if (options.subject !== undefined) {
2560
+ body.subject = options.subject;
2561
+ }
2562
+ if (options.windowSince !== undefined) {
2563
+ body.windowSince = options.windowSince;
2564
+ }
2565
+ if (options.windowUntil !== undefined) {
2566
+ body.windowUntil = options.windowUntil;
2567
+ }
2568
+ return body;
2569
+ }
2570
+ function readContentFile(filePath) {
2571
+ if (!existsSync2(filePath)) {
2572
+ throw new CliError2("file_not_found", `Content file not found: ${filePath}`);
2573
+ }
2574
+ const text = readFileSync2(filePath, "utf-8");
2575
+ try {
2576
+ return JSON.parse(text);
2577
+ } catch (error) {
2578
+ throw new CliError2("invalid_content_json", `Content JSON parse failed: ${error instanceof Error ? error.message : String(error)}`);
2579
+ }
2580
+ }
2581
+ async function newsletterDraftCommand(options) {
2582
+ return adminApiPost("/api/admin/newsletter/editions", buildBody2(options, { requireContent: true }));
2583
+ }
2584
+ async function newsletterUpdateCommand(id, options) {
2585
+ return adminApiPatch(`/api/admin/newsletter/editions/${encodeURIComponent(id)}`, buildBody2(options, { requireContent: false }));
2586
+ }
2587
+ async function newsletterSendCommand(id) {
2588
+ return adminApiPost(`/api/admin/newsletter/editions/${encodeURIComponent(id)}/send`);
2589
+ }
2590
+ async function newsletterListCommand() {
2591
+ const response = await adminApiGet("/api/admin/newsletter/editions");
2592
+ return response.editions;
2593
+ }
2594
+ var init_newsletter = __esm(() => {
2595
+ init_api();
2596
+ init_output();
2597
+ });
2598
+
2543
2599
  // src/commands/add.ts
2544
2600
  async function addCommand2(spotifyUrl, options) {
2545
2601
  const result = await adminApiPost("/api/admin/tracks", {
@@ -3149,7 +3205,7 @@ var COORD_FALLBACK2 = "—";
3149
3205
  var init_format2 = () => {};
3150
3206
 
3151
3207
  // src/cli.ts
3152
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3208
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
3153
3209
  import path2 from "path";
3154
3210
 
3155
3211
  // ../../node_modules/commander/lib/error.js
@@ -5476,6 +5532,26 @@ function addAdminCommands(program2) {
5476
5532
  const { publishYoutubeCommand: publishYoutubeCommand3 } = await Promise.resolve().then(() => (init_mixtape_youtube2(), exports_mixtape_youtube2));
5477
5533
  await runMixtapePublishYoutube(idOrLogId, options, publishYoutubeCommand3);
5478
5534
  });
5535
+ const adminNewsletter = configureCommand(admin.command("newsletter").description("Newsletter edition commands"));
5536
+ adminNewsletter.action(() => {
5537
+ adminNewsletter.outputHelp();
5538
+ });
5539
+ adminNewsletter.command("draft").description("Persist a newsletter edition draft (the agent authors it, you send it)").option("--content-file <file>", "Structured edition content payload (JSON)").option("--subject <text>", "Email subject line").option("--window-since <date>", "Discovery-window start (ISO)").option("--window-until <date>", "Discovery-window end (ISO)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
5540
+ const { newsletterDraftCommand: newsletterDraftCommand2 } = await Promise.resolve().then(() => (init_newsletter(), exports_newsletter));
5541
+ await runNewsletterDraft(options, newsletterDraftCommand2);
5542
+ });
5543
+ adminNewsletter.command("update").description("Update a draft edition's payload, subject, or window").argument("[id]").option("--content-file <file>", "Structured edition content payload (JSON)").option("--subject <text>", "Email subject line").option("--window-since <date>", "Discovery-window start (ISO)").option("--window-until <date>", "Discovery-window end (ISO)").option("--json", "Print JSON", false).allowExcessArguments().action(async (id, options) => {
5544
+ const { newsletterUpdateCommand: newsletterUpdateCommand2 } = await Promise.resolve().then(() => (init_newsletter(), exports_newsletter));
5545
+ await runNewsletterUpdate(id, options, newsletterUpdateCommand2);
5546
+ });
5547
+ adminNewsletter.command("send").description("Send an edition \u2014 OPERATOR only (Resend broadcast + mint the number)").argument("[id]").option("--json", "Print JSON", false).allowExcessArguments().action(async (id, options) => {
5548
+ const { newsletterSendCommand: newsletterSendCommand2 } = await Promise.resolve().then(() => (init_newsletter(), exports_newsletter));
5549
+ await runNewsletterSend(id, options, newsletterSendCommand2);
5550
+ });
5551
+ adminNewsletter.command("list").description("List every edition including drafts (the cron's miss-recovery read)").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
5552
+ const { newsletterListCommand: newsletterListCommand2 } = await Promise.resolve().then(() => (init_newsletter(), exports_newsletter));
5553
+ await runNewsletterList(options, newsletterListCommand2);
5554
+ });
5479
5555
  const submissions = configureCommand(admin.command("submissions").description("Review listener submissions"));
5480
5556
  submissions.option("--json", "Print JSON", false).action(async (options) => {
5481
5557
  const { listSubmissionsCommand: listSubmissionsCommand2 } = await Promise.resolve().then(() => (init_submissions(), exports_submissions));
@@ -5555,7 +5631,7 @@ async function runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCo
5555
5631
  console.log(` mime: ${result.mime}`);
5556
5632
  }
5557
5633
  async function runTrackObserve(idOrLogId, options, trackObserveCommand2) {
5558
- const script = options.scriptFile ? readFileSync2(options.scriptFile, "utf8") : options.script;
5634
+ const script = options.scriptFile ? readFileSync3(options.scriptFile, "utf8") : options.script;
5559
5635
  if (!idOrLogId || !script || !script.trim()) {
5560
5636
  throw new Error("Usage: fluncle admin tracks observe <track_id|log_id> (--script <text> | --script-file <file>) [--voice-id <id>] [--model <model>] [--duration-ms <ms>] [--context-note <text>] [--json]");
5561
5637
  }
@@ -5717,7 +5793,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
5717
5793
  return;
5718
5794
  }
5719
5795
  const candidate = path2.join(dir, name);
5720
- return existsSync2(candidate) ? candidate : undefined;
5796
+ return existsSync3(candidate) ? candidate : undefined;
5721
5797
  };
5722
5798
  const resolveFile = (explicit, name) => {
5723
5799
  if (explicit) {
@@ -5983,6 +6059,53 @@ function formatCue(ms) {
5983
6059
  }
5984
6060
  return `${minutes}:${seconds.toString().padStart(2, "0")}`;
5985
6061
  }
6062
+ async function runNewsletterDraft(options, newsletterDraftCommand2) {
6063
+ const result = await newsletterDraftCommand2(options);
6064
+ if (options.json) {
6065
+ printJson(result);
6066
+ return;
6067
+ }
6068
+ console.log(`Drafted edition ${result.edition.id}. It stays a draft until the operator sends it.`);
6069
+ }
6070
+ async function runNewsletterUpdate(id, options, newsletterUpdateCommand2) {
6071
+ if (!id) {
6072
+ throw new Error("Missing edition id. Usage: fluncle admin newsletter update <id>");
6073
+ }
6074
+ const result = await newsletterUpdateCommand2(id, options);
6075
+ if (options.json) {
6076
+ printJson(result);
6077
+ return;
6078
+ }
6079
+ console.log(`Saved draft ${result.edition.id}.`);
6080
+ }
6081
+ async function runNewsletterSend(id, options, newsletterSendCommand2) {
6082
+ if (!id) {
6083
+ throw new Error("Missing edition id. Usage: fluncle admin newsletter send <id>");
6084
+ }
6085
+ const result = await newsletterSendCommand2(id);
6086
+ if (options.json) {
6087
+ printJson(result);
6088
+ return;
6089
+ }
6090
+ const number = result.edition.number;
6091
+ console.log(number === undefined ? `Sent edition ${result.edition.id}.` : `Sent edition #${number} \u2014 it's out to the list and in the archive.`);
6092
+ }
6093
+ async function runNewsletterList(options, newsletterListCommand2) {
6094
+ const editions = await newsletterListCommand2();
6095
+ if (options.json) {
6096
+ printJson({ editions, ok: true });
6097
+ return;
6098
+ }
6099
+ if (editions.length === 0) {
6100
+ console.log("No editions yet.");
6101
+ return;
6102
+ }
6103
+ for (const edition of editions) {
6104
+ const label = edition.number === undefined ? "draft" : `#${edition.number}`;
6105
+ const subject = edition.subject ?? "(no subject)";
6106
+ console.log(`${label} ${edition.status} ${edition.id} ${subject}`);
6107
+ }
6108
+ }
5986
6109
  async function runAdd(spotifyUrl, options, addCommand3) {
5987
6110
  if (!spotifyUrl) {
5988
6111
  throw new Error("Missing Spotify track URL");
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.52.0"
34
+ "version": "0.53.0"
35
35
  }