fluncle 0.114.0 → 0.115.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 +137 -3
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -557,7 +557,7 @@ function parseVersion(version) {
557
557
  var currentVersion;
558
558
  var init_version = __esm(() => {
559
559
  init_output();
560
- currentVersion = "0.114.0".trim() ? "0.114.0".trim() : "0.1.0";
560
+ currentVersion = "0.115.0".trim() ? "0.115.0".trim() : "0.1.0";
561
561
  });
562
562
 
563
563
  // src/update-notifier.ts
@@ -1042,6 +1042,48 @@ var init_artists = __esm(() => {
1042
1042
  init_api();
1043
1043
  });
1044
1044
 
1045
+ // src/commands/galaxies.ts
1046
+ var exports_galaxies = {};
1047
+ __export(exports_galaxies, {
1048
+ galaxyMapWriteCommand: () => galaxyMapWriteCommand,
1049
+ galaxyMapReadCommand: () => galaxyMapReadCommand,
1050
+ galaxyGetCommand: () => galaxyGetCommand,
1051
+ galaxyEmbeddingsCommand: () => galaxyEmbeddingsCommand,
1052
+ galaxiesListCommand: () => galaxiesListCommand
1053
+ });
1054
+ async function galaxiesListCommand() {
1055
+ const response = await publicApiGet("/api/v1/galaxies");
1056
+ return response.galaxies;
1057
+ }
1058
+ async function galaxyGetCommand(slug) {
1059
+ const response = await publicApiGet(`/api/v1/galaxies/${encodeURIComponent(slug)}`);
1060
+ return { findings: response.findings, galaxy: response.galaxy };
1061
+ }
1062
+ async function galaxyMapReadCommand() {
1063
+ const response = await adminApiGet("/api/admin/galaxies");
1064
+ return response.galaxies;
1065
+ }
1066
+ async function galaxyEmbeddingsCommand(options) {
1067
+ const params = new URLSearchParams;
1068
+ if (options.cursor) {
1069
+ params.set("cursor", options.cursor);
1070
+ }
1071
+ if (options.limit) {
1072
+ params.set("limit", options.limit);
1073
+ }
1074
+ const query = params.toString();
1075
+ return adminApiGet(`/api/admin/tracks/embeddings${query ? `?${query}` : ""}`);
1076
+ }
1077
+ async function galaxyMapWriteCommand(clusters) {
1078
+ const response = await adminApiPut("/api/admin/galaxies/map", {
1079
+ clusters
1080
+ });
1081
+ return response.galaxies;
1082
+ }
1083
+ var init_galaxies = __esm(() => {
1084
+ init_api();
1085
+ });
1086
+
1045
1087
  // src/commands/mixtape-api.ts
1046
1088
  async function mixtapeListCommand() {
1047
1089
  const response = await adminApiGet("/api/admin/mixtapes");
@@ -2209,7 +2251,7 @@ function parseVersion2(version) {
2209
2251
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2210
2252
  var init_version2 = __esm(() => {
2211
2253
  init_output();
2212
- currentVersion2 = "0.114.0".trim() ? "0.114.0".trim() : "0.1.0";
2254
+ currentVersion2 = "0.115.0".trim() ? "0.115.0".trim() : "0.1.0";
2213
2255
  });
2214
2256
 
2215
2257
  // ../../packages/registry/src/index.ts
@@ -3270,6 +3312,9 @@ async function trackUpdateCommand(trackId, options) {
3270
3312
  if (options.embedding !== undefined) {
3271
3313
  body.embedding = options.embedding;
3272
3314
  }
3315
+ if (options.galaxyId !== undefined) {
3316
+ body.galaxyId = options.galaxyId;
3317
+ }
3273
3318
  if (options.note !== undefined) {
3274
3319
  body.note = options.note;
3275
3320
  }
@@ -3462,6 +3507,9 @@ async function trackUpdateCommand2(trackId, options) {
3462
3507
  if (options.embedding !== undefined) {
3463
3508
  body.embedding = options.embedding;
3464
3509
  }
3510
+ if (options.galaxyId !== undefined) {
3511
+ body.galaxyId = options.galaxyId;
3512
+ }
3465
3513
  if (options.note !== undefined) {
3466
3514
  body.note = options.note;
3467
3515
  }
@@ -7768,6 +7816,10 @@ function addListenCommands(program2) {
7768
7816
  const { artistsListCommand: artistsListCommand2, artistsGetCommand: artistsGetCommand2 } = await Promise.resolve().then(() => (init_artists(), exports_artists));
7769
7817
  await runArtists(slug, options, { artistsGetCommand: artistsGetCommand2, artistsListCommand: artistsListCommand2 });
7770
7818
  });
7819
+ program2.command("galaxies").description("Wander Fluncle's sonic galaxies").argument("[slug]", "Galaxy slug (omit for the full map)").option("--json", "Print JSON", false).action(async (slug, options) => {
7820
+ const { galaxiesListCommand: galaxiesListCommand2, galaxyGetCommand: galaxyGetCommand2 } = await Promise.resolve().then(() => (init_galaxies(), exports_galaxies));
7821
+ await runGalaxies(slug, options, { galaxiesListCommand: galaxiesListCommand2, galaxyGetCommand: galaxyGetCommand2 });
7822
+ });
7771
7823
  program2.command("mixtapes").description("Fluncle's checkpoint sets").option("--json", "Print JSON", false).action(async (options) => {
7772
7824
  const { mixtapesCommand: mixtapesCommand2 } = await Promise.resolve().then(() => (init_mixtapes(), exports_mixtapes));
7773
7825
  await runMixtapes(options, mixtapesCommand2);
@@ -7901,7 +7953,7 @@ function addAdminCommands(program2) {
7901
7953
  const { mixableOrderCommand: mixableOrderCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
7902
7954
  await runMixableOrder(logIds, options, mixableOrderCommand2);
7903
7955
  });
7904
- adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--analyzed-at <iso>", "Analysis-write ISO timestamp (BPM/key provenance)").option("--analyzed-from <class>", "Audio class BPM/key were analyzed from: full or preview").option("--bpm <number>", "Track BPM").option("--bpm-confidence <number>", "Analyzer confidence in the BPM (0..1)").option("--bpm-source <source>", "Where the BPM came from (analyzer bpmSource)").option("--embedding <json>", "MuQ audio embedding as a JSON array of 1024 floats").option("--embedding-file <file>", "Read the MuQ embedding JSON array from a file").option("--features <json>", "Audio feature JSON").option("--json", "Print JSON", false).option("--key <key>", "Musical key").option("--key-confidence <number>", "Analyzer confidence in the key (0..1)").option("--key-source <source>", "Where the key came from (analyzer keySource)").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
7956
+ adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--analyzed-at <iso>", "Analysis-write ISO timestamp (BPM/key provenance)").option("--analyzed-from <class>", "Audio class BPM/key were analyzed from: full or preview").option("--bpm <number>", "Track BPM").option("--bpm-confidence <number>", "Analyzer confidence in the BPM (0..1)").option("--bpm-source <source>", "Where the BPM came from (analyzer bpmSource)").option("--embedding <json>", "MuQ audio embedding as a JSON array of 1024 floats").option("--embedding-file <file>", "Read the MuQ embedding JSON array from a file").option("--features <json>", "Audio feature JSON").option("--galaxy-id <id>", "Sonic galaxy assignment (empty string clears it)").option("--json", "Print JSON", false).option("--key <key>", "Musical key").option("--key-confidence <number>", "Analyzer confidence in the key (0..1)").option("--key-source <source>", "Where the key came from (analyzer keySource)").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
7905
7957
  const { trackUpdateCommand: trackUpdateCommand3 } = await Promise.resolve().then(() => (init_track(), exports_track));
7906
7958
  await runTrackUpdate(trackId, options, trackUpdateCommand3);
7907
7959
  });
@@ -8143,6 +8195,22 @@ function addAdminCommands(program2) {
8143
8195
  artists.action(() => {
8144
8196
  artists.outputHelp();
8145
8197
  });
8198
+ const galaxies = configureCommand(admin.command("galaxies").description("Sonic galaxy map (cluster sweep) commands"));
8199
+ galaxies.action(() => {
8200
+ galaxies.outputHelp();
8201
+ });
8202
+ galaxies.command("map").description("Read the full galaxy map (named + unnamed + retired, with centroids)").option("--json", "Print JSON", false).action(async (options) => {
8203
+ const { galaxyMapReadCommand: galaxyMapReadCommand2 } = await Promise.resolve().then(() => (init_galaxies(), exports_galaxies));
8204
+ await runGalaxyMapRead(options, galaxyMapReadCommand2);
8205
+ });
8206
+ galaxies.command("embeddings").description("Read a cursor page of the embedded corpus (the cluster engine's input)").option("--cursor <cursor>", "Opaque cursor from a prior page's nextCursor").option("--limit <limit>", "Page size (default 200, clamped to 500)").option("--json", "Print JSON", false).action(async (options) => {
8207
+ const { galaxyEmbeddingsCommand: galaxyEmbeddingsCommand2 } = await Promise.resolve().then(() => (init_galaxies(), exports_galaxies));
8208
+ await runGalaxyEmbeddings(options, galaxyEmbeddingsCommand2);
8209
+ });
8210
+ galaxies.command("set-map").description("Write the galaxy map transactionally (mint new ids, upsert centroids, retire)").requiredOption("--file <file>", "Path to the clusters JSON ({ clusters: [...] } or a bare array)").option("--json", "Print JSON", false).action(async (options) => {
8211
+ const { galaxyMapWriteCommand: galaxyMapWriteCommand2 } = await Promise.resolve().then(() => (init_galaxies(), exports_galaxies));
8212
+ await runGalaxyMapWrite(options, galaxyMapWriteCommand2);
8213
+ });
8146
8214
  const backfill = configureCommand(admin.command("backfills").description("Backfill operator-only archives"));
8147
8215
  backfill.action(() => {
8148
8216
  backfill.outputHelp();
@@ -8792,6 +8860,7 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand3) {
8792
8860
  bpmSource: options.bpmSource,
8793
8861
  embedding,
8794
8862
  features: options.features,
8863
+ galaxyId: options.galaxyId,
8795
8864
  key: options.key,
8796
8865
  keyConfidence,
8797
8866
  keySource: options.keySource,
@@ -9155,6 +9224,71 @@ async function runArtists(slug, options, commands) {
9155
9224
  console.log(`${a.name.padEnd(40)} ${String(a.findingCount).padStart(3)} finding${a.findingCount === 1 ? "" : "s"}`);
9156
9225
  }
9157
9226
  }
9227
+ async function runGalaxies(slug, options, commands) {
9228
+ if (slug) {
9229
+ const { findings, galaxy } = await commands.galaxyGetCommand(slug);
9230
+ if (options.json) {
9231
+ printJson({ findings, galaxy, ok: true });
9232
+ return;
9233
+ }
9234
+ console.log(`${galaxy.name} (${galaxy.slug})`);
9235
+ console.log(`${galaxy.memberCount} finding${galaxy.memberCount === 1 ? "" : "s"}`);
9236
+ for (const finding of findings) {
9237
+ const coordinate3 = finding.logId ? `${finding.logId} ` : "";
9238
+ console.log(` ${coordinate3}${finding.artists.join(", ")} \u2014 ${finding.title}`);
9239
+ }
9240
+ return;
9241
+ }
9242
+ const galaxies = await commands.galaxiesListCommand();
9243
+ if (options.json) {
9244
+ printJson({ galaxies, ok: true });
9245
+ return;
9246
+ }
9247
+ if (galaxies.length === 0) {
9248
+ console.log("No galaxies named yet. Quiet map tonight.");
9249
+ return;
9250
+ }
9251
+ for (const galaxy of galaxies) {
9252
+ console.log(`${galaxy.name.padEnd(40)} ${String(galaxy.memberCount).padStart(3)} finding${galaxy.memberCount === 1 ? "" : "s"}`);
9253
+ }
9254
+ }
9255
+ async function runGalaxyMapRead(options, galaxyMapReadCommand2) {
9256
+ const galaxies = await galaxyMapReadCommand2();
9257
+ if (options.json) {
9258
+ printJson({ galaxies, ok: true });
9259
+ return;
9260
+ }
9261
+ if (galaxies.length === 0) {
9262
+ console.log("No galaxies on the map yet.");
9263
+ return;
9264
+ }
9265
+ for (const galaxy of galaxies) {
9266
+ const name = galaxy.name ?? "(unnamed)";
9267
+ const retired = galaxy.retiredAt ? " [retired]" : "";
9268
+ console.log(`${galaxy.id} ${galaxy.handle.padEnd(30)} ${name.padEnd(24)} ${String(galaxy.memberCount).padStart(4)} members${retired}`);
9269
+ }
9270
+ }
9271
+ async function runGalaxyEmbeddings(options, galaxyEmbeddingsCommand2) {
9272
+ const result = await galaxyEmbeddingsCommand2({ cursor: options.cursor, limit: options.limit });
9273
+ if (options.json) {
9274
+ printJson(result);
9275
+ return;
9276
+ }
9277
+ console.log(`${result.embeddings.length} embedding(s)${result.nextCursor ? ` \xB7 nextCursor: ${result.nextCursor}` : " \xB7 end of corpus"}`);
9278
+ }
9279
+ async function runGalaxyMapWrite(options, galaxyMapWriteCommand2) {
9280
+ const raw = JSON.parse(readFileSync6(options.file, "utf8"));
9281
+ const clusters = Array.isArray(raw) || raw === null ? raw : raw.clusters;
9282
+ if (!Array.isArray(clusters)) {
9283
+ throw new Error("Invalid --file: expected { clusters: [...] } or a JSON array of cluster rows");
9284
+ }
9285
+ const galaxies = await galaxyMapWriteCommand2(clusters);
9286
+ if (options.json) {
9287
+ printJson({ galaxies, ok: true });
9288
+ return;
9289
+ }
9290
+ console.log(`Wrote ${clusters.length} cluster(s); the map now holds ${galaxies.length} galaxy(ies).`);
9291
+ }
9158
9292
  async function runMixtapes(options, mixtapesCommand2) {
9159
9293
  const mixtapes = await mixtapesCommand2();
9160
9294
  if (options.json) {
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.114.0"
34
+ "version": "0.115.0"
35
35
  }