fluncle 0.245.0 → 0.247.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 +143 -8
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -622,7 +622,7 @@ function parseVersion(version) {
|
|
|
622
622
|
var currentVersion;
|
|
623
623
|
var init_version = __esm(() => {
|
|
624
624
|
init_output();
|
|
625
|
-
currentVersion = "0.
|
|
625
|
+
currentVersion = "0.247.0".trim() ? "0.247.0".trim() : "0.1.0";
|
|
626
626
|
});
|
|
627
627
|
|
|
628
628
|
// src/update-notifier.ts
|
|
@@ -2335,7 +2335,7 @@ function parseVersion2(version) {
|
|
|
2335
2335
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2336
2336
|
var init_version2 = __esm(() => {
|
|
2337
2337
|
init_output();
|
|
2338
|
-
currentVersion2 = "0.
|
|
2338
|
+
currentVersion2 = "0.247.0".trim() ? "0.247.0".trim() : "0.1.0";
|
|
2339
2339
|
});
|
|
2340
2340
|
|
|
2341
2341
|
// ../../packages/registry/src/index.ts
|
|
@@ -12341,7 +12341,7 @@ var init_admin_prompts = __esm(() => {
|
|
|
12341
12341
|
});
|
|
12342
12342
|
|
|
12343
12343
|
// ../../packages/contracts/src/orpc/admin-projections.ts
|
|
12344
|
-
var PROJECTION_STEP_LIMIT_MAX = 500, ProjectionTargetSchema, ProjectionCutoverSchema, ProjectionStepActionSchema, CountSchema, BoundedCountSchema, DigestSchema2, RebuildSchema, BacklogSchema, RepairSchema, OldestOutstandingMarkerAgeSchema, ConvergenceSchema, ProjectionFamilyStatusSchema, ProjectionStatusSchema, getProjectionStatus, advanceProjection, setProjectionCutover, adminProjectionsContract;
|
|
12344
|
+
var PROJECTION_STEP_LIMIT_MAX = 500, ProjectionTargetSchema, ProjectionCutoverSchema, ProjectionStepActionSchema, CountSchema, BoundedCountSchema, DigestSchema2, RebuildSchema, BacklogSchema, RepairSchema, OldestOutstandingMarkerAgeSchema, ConvergenceSchema, ProjectionFamilyStatusSchema, ProjectionStatusSchema, getProjectionStatus, advanceProjection, setProjectionCutover, DUE_WORK_REKEY_LIMIT_MAX = 500, rekeyDueWorkQueue, adminProjectionsContract;
|
|
12345
12345
|
var init_admin_projections = __esm(() => {
|
|
12346
12346
|
init_dist3();
|
|
12347
12347
|
init_zod();
|
|
@@ -12433,6 +12433,8 @@ var init_admin_projections = __esm(() => {
|
|
|
12433
12433
|
complete: boolean2(),
|
|
12434
12434
|
ok: literal(true),
|
|
12435
12435
|
processed: CountSchema,
|
|
12436
|
+
rebuildRowsWalked: CountSchema.optional(),
|
|
12437
|
+
rebuildStaleFamilies: CountSchema.optional(),
|
|
12436
12438
|
scheduled: CountSchema,
|
|
12437
12439
|
status: ProjectionStatusSchema.optional(),
|
|
12438
12440
|
target: ProjectionTargetSchema,
|
|
@@ -12450,9 +12452,33 @@ var init_admin_projections = __esm(() => {
|
|
|
12450
12452
|
status: ProjectionStatusSchema,
|
|
12451
12453
|
target: ProjectionCutoverSchema
|
|
12452
12454
|
}));
|
|
12455
|
+
rekeyDueWorkQueue = oc.route({
|
|
12456
|
+
method: "POST",
|
|
12457
|
+
operationId: "rekeyDueWorkQueue",
|
|
12458
|
+
path: "/admin/projections/due-work/{workKind}/rekey",
|
|
12459
|
+
summary: "Mark one due-work queue for re-keying under today's order definition",
|
|
12460
|
+
tags: ["Admin"]
|
|
12461
|
+
}).input(object({
|
|
12462
|
+
apply: boolean2().default(false),
|
|
12463
|
+
cursor: string2().max(200).nullable().default(null),
|
|
12464
|
+
limit: number2().int().min(1).max(DUE_WORK_REKEY_LIMIT_MAX).default(DUE_WORK_REKEY_LIMIT_MAX),
|
|
12465
|
+
workKind: string2().min(1).max(64)
|
|
12466
|
+
})).output(object({
|
|
12467
|
+
applied: boolean2(),
|
|
12468
|
+
cursor: string2().nullable(),
|
|
12469
|
+
definitionVersion: string2(),
|
|
12470
|
+
hasMore: boolean2(),
|
|
12471
|
+
marked: CountSchema,
|
|
12472
|
+
matched: CountSchema,
|
|
12473
|
+
ok: literal(true),
|
|
12474
|
+
remaining: BoundedCountSchema,
|
|
12475
|
+
subjectType: string2(),
|
|
12476
|
+
workKind: string2()
|
|
12477
|
+
}));
|
|
12453
12478
|
adminProjectionsContract = {
|
|
12454
12479
|
advance_projection: advanceProjection,
|
|
12455
12480
|
get_projection_status: getProjectionStatus,
|
|
12481
|
+
rekey_due_work_queue: rekeyDueWorkQueue,
|
|
12456
12482
|
set_projection_cutover: setProjectionCutover
|
|
12457
12483
|
};
|
|
12458
12484
|
});
|
|
@@ -13468,7 +13494,7 @@ var init_admin_hub_counts = __esm(() => {
|
|
|
13468
13494
|
});
|
|
13469
13495
|
|
|
13470
13496
|
// ../../packages/contracts/src/orpc/admin-labels.ts
|
|
13471
|
-
var LabelSeedStateSchema, LabelAdminItemSchema, listLabelsAdmin, updateLabel, listLabelArtistRules, replaceLabelArtistRules, MergeLabelResultSchema, mergeLabel, LabelAliasSourceSchema, LabelAliasKindSchema, LabelAliasCandidateSchema, listLabelAliases, confirmLabelAlias, rejectLabelAlias, DescribeLabelBodySchema, describeLabel, draftLabelBio, LabelBioWorkItemSchema, listLabelsMissingBio, adminLabelsContract;
|
|
13497
|
+
var LabelSeedStateSchema, LabelAdminItemSchema, listLabelsAdmin, updateLabel, listLabelArtistRules, replaceLabelArtistRules, MergeLabelResultSchema, mergeLabel, MB_LABEL_MBID_PATTERN, MintLabelOutcomeSchema, mintLabel, LabelAliasSourceSchema, LabelAliasKindSchema, LabelAliasCandidateSchema, listLabelAliases, confirmLabelAlias, rejectLabelAlias, DescribeLabelBodySchema, describeLabel, draftLabelBio, LabelBioWorkItemSchema, listLabelsMissingBio, adminLabelsContract;
|
|
13472
13498
|
var init_admin_labels = __esm(() => {
|
|
13473
13499
|
init_dist3();
|
|
13474
13500
|
init_zod();
|
|
@@ -13562,6 +13588,24 @@ var init_admin_labels = __esm(() => {
|
|
|
13562
13588
|
summary: "Merge a slug-split label into its canonical row (operator; re-points + redirects)",
|
|
13563
13589
|
tags: ["Admin"]
|
|
13564
13590
|
}).input(object({ canonicalSlug: string2(), slug: string2() })).output(object({ ok: literal(true), result: MergeLabelResultSchema }));
|
|
13591
|
+
MB_LABEL_MBID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13592
|
+
MintLabelOutcomeSchema = _enum(["minted", "adopted", "known"]).meta({ id: "MintLabelOutcome" });
|
|
13593
|
+
mintLabel = oc.route({
|
|
13594
|
+
method: "POST",
|
|
13595
|
+
operationId: "mintLabel",
|
|
13596
|
+
path: "/admin/labels",
|
|
13597
|
+
summary: "Mint a label from its MusicBrainz identity (operator; connect-or-create, idempotent)",
|
|
13598
|
+
tags: ["Admin"]
|
|
13599
|
+
}).input(object({
|
|
13600
|
+
mbLabelId: string2().regex(MB_LABEL_MBID_PATTERN, {
|
|
13601
|
+
error: "mbLabelId must be a MusicBrainz label MBID"
|
|
13602
|
+
}),
|
|
13603
|
+
seedState: LabelSeedStateSchema.optional()
|
|
13604
|
+
})).output(object({
|
|
13605
|
+
label: LabelAdminItemSchema,
|
|
13606
|
+
ok: literal(true),
|
|
13607
|
+
outcome: MintLabelOutcomeSchema
|
|
13608
|
+
}));
|
|
13565
13609
|
LabelAliasSourceSchema = _enum(["operator", "apple", "musicbrainz", "discogs", "spotify"]).meta({ id: "LabelAliasSource" });
|
|
13566
13610
|
LabelAliasKindSchema = _enum(["name", "hint"]).meta({ id: "LabelAliasKind" });
|
|
13567
13611
|
LabelAliasCandidateSchema = object({
|
|
@@ -13648,6 +13692,7 @@ var init_admin_labels = __esm(() => {
|
|
|
13648
13692
|
list_labels_admin: listLabelsAdmin,
|
|
13649
13693
|
list_labels_missing_bio: listLabelsMissingBio,
|
|
13650
13694
|
merge_label: mergeLabel,
|
|
13695
|
+
mint_label: mintLabel,
|
|
13651
13696
|
reject_label_alias: rejectLabelAlias,
|
|
13652
13697
|
replace_label_artist_rules: replaceLabelArtistRules,
|
|
13653
13698
|
update_label: updateLabel
|
|
@@ -16862,6 +16907,7 @@ async function advanceProjectionCommand(input) {
|
|
|
16862
16907
|
let processed = response.processed;
|
|
16863
16908
|
let scheduled = response.scheduled;
|
|
16864
16909
|
let wallStopped = false;
|
|
16910
|
+
let rebuildRowsWalked = response.rebuildRowsWalked ?? 0;
|
|
16865
16911
|
while (!response.complete && steps < maxSteps) {
|
|
16866
16912
|
if (wallMs !== undefined && now() - startedAt >= wallMs) {
|
|
16867
16913
|
wallStopped = true;
|
|
@@ -16871,18 +16917,56 @@ async function advanceProjectionCommand(input) {
|
|
|
16871
16917
|
steps += 1;
|
|
16872
16918
|
processed += response.processed;
|
|
16873
16919
|
scheduled += response.scheduled;
|
|
16920
|
+
rebuildRowsWalked += response.rebuildRowsWalked ?? 0;
|
|
16874
16921
|
}
|
|
16922
|
+
const rebuild = response.rebuildStaleFamilies === undefined ? {} : { rebuildRowsWalked, rebuildStaleFamilies: response.rebuildStaleFamilies };
|
|
16875
16923
|
if (!includeTerminalStatus) {
|
|
16876
16924
|
const { status: _status, ...withoutStatus } = response;
|
|
16877
|
-
return { ...withoutStatus, processed, scheduled, steps, wallStopped };
|
|
16925
|
+
return { ...withoutStatus, ...rebuild, processed, scheduled, steps, wallStopped };
|
|
16878
16926
|
}
|
|
16879
16927
|
const status = response.status ?? (await getProjectionStatusCommand()).status;
|
|
16880
|
-
return { ...response, processed, scheduled, status, steps, wallStopped };
|
|
16928
|
+
return { ...response, ...rebuild, processed, scheduled, status, steps, wallStopped };
|
|
16881
16929
|
}
|
|
16882
16930
|
async function setProjectionCutoverCommand(input) {
|
|
16883
16931
|
const { enabled, target } = input;
|
|
16884
16932
|
return adminApiPut(`/api/v1/admin/projections/${target}/cutover`, { enabled });
|
|
16885
16933
|
}
|
|
16934
|
+
async function rekeyDueWorkQueueCommand(input) {
|
|
16935
|
+
const { apply, limit, maxPages, workKind } = input;
|
|
16936
|
+
let cursor = input.cursor;
|
|
16937
|
+
let response = await adminApiPost(`/api/v1/admin/projections/due-work/${encodeURIComponent(workKind)}/rekey`, { apply, cursor, limit });
|
|
16938
|
+
let pages = 1;
|
|
16939
|
+
let marked = response.marked;
|
|
16940
|
+
let matched = response.matched;
|
|
16941
|
+
cursor = response.cursor;
|
|
16942
|
+
while (apply && response.hasMore && response.cursor !== null && pages < maxPages) {
|
|
16943
|
+
response = await adminApiPost(`/api/v1/admin/projections/due-work/${encodeURIComponent(workKind)}/rekey`, { apply, cursor, limit });
|
|
16944
|
+
pages += 1;
|
|
16945
|
+
marked += response.marked;
|
|
16946
|
+
matched += response.matched;
|
|
16947
|
+
cursor = response.cursor;
|
|
16948
|
+
}
|
|
16949
|
+
return { ...response, cursor, marked, matched, pages };
|
|
16950
|
+
}
|
|
16951
|
+
function parseDueWorkRekeyLimit(value) {
|
|
16952
|
+
const limit = Number(value);
|
|
16953
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > DUE_WORK_REKEY_LIMIT_MAX) {
|
|
16954
|
+
throw new Error(`--limit must be a whole number from 1 through ${DUE_WORK_REKEY_LIMIT_MAX}`);
|
|
16955
|
+
}
|
|
16956
|
+
return limit;
|
|
16957
|
+
}
|
|
16958
|
+
function parseDueWorkRekeyMaxPages(value) {
|
|
16959
|
+
const pages = Number(value);
|
|
16960
|
+
if (!Number.isSafeInteger(pages) || pages < 1 || pages > DUE_WORK_REKEY_MAX_PAGES) {
|
|
16961
|
+
throw new Error(`--max-pages must be a whole number from 1 through ${DUE_WORK_REKEY_MAX_PAGES}`);
|
|
16962
|
+
}
|
|
16963
|
+
return pages;
|
|
16964
|
+
}
|
|
16965
|
+
function dueWorkRekeyLine(result) {
|
|
16966
|
+
const remaining = `${result.remaining.count}${result.remaining.truncated ? "+" : ""}`;
|
|
16967
|
+
const verb = result.applied ? `marked ${result.marked}` : `would mark ${result.matched}`;
|
|
16968
|
+
return `${result.workKind} re-key — ${verb} over ${result.pages} page(s); ${remaining} still on the old key; cursor ${result.cursor ?? "start"}; definition ${result.definitionVersion}.`;
|
|
16969
|
+
}
|
|
16886
16970
|
function parseProjectionEnabled(value) {
|
|
16887
16971
|
if (value !== "true" && value !== "false") {
|
|
16888
16972
|
throw new Error("--enabled must be true or false");
|
|
@@ -16937,7 +17021,7 @@ function projectionStatusLines(status) {
|
|
|
16937
17021
|
row("Artist qualification", status.projections.artistQualification, status.cutovers.publicProjections)
|
|
16938
17022
|
];
|
|
16939
17023
|
}
|
|
16940
|
-
var PROJECTION_MAX_STEPS = 100, PROJECTION_WALL_MS_MIN = 1000, PROJECTION_WALL_MS_MAX = 600000;
|
|
17024
|
+
var PROJECTION_MAX_STEPS = 100, DUE_WORK_REKEY_MAX_PAGES = 200, PROJECTION_WALL_MS_MIN = 1000, PROJECTION_WALL_MS_MAX = 600000;
|
|
16941
17025
|
var init_admin_projections2 = __esm(() => {
|
|
16942
17026
|
init_orpc();
|
|
16943
17027
|
init_api();
|
|
@@ -19049,6 +19133,17 @@ async function updateLabelCommand(slugOrId, seedState, rewalk = false) {
|
|
|
19049
19133
|
});
|
|
19050
19134
|
return response.label;
|
|
19051
19135
|
}
|
|
19136
|
+
async function mintLabelCommand(mbLabelId, seedState) {
|
|
19137
|
+
const mbid = mbLabelId.trim().toLowerCase();
|
|
19138
|
+
if (!MBID_PATTERN2.test(mbid)) {
|
|
19139
|
+
throw new Error(`'${mbLabelId}' is not a MusicBrainz label MBID`);
|
|
19140
|
+
}
|
|
19141
|
+
const response = await adminApiPost("/api/v1/admin/labels", {
|
|
19142
|
+
mbLabelId: mbid,
|
|
19143
|
+
...seedState === undefined ? {} : { seedState }
|
|
19144
|
+
});
|
|
19145
|
+
return { label: response.label, outcome: response.outcome };
|
|
19146
|
+
}
|
|
19052
19147
|
async function describeLabelCommand(slug, options) {
|
|
19053
19148
|
return adminApiPost(`/api/v1/admin/labels/${encodeURIComponent(slug)}/bio`, buildBioBody2(options));
|
|
19054
19149
|
}
|
|
@@ -21502,6 +21597,15 @@ function labelUpdateLines(label, options) {
|
|
|
21502
21597
|
}
|
|
21503
21598
|
return lines;
|
|
21504
21599
|
}
|
|
21600
|
+
var LABEL_MINT_WORDS = {
|
|
21601
|
+
adopted: "ADOPTED",
|
|
21602
|
+
known: "ALREADY KNOWN",
|
|
21603
|
+
minted: "MINTED"
|
|
21604
|
+
};
|
|
21605
|
+
function labelMintLine(label, outcome, requestedMbid) {
|
|
21606
|
+
const word = LABEL_MINT_WORDS[outcome] ?? outcome.toUpperCase();
|
|
21607
|
+
return `${word} \u2014 ${label.name} (${label.slug}), MusicBrainz ${label.mbLabelId ?? requestedMbid}, seed state ${label.seedState.toUpperCase()}.`;
|
|
21608
|
+
}
|
|
21505
21609
|
var ARTIST_RULE_BOUNDARY = "Rules change what the next crawl takes. Everything already here stays.";
|
|
21506
21610
|
var UNLISTED_RULE_BOUNDARY = "Unlisted takes the artist page down. The crawl and everything already here stay.";
|
|
21507
21611
|
function artistRuleWriteLine(prefix, boundary = ARTIST_RULE_BOUNDARY) {
|
|
@@ -21786,6 +21890,21 @@ JSON field reference:
|
|
|
21786
21890
|
}
|
|
21787
21891
|
console.log(`${result.target} ${result.action}: steps ${result.steps}, processed ${result.processed}, scheduled ${result.scheduled}, ${result.complete ? "complete" : result.wallStopped ? "wall budget spent, more work remains" : "more work remains"}.`);
|
|
21788
21892
|
});
|
|
21893
|
+
adminProjections.command("rekey").description("Mark one due-work queue for re-keying under today's order definition").requiredOption("--work-kind <kind>", "Physical queue, e.g. capture-catalogue").option("--cursor <subjectId>", "Resume after this subject id").option("--limit <limit>", "Rows per page (1-500)", "500").option("--max-pages <pages>", "Maximum pages to mark in one run (1-200)", "1").option("--apply", "Write the repair markers; omit for a dry run", false).option("--json", "Print the page result as JSON", false).action(async (options) => {
|
|
21894
|
+
const projections = await Promise.resolve().then(() => (init_admin_projections2(), {}));
|
|
21895
|
+
const result = await rekeyDueWorkQueueCommand({
|
|
21896
|
+
apply: options.apply,
|
|
21897
|
+
cursor: options.cursor ?? null,
|
|
21898
|
+
limit: parseDueWorkRekeyLimit(options.limit),
|
|
21899
|
+
maxPages: parseDueWorkRekeyMaxPages(options.maxPages),
|
|
21900
|
+
workKind: options.workKind
|
|
21901
|
+
});
|
|
21902
|
+
if (options.json) {
|
|
21903
|
+
printJson(result);
|
|
21904
|
+
return;
|
|
21905
|
+
}
|
|
21906
|
+
console.log(dueWorkRekeyLine(result));
|
|
21907
|
+
});
|
|
21789
21908
|
adminProjections.command("set").description("Open or close one readiness-gated projection cutover").requiredOption("--target <target>", "track_due_work, crawl_due_work, or public_projections").requiredOption("--enabled <boolean>", "true opens; false closes unconditionally").option("--json", "Print the stored flag and current readiness as JSON", false).action(async (options) => {
|
|
21790
21909
|
const projections = await Promise.resolve().then(() => (init_admin_projections2(), {}));
|
|
21791
21910
|
const result = await setProjectionCutoverCommand({
|
|
@@ -22814,6 +22933,19 @@ JSON field reference:
|
|
|
22814
22933
|
console.log(line);
|
|
22815
22934
|
}
|
|
22816
22935
|
});
|
|
22936
|
+
labels.command("mint").description("Mint a label from its MusicBrainz MBID (operator; idempotent)").argument("<mbid>", "The MusicBrainz label MBID").option("--seed-state <state>", "Rule it on arrival: enabled, disabled, or undecided").option("--json", "Print JSON", false).action(async (mbid, options) => {
|
|
22937
|
+
const seedState = options.seedState;
|
|
22938
|
+
if (seedState !== undefined && seedState !== "enabled" && seedState !== "disabled" && seedState !== "undecided") {
|
|
22939
|
+
throw new Error("Pass --seed-state enabled|disabled|undecided");
|
|
22940
|
+
}
|
|
22941
|
+
await Promise.resolve().then(() => init_admin_labels2());
|
|
22942
|
+
const { label, outcome } = await mintLabelCommand(mbid, seedState);
|
|
22943
|
+
if (options.json) {
|
|
22944
|
+
printJson({ label, ok: true, outcome });
|
|
22945
|
+
return;
|
|
22946
|
+
}
|
|
22947
|
+
console.log(labelMintLine(label, outcome, mbid));
|
|
22948
|
+
});
|
|
22817
22949
|
labels.command("merge").description("Merge a slug-split label into its canonical row (operator; re-points + redirects)").argument("<losingSlug>", "The duplicate label to fold away").argument("<canonicalSlug>", "The label to keep \u2014 everything re-points onto it").option("--json", "Print JSON", false).action(async (losingSlug, canonicalSlug, options) => {
|
|
22818
22950
|
await Promise.resolve().then(() => init_admin_labels2());
|
|
22819
22951
|
const result = await mergeLabelCommand(losingSlug, canonicalSlug);
|
|
@@ -25785,6 +25917,7 @@ var stringOptions = new Set([
|
|
|
25785
25917
|
"--lens",
|
|
25786
25918
|
"--limit",
|
|
25787
25919
|
"--max-hop",
|
|
25920
|
+
"--max-pages",
|
|
25788
25921
|
"--max-steps",
|
|
25789
25922
|
"--max-overlap",
|
|
25790
25923
|
"--metrics",
|
|
@@ -25845,7 +25978,8 @@ var stringOptions = new Set([
|
|
|
25845
25978
|
"--voice-id",
|
|
25846
25979
|
"--wall-ms",
|
|
25847
25980
|
"--window-since",
|
|
25848
|
-
"--window-until"
|
|
25981
|
+
"--window-until",
|
|
25982
|
+
"--work-kind"
|
|
25849
25983
|
]);
|
|
25850
25984
|
var rootHelpSections = `
|
|
25851
25985
|
|
|
@@ -25887,6 +26021,7 @@ export {
|
|
|
25887
26021
|
UNLISTED_RULE_BOUNDARY,
|
|
25888
26022
|
artistRuleLines,
|
|
25889
26023
|
createProgram,
|
|
26024
|
+
labelMintLine,
|
|
25890
26025
|
labelUpdateLines,
|
|
25891
26026
|
parseTelemetryTimestamp,
|
|
25892
26027
|
unmatchedRowLine
|
package/package.json
CHANGED