fluncle 0.51.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.
- package/bin/fluncle.mjs +153 -27
- 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.
|
|
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.
|
|
1915
|
+
currentVersion2 = "0.53.0".trim() ? "0.53.0".trim() : "0.1.0";
|
|
1916
1916
|
});
|
|
1917
1917
|
|
|
1918
1918
|
// src/commands/track.ts
|
|
@@ -2200,7 +2200,7 @@ async function previewArchiveUploadCommand(idOrLogId, options) {
|
|
|
2200
2200
|
form.append("preview", Bun.file(options.file), basename(options.file));
|
|
2201
2201
|
form.append("source", options.source);
|
|
2202
2202
|
form.append("mime", options.mime);
|
|
2203
|
-
return adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/preview
|
|
2203
|
+
return adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/preview`, form);
|
|
2204
2204
|
}
|
|
2205
2205
|
async function previewArchiveBackfillCommand(options) {
|
|
2206
2206
|
const result = {
|
|
@@ -2220,7 +2220,7 @@ async function previewArchiveBackfillCommand(options) {
|
|
|
2220
2220
|
result.skipped.push({ reason: "no_log_id", trackId: track.trackId });
|
|
2221
2221
|
continue;
|
|
2222
2222
|
}
|
|
2223
|
-
const status = await adminApiGet(`/api/admin/tracks/${encodeURIComponent(track.trackId)}/preview
|
|
2223
|
+
const status = await adminApiGet(`/api/admin/tracks/${encodeURIComponent(track.trackId)}/preview`);
|
|
2224
2224
|
if (status.archived) {
|
|
2225
2225
|
result.skipped.push({ reason: "already_archived", trackId: track.trackId });
|
|
2226
2226
|
continue;
|
|
@@ -2259,7 +2259,7 @@ async function uploadResolvedPreview(trackId, preview) {
|
|
|
2259
2259
|
form.append("preview", new Blob([preview.bytes], { type: preview.mime }), `preview.${extension}`);
|
|
2260
2260
|
form.append("source", preview.source);
|
|
2261
2261
|
form.append("mime", preview.mime);
|
|
2262
|
-
await adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(trackId)}/preview
|
|
2262
|
+
await adminApiPostForm(`/api/admin/tracks/${encodeURIComponent(trackId)}/preview`, form);
|
|
2263
2263
|
}
|
|
2264
2264
|
async function resolvePreview(track) {
|
|
2265
2265
|
const stored = await downloadPreview(track.previewUrl, "deezer:stored");
|
|
@@ -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
|
|
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
|
|
@@ -5346,8 +5402,8 @@ function addMetaCommands(program2) {
|
|
|
5346
5402
|
});
|
|
5347
5403
|
}
|
|
5348
5404
|
function addTrackCommands(program2) {
|
|
5349
|
-
const
|
|
5350
|
-
|
|
5405
|
+
const tracks = configureCommand(program2.command("tracks", { hidden: true }).alias("track").description("Public track lookups"));
|
|
5406
|
+
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) => {
|
|
5351
5407
|
const { trackGetCommand: trackGetCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
5352
5408
|
await runTrackGet(idOrLogId, options, trackGetCommand2);
|
|
5353
5409
|
});
|
|
@@ -5380,19 +5436,12 @@ function addAdminCommands(program2) {
|
|
|
5380
5436
|
const { queueCommand: queueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
5381
5437
|
await runAdminQueue(options, queueCommand2);
|
|
5382
5438
|
});
|
|
5383
|
-
adminTracks.command("
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
await runAdminObserveQueue(options, observeQueueCommand2);
|
|
5390
|
-
});
|
|
5391
|
-
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) => {
|
|
5392
|
-
const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
5393
|
-
await runAdminEnrichQueue(options, enrichQueueCommand2);
|
|
5394
|
-
});
|
|
5395
|
-
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) => {
|
|
5439
|
+
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) => {
|
|
5440
|
+
if (!options.queue) {
|
|
5441
|
+
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.");
|
|
5442
|
+
process.exitCode = 1;
|
|
5443
|
+
return;
|
|
5444
|
+
}
|
|
5396
5445
|
const { enrichQueueCommand: enrichQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
5397
5446
|
await runAdminEnrichQueue(options, enrichQueueCommand2);
|
|
5398
5447
|
});
|
|
@@ -5421,15 +5470,25 @@ function addAdminCommands(program2) {
|
|
|
5421
5470
|
const { trackSocialShowCommand: trackSocialShowCommand2, trackSocialUpdateCommand: trackSocialUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
5422
5471
|
await runTrackSocial(idOrLogId, options, trackSocialShowCommand2, trackSocialUpdateCommand2);
|
|
5423
5472
|
});
|
|
5424
|
-
adminTrack.command("preview").
|
|
5473
|
+
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) => {
|
|
5425
5474
|
const { previewArchiveUploadCommand: previewArchiveUploadCommand2 } = await Promise.resolve().then(() => (init_preview_archive(), exports_preview_archive));
|
|
5426
5475
|
await runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCommand2);
|
|
5427
5476
|
});
|
|
5428
|
-
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) => {
|
|
5477
|
+
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) => {
|
|
5478
|
+
if (options.queue) {
|
|
5479
|
+
const { observeQueueCommand: observeQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
5480
|
+
await runAdminObserveQueue(options, observeQueueCommand2);
|
|
5481
|
+
return;
|
|
5482
|
+
}
|
|
5429
5483
|
const { trackObserveCommand: trackObserveCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
5430
5484
|
await runTrackObserve(idOrLogId, options, trackObserveCommand2);
|
|
5431
5485
|
});
|
|
5432
|
-
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) => {
|
|
5486
|
+
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) => {
|
|
5487
|
+
if (options.queue) {
|
|
5488
|
+
const { contextQueueCommand: contextQueueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
5489
|
+
await runAdminContextQueue(options, contextQueueCommand2);
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5433
5492
|
const { trackContextCommand: trackContextCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
5434
5493
|
await runTrackContext(idOrLogId, options, trackContextCommand2);
|
|
5435
5494
|
});
|
|
@@ -5473,6 +5532,26 @@ function addAdminCommands(program2) {
|
|
|
5473
5532
|
const { publishYoutubeCommand: publishYoutubeCommand3 } = await Promise.resolve().then(() => (init_mixtape_youtube2(), exports_mixtape_youtube2));
|
|
5474
5533
|
await runMixtapePublishYoutube(idOrLogId, options, publishYoutubeCommand3);
|
|
5475
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
|
+
});
|
|
5476
5555
|
const submissions = configureCommand(admin.command("submissions").description("Review listener submissions"));
|
|
5477
5556
|
submissions.option("--json", "Print JSON", false).action(async (options) => {
|
|
5478
5557
|
const { listSubmissionsCommand: listSubmissionsCommand2 } = await Promise.resolve().then(() => (init_submissions(), exports_submissions));
|
|
@@ -5552,7 +5631,7 @@ async function runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCo
|
|
|
5552
5631
|
console.log(` mime: ${result.mime}`);
|
|
5553
5632
|
}
|
|
5554
5633
|
async function runTrackObserve(idOrLogId, options, trackObserveCommand2) {
|
|
5555
|
-
const script = options.scriptFile ?
|
|
5634
|
+
const script = options.scriptFile ? readFileSync3(options.scriptFile, "utf8") : options.script;
|
|
5556
5635
|
if (!idOrLogId || !script || !script.trim()) {
|
|
5557
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]");
|
|
5558
5637
|
}
|
|
@@ -5714,7 +5793,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
5714
5793
|
return;
|
|
5715
5794
|
}
|
|
5716
5795
|
const candidate = path2.join(dir, name);
|
|
5717
|
-
return
|
|
5796
|
+
return existsSync3(candidate) ? candidate : undefined;
|
|
5718
5797
|
};
|
|
5719
5798
|
const resolveFile = (explicit, name) => {
|
|
5720
5799
|
if (explicit) {
|
|
@@ -5800,7 +5879,7 @@ async function runTrackSocial(idOrLogId, options, trackSocialShowCommand2, track
|
|
|
5800
5879
|
}
|
|
5801
5880
|
async function runTrackGet(idOrLogId, options, trackGetCommand2) {
|
|
5802
5881
|
if (!idOrLogId) {
|
|
5803
|
-
throw new Error("Missing id. Usage: fluncle
|
|
5882
|
+
throw new Error("Missing id. Usage: fluncle tracks get <track_id|log_id> [--json]");
|
|
5804
5883
|
}
|
|
5805
5884
|
const result = await trackGetCommand2(idOrLogId);
|
|
5806
5885
|
if (options.json) {
|
|
@@ -5980,6 +6059,53 @@ function formatCue(ms) {
|
|
|
5980
6059
|
}
|
|
5981
6060
|
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
|
5982
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
|
+
}
|
|
5983
6109
|
async function runAdd(spotifyUrl, options, addCommand3) {
|
|
5984
6110
|
if (!spotifyUrl) {
|
|
5985
6111
|
throw new Error("Missing Spotify track URL");
|
package/package.json
CHANGED