fluncle 0.265.0 → 0.267.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 +87 -8
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -386,12 +386,16 @@ __export(exports_env, {
386
386
  loadEnv: () => loadEnv,
387
387
  readOptionalEnv: () => readOptionalEnv
388
388
  });
389
+ import { execFileSync } from "node:child_process";
389
390
  import { homedir } from "node:os";
390
391
  import { join } from "node:path";
391
392
  function loadEnv(keys) {
392
393
  loadConfig();
393
394
  const result = {};
394
395
  const missing = [];
396
+ if (keys.includes("FLUNCLE_API_TOKEN")) {
397
+ resolveTokenRef();
398
+ }
395
399
  for (const key of keys) {
396
400
  const value = process.env[key];
397
401
  if (value === undefined) {
@@ -413,6 +417,21 @@ function getApiBaseUrl() {
413
417
  loadConfig();
414
418
  return (process.env.FLUNCLE_API_BASE_URL ?? "https://www.fluncle.com").replace(/\/+$/, "");
415
419
  }
420
+ function resolveTokenRef() {
421
+ const ref = process.env.FLUNCLE_API_TOKEN_REF;
422
+ if (process.env.FLUNCLE_API_TOKEN !== undefined || !ref || false) {
423
+ return;
424
+ }
425
+ try {
426
+ const secretRef = ref.startsWith("op://") ? ref : `op://${ref}`;
427
+ process.env.FLUNCLE_API_TOKEN = execFileSync("op", ["read", "--no-newline", secretRef], {
428
+ encoding: "utf8",
429
+ stdio: ["ignore", "pipe", "pipe"]
430
+ });
431
+ } catch {
432
+ throw new Error(`Could not read FLUNCLE_API_TOKEN_REF (${ref}) with op.`);
433
+ }
434
+ }
416
435
  function loadConfig() {
417
436
  const profile = getEnvProfile();
418
437
  if (loadedProfile === profile) {
@@ -632,7 +651,7 @@ function parseVersion(version) {
632
651
  var currentVersion;
633
652
  var init_version = __esm(() => {
634
653
  init_output();
635
- currentVersion = "0.265.0".trim() ? "0.265.0".trim() : "0.1.0";
654
+ currentVersion = "0.267.0".trim() ? "0.267.0".trim() : "0.1.0";
636
655
  });
637
656
 
638
657
  // src/update-notifier.ts
@@ -2345,7 +2364,7 @@ function parseVersion2(version) {
2345
2364
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2346
2365
  var init_version2 = __esm(() => {
2347
2366
  init_output();
2348
- currentVersion2 = "0.265.0".trim() ? "0.265.0".trim() : "0.1.0";
2367
+ currentVersion2 = "0.267.0".trim() ? "0.267.0".trim() : "0.1.0";
2349
2368
  });
2350
2369
 
2351
2370
  // ../../packages/registry/src/index.ts
@@ -3725,13 +3744,15 @@ var init_src = __esm(() => {
3725
3744
  },
3726
3745
  {
3727
3746
  command: "fluncle admin labels list --seed-state undecided",
3728
- exposedContent: ["read the undecided crawl-seed pile and report whether a triage round is due"],
3747
+ exposedContent: [
3748
+ "read the undecided crawl-seed pile and, once a round is due, research it into proposals"
3749
+ ],
3729
3750
  kind: "cron",
3730
3751
  name: "cron.label-triage",
3731
- operatorNotes: "daily. A PURE trigger — zero model tokens, one countless admin read and a sort. Fires only when 40+ NEVER-LOOKED labels have accumulated; stale ones ride along but never trigger a round. It cannot rule: recording a finding is record_label_triage (agent tier) and ruling is update_label (operator tier), which 403s the box token. The batched research leg is deliberately unwired until the gate proves it reports honestly. Source: docs/agents/hermes/scripts/{label-triage-sweep.ts,label-triage-sweep.sh}.",
3752
+ operatorNotes: "daily. The gate is one countless admin read and a sort; it fires only when 40+ NEVER-LOOKED labels with a MusicBrainz identity and no artist rules have accumulated, and stale ones ride along. A fired round runs one claude -p per batch of 10 labels (capped at 3 batches and 30 labels) whose only fetcher is `fluncle admin labels evidence`; the model holds no Fluncle token, and the script records each verdict with record_label_triage (agent tier). It cannot rule: update_label is operator tier and 403s the box token. Labels a round left unfinished (a failed batch, or past the cap) are carried and fire the next day. The model runs --restricted with dontAsk permissions, confined to its batch directory and the skill. A fired round that records zero proposals, or any failed batch, exits non-zero, reads failed on /status and posts to Discord. Source: docs/agents/hermes/scripts/{label-triage-sweep.ts,label-triage-sweep.sh}.",
3732
3753
  probeConfig: { cadenceMs: 24 * 60 * MINUTE_MS, cronName: "fluncle-label-triage", kind: "cron" },
3733
- statusDescription: "reports when the label pile needs a round",
3734
- title: "Label triage gate",
3754
+ statusDescription: "researches the label pile when it needs a round",
3755
+ title: "Label triage",
3735
3756
  weights: { status: "hidden" }
3736
3757
  },
3737
3758
  {
@@ -11190,7 +11211,7 @@ var init_admin_albums = __esm(() => {
11190
11211
  });
11191
11212
 
11192
11213
  // ../../packages/contracts/src/orpc/admin-artifacts.ts
11193
- var ARTIFACT_CHANGE_API_MAX_LIMIT = 500, ARTIFACT_SNAPSHOT_API_MAX_LIMIT = 200, ARTIFACT_COMPACTION_API_MAX_LIMIT = 1000, ARTIFACT_SUPPORTED_CONTRACTS, ArtifactStreamSchema, ArtifactContractSchema, ConsumerIdSchema, DigestSchema, SequenceSchema, ArtifactRebuildCheckpointSchema, ArtifactConsumerStatusSchema, ArtifactChangeEventSchema, ArtifactSnapshotItemSchema, ArtifactSnapshotPageSchema, ArtifactChangePageSchema, ArtifactCompactionResultSchema, registerArtifactConsumer, getArtifactConsumer, listArtifactSnapshot, checkpointArtifactRebuild, activateArtifactConsumer, listArtifactChanges, acknowledgeArtifactChanges, inactivateArtifactConsumer, compactArtifactChanges, adminArtifactsContract;
11214
+ var ARTIFACT_CHANGE_API_MAX_LIMIT = 500, ARTIFACT_SNAPSHOT_API_MAX_LIMIT = 200, ARTIFACT_COMPACTION_API_MAX_LIMIT = 1000, ARTIFACT_SUPPORTED_CONTRACTS, ArtifactStreamSchema, ArtifactContractSchema, ConsumerIdSchema, DigestSchema, SequenceSchema, ArtifactRebuildCheckpointSchema, ArtifactConsumerStatusSchema, ArtifactChangeEventSchema, ArtifactSnapshotItemSchema, ArtifactSnapshotPageSchema, ArtifactChangePageSchema, ArtifactCompactionResultSchema, SonarSourceAfterSchema, SonarSourceLimitSchema, SonarSourceIdsSchema, listSonarTrackDigests, listSonarTracks, listSonarCentroidDigests, listSonarCentroids, registerArtifactConsumer, getArtifactConsumer, listArtifactSnapshot, checkpointArtifactRebuild, activateArtifactConsumer, listArtifactChanges, acknowledgeArtifactChanges, inactivateArtifactConsumer, compactArtifactChanges, adminArtifactsContract;
11194
11215
  var init_admin_artifacts = __esm(() => {
11195
11216
  init_dist3();
11196
11217
  init_zod();
@@ -11304,6 +11325,57 @@ var init_admin_artifacts = __esm(() => {
11304
11325
  ok: literal(true),
11305
11326
  reason: _enum(["compacted", "empty", "no_safe_barrier"])
11306
11327
  });
11328
+ SonarSourceAfterSchema = string2().min(1).max(1024).optional();
11329
+ SonarSourceLimitSchema = number3().int().min(1).max(2000).default(1000);
11330
+ SonarSourceIdsSchema = array(string2().min(1).max(1024)).min(1).max(200).refine((ids) => new Set(ids).size === ids.length);
11331
+ listSonarTrackDigests = oc.route({
11332
+ method: "GET",
11333
+ operationId: "listSonarTrackDigests",
11334
+ path: "/admin/sonar/source/tracks",
11335
+ summary: "List source track digests and revisions",
11336
+ tags: ["Admin"]
11337
+ }).input(strictObject({ after: SonarSourceAfterSchema, limit: SonarSourceLimitSchema })).output(strictObject({
11338
+ items: array(strictObject({
11339
+ payloadDigest: DigestSchema,
11340
+ revision: SequenceSchema,
11341
+ subjectId: string2().min(1).max(1024)
11342
+ })),
11343
+ nextAfter: string2().nullable(),
11344
+ ok: literal(true)
11345
+ }));
11346
+ listSonarTracks = oc.route({
11347
+ method: "POST",
11348
+ operationId: "listSonarTracks",
11349
+ path: "/admin/sonar/source/tracks/items",
11350
+ summary: "Read selected source tracks",
11351
+ tags: ["Admin"]
11352
+ }).input(strictObject({ subjectIds: SonarSourceIdsSchema })).output(strictObject({
11353
+ absentIds: array(string2()),
11354
+ items: array(ArtifactSnapshotItemSchema.extend({ revision: SequenceSchema })),
11355
+ ok: literal(true)
11356
+ }));
11357
+ listSonarCentroidDigests = oc.route({
11358
+ method: "GET",
11359
+ operationId: "listSonarCentroidDigests",
11360
+ path: "/admin/sonar/source/centroids",
11361
+ summary: "List source artist centroid digests",
11362
+ tags: ["Admin"]
11363
+ }).input(strictObject({ after: SonarSourceAfterSchema, limit: SonarSourceLimitSchema })).output(strictObject({
11364
+ items: array(strictObject({ artistId: string2(), digest: DigestSchema })),
11365
+ nextAfter: string2().nullable(),
11366
+ ok: literal(true)
11367
+ }));
11368
+ listSonarCentroids = oc.route({
11369
+ method: "POST",
11370
+ operationId: "listSonarCentroids",
11371
+ path: "/admin/sonar/source/centroids/items",
11372
+ summary: "Read selected artist centroids",
11373
+ tags: ["Admin"]
11374
+ }).input(strictObject({ artistIds: SonarSourceIdsSchema })).output(strictObject({
11375
+ absentIds: array(string2()),
11376
+ items: array(strictObject({ artistId: string2(), blobBase64: string2(), digest: DigestSchema })),
11377
+ ok: literal(true)
11378
+ }));
11307
11379
  registerArtifactConsumer = oc.route({
11308
11380
  method: "POST",
11309
11381
  operationId: "registerArtifactConsumer",
@@ -11364,6 +11436,7 @@ var init_admin_artifacts = __esm(() => {
11364
11436
  tags: ["Admin"]
11365
11437
  }).input(strictObject({
11366
11438
  consumerId: ConsumerIdSchema,
11439
+ fromSeq: number3().int().nonnegative().optional(),
11367
11440
  limit: number3().int().min(1).max(ARTIFACT_CHANGE_API_MAX_LIMIT).default(100)
11368
11441
  })).output(ArtifactChangePageSchema);
11369
11442
  acknowledgeArtifactChanges = oc.route({
@@ -11404,6 +11477,10 @@ var init_admin_artifacts = __esm(() => {
11404
11477
  inactivate_artifact_consumer: inactivateArtifactConsumer,
11405
11478
  list_artifact_changes: listArtifactChanges,
11406
11479
  list_artifact_snapshot: listArtifactSnapshot,
11480
+ list_sonar_centroid_digests: listSonarCentroidDigests,
11481
+ list_sonar_centroids: listSonarCentroids,
11482
+ list_sonar_track_digests: listSonarTrackDigests,
11483
+ list_sonar_tracks: listSonarTracks,
11407
11484
  register_artifact_consumer: registerArtifactConsumer
11408
11485
  };
11409
11486
  });
@@ -14033,6 +14110,7 @@ var init_admin_labels = __esm(() => {
14033
14110
  LabelSeedStateSchema = _enum(["disabled", "enabled", "undecided"]).meta({ id: "LabelSeedState" });
14034
14111
  LabelTriageVerdictSchema = _enum(["dnb", "dnb_partial", "not_dnb", "unclear"]).meta({ id: "LabelTriageVerdict" });
14035
14112
  LabelAdminItemSchema = object({
14113
+ carriesArtistRules: boolean2().optional(),
14036
14114
  createdAt: string2(),
14037
14115
  disambiguation: string2().nullable().optional(),
14038
14116
  findingCount: number2(),
@@ -25013,7 +25091,8 @@ JSON field reference:
25013
25091
  }
25014
25092
  for (const label of labels) {
25015
25093
  const looked = label.triageCheckedAt ? label.triageVerdict : "never looked";
25016
- console.log(`${label.slug} ${label.seedState} ${looked ?? ""}`.trimEnd());
25094
+ const rules = label.carriesArtistRules ? " artist rules" : "";
25095
+ console.log(`${label.slug} ${label.seedState} ${looked ?? ""}${rules}`.trimEnd());
25017
25096
  }
25018
25097
  });
25019
25098
  labels.command("evidence").description("Gather a label's MusicBrainz, Discogs, Beatport and Apple evidence for triage (cached, rate-limited)").argument("<label>", "The label's MusicBrainz MBID, slug, or lbl_\u2026 id").option("--census", "Also count FIRST credits per recording over the label's releases", false).option("--census-pages <n>", "MusicBrainz pages the census may read, 1-5", "5").option("--sources <list>", "Comma list of musicbrainz,discogs,beatport,apple (default all)").option("--refresh", "Ignore cached responses and fetch fresh", false).option("--json", "Print JSON", false).action(async (input, options) => {
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.265.0"
34
+ "version": "0.267.0"
35
35
  }