fluncle 0.112.0 → 0.113.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 +131 -8
- 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.
|
|
560
|
+
currentVersion = "0.113.0".trim() ? "0.113.0".trim() : "0.1.0";
|
|
561
561
|
});
|
|
562
562
|
|
|
563
563
|
// src/update-notifier.ts
|
|
@@ -2209,7 +2209,7 @@ function parseVersion2(version) {
|
|
|
2209
2209
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2210
2210
|
var init_version2 = __esm(() => {
|
|
2211
2211
|
init_output();
|
|
2212
|
-
currentVersion2 = "0.
|
|
2212
|
+
currentVersion2 = "0.113.0".trim() ? "0.113.0".trim() : "0.1.0";
|
|
2213
2213
|
});
|
|
2214
2214
|
|
|
2215
2215
|
// ../../packages/registry/src/index.ts
|
|
@@ -2244,6 +2244,18 @@ var init_src = __esm(() => {
|
|
|
2244
2244
|
url: `${SITE}/log`,
|
|
2245
2245
|
weights: { ssh: "secondary", web: "primary" }
|
|
2246
2246
|
},
|
|
2247
|
+
{
|
|
2248
|
+
exposedContent: [
|
|
2249
|
+
"Fluncle's Logbook — the voyage as a first-person travelogue, one entry per sector-day",
|
|
2250
|
+
"/logbook/:sector — one day written up, the findings inlined as photos"
|
|
2251
|
+
],
|
|
2252
|
+
kind: "web_route",
|
|
2253
|
+
name: "web.logbook",
|
|
2254
|
+
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
2255
|
+
route: "/logbook",
|
|
2256
|
+
url: `${SITE}/logbook`,
|
|
2257
|
+
weights: { ssh: "tertiary", web: "primary" }
|
|
2258
|
+
},
|
|
2247
2259
|
{
|
|
2248
2260
|
exposedContent: ["Fluncle's own DJ mixtapes — each a checkpoint set with an F-marked Log ID"],
|
|
2249
2261
|
kind: "web_route",
|
|
@@ -2851,6 +2863,22 @@ var init_src = __esm(() => {
|
|
|
2851
2863
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-note", kind: "cron" },
|
|
2852
2864
|
weights: { status: "hidden" }
|
|
2853
2865
|
},
|
|
2866
|
+
{
|
|
2867
|
+
command: "fluncle admin logbook gaps",
|
|
2868
|
+
exposedContent: [
|
|
2869
|
+
"author the previous day's Logbook travelogue entry, fill-empty-only (hybrid: one claude -p call)"
|
|
2870
|
+
],
|
|
2871
|
+
kind: "cron",
|
|
2872
|
+
name: "cron.logbook",
|
|
2873
|
+
operatorNotes: "00:40 Amsterdam daily. Hybrid --no-agent; one claude -p writes the day up. Never clobbers an operator entry; the self-healing gap window backfills history oldest-first. Source: docs/agents/hermes/scripts/logbook-sweep.{sh,ts}.",
|
|
2874
|
+
probeConfig: {
|
|
2875
|
+
cadenceMs: 24 * 60 * MINUTE_MS,
|
|
2876
|
+
cronName: "fluncle-logbook",
|
|
2877
|
+
kind: "cron",
|
|
2878
|
+
schedule: { time: "00:40", tz: "Europe/Amsterdam" }
|
|
2879
|
+
},
|
|
2880
|
+
weights: { status: "hidden" }
|
|
2881
|
+
},
|
|
2854
2882
|
{
|
|
2855
2883
|
command: "fluncle admin tracks observe --queue",
|
|
2856
2884
|
exposedContent: [
|
|
@@ -4821,6 +4849,54 @@ var init_newsletter = __esm(() => {
|
|
|
4821
4849
|
init_output();
|
|
4822
4850
|
});
|
|
4823
4851
|
|
|
4852
|
+
// src/commands/admin-logbook.ts
|
|
4853
|
+
var exports_admin_logbook = {};
|
|
4854
|
+
__export(exports_admin_logbook, {
|
|
4855
|
+
logbookUpdateCommand: () => logbookUpdateCommand,
|
|
4856
|
+
logbookGapsCommand: () => logbookGapsCommand,
|
|
4857
|
+
logbookCreateCommand: () => logbookCreateCommand
|
|
4858
|
+
});
|
|
4859
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
4860
|
+
async function logbookGapsCommand(limit) {
|
|
4861
|
+
const query = typeof limit === "number" ? `?limit=${encodeURIComponent(String(limit))}` : "";
|
|
4862
|
+
const response = await adminApiGet(`/api/admin/logbook/gaps${query}`);
|
|
4863
|
+
return response.gaps;
|
|
4864
|
+
}
|
|
4865
|
+
function resolveBody(options) {
|
|
4866
|
+
if (options.body !== undefined) {
|
|
4867
|
+
return options.body;
|
|
4868
|
+
}
|
|
4869
|
+
if (options.bodyFile !== undefined) {
|
|
4870
|
+
if (!existsSync5(options.bodyFile)) {
|
|
4871
|
+
throw new CliError2("file_not_found", `Body file not found: ${options.bodyFile}`);
|
|
4872
|
+
}
|
|
4873
|
+
return readFileSync5(options.bodyFile, "utf-8");
|
|
4874
|
+
}
|
|
4875
|
+
throw new CliError2("missing_body", "An entry needs a body via --body <text> or --body-file <entry.md>");
|
|
4876
|
+
}
|
|
4877
|
+
function requireTitle(options) {
|
|
4878
|
+
if (options.title === undefined || !options.title.trim()) {
|
|
4879
|
+
throw new CliError2("missing_title", "An entry needs a title via --title <text>");
|
|
4880
|
+
}
|
|
4881
|
+
return options.title;
|
|
4882
|
+
}
|
|
4883
|
+
async function logbookCreateCommand(sector, options) {
|
|
4884
|
+
return adminApiPost(`/api/admin/logbook/${encodeURIComponent(sector)}`, {
|
|
4885
|
+
body: resolveBody(options),
|
|
4886
|
+
title: requireTitle(options)
|
|
4887
|
+
});
|
|
4888
|
+
}
|
|
4889
|
+
async function logbookUpdateCommand(sector, options) {
|
|
4890
|
+
return adminApiPatch(`/api/admin/logbook/${encodeURIComponent(sector)}`, {
|
|
4891
|
+
body: resolveBody(options),
|
|
4892
|
+
title: requireTitle(options)
|
|
4893
|
+
});
|
|
4894
|
+
}
|
|
4895
|
+
var init_admin_logbook = __esm(() => {
|
|
4896
|
+
init_api();
|
|
4897
|
+
init_output();
|
|
4898
|
+
});
|
|
4899
|
+
|
|
4824
4900
|
// src/commands/add.ts
|
|
4825
4901
|
async function addCommand2(spotifyUrl, options) {
|
|
4826
4902
|
const result = await adminApiPost("/api/admin/tracks", {
|
|
@@ -5485,7 +5561,7 @@ var init_format2 = __esm(() => {
|
|
|
5485
5561
|
});
|
|
5486
5562
|
|
|
5487
5563
|
// src/cli.ts
|
|
5488
|
-
import { existsSync as
|
|
5564
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
5489
5565
|
import path2 from "path";
|
|
5490
5566
|
|
|
5491
5567
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -7969,6 +8045,22 @@ function addAdminCommands(program2) {
|
|
|
7969
8045
|
const { newsletterDeleteCommand: newsletterDeleteCommand2 } = await Promise.resolve().then(() => (init_newsletter(), exports_newsletter));
|
|
7970
8046
|
await runNewsletterDelete(id, options, newsletterDeleteCommand2);
|
|
7971
8047
|
});
|
|
8048
|
+
const adminLogbook = configureCommand(admin.command("logbook").description("Fluncle's Logbook (travelogue) commands"));
|
|
8049
|
+
adminLogbook.action(() => {
|
|
8050
|
+
adminLogbook.outputHelp();
|
|
8051
|
+
});
|
|
8052
|
+
adminLogbook.command("gaps").description("List sector-days with findings but no logbook entry (the sweep's worklist)").option("--limit <limit>", "Max gaps to return").option("--json", "Print JSON", false).allowExcessArguments().action(async (options) => {
|
|
8053
|
+
const { logbookGapsCommand: logbookGapsCommand2 } = await Promise.resolve().then(() => (init_admin_logbook(), exports_admin_logbook));
|
|
8054
|
+
await runLogbookGaps(options, logbookGapsCommand2);
|
|
8055
|
+
});
|
|
8056
|
+
adminLogbook.command("create").description("Author a sector-day's entry (fills an empty sector only)").argument("[sector]").option("--title <text>", "The entry title").option("--body <text>", "The entry body (markdown; [[logId]] figure tokens)").option("--body-file <file>", "Read the body from a file (the sweep's path)").option("--json", "Print JSON", false).allowExcessArguments().action(async (sector, options) => {
|
|
8057
|
+
const { logbookCreateCommand: logbookCreateCommand2 } = await Promise.resolve().then(() => (init_admin_logbook(), exports_admin_logbook));
|
|
8058
|
+
await runLogbookWrite(sector, options, logbookCreateCommand2);
|
|
8059
|
+
});
|
|
8060
|
+
adminLogbook.command("update").description("Create or overwrite a sector-day's entry. OPERATOR only").argument("[sector]").option("--title <text>", "The entry title").option("--body <text>", "The entry body (markdown; [[logId]] figure tokens)").option("--body-file <file>", "Read the body from a file").option("--json", "Print JSON", false).allowExcessArguments().action(async (sector, options) => {
|
|
8061
|
+
const { logbookUpdateCommand: logbookUpdateCommand2 } = await Promise.resolve().then(() => (init_admin_logbook(), exports_admin_logbook));
|
|
8062
|
+
await runLogbookWrite(sector, options, logbookUpdateCommand2);
|
|
8063
|
+
});
|
|
7972
8064
|
const submissions = configureCommand(admin.command("submissions").description("Review listener submissions"));
|
|
7973
8065
|
submissions.option("--json", "Print JSON", false).action(async (options) => {
|
|
7974
8066
|
const { listSubmissionsCommand: listSubmissionsCommand2 } = await Promise.resolve().then(() => (init_submissions(), exports_submissions));
|
|
@@ -7999,7 +8091,7 @@ function addAdminCommands(program2) {
|
|
|
7999
8091
|
if (!submissionId) {
|
|
8000
8092
|
throw new Error("Missing submission id for: triage");
|
|
8001
8093
|
}
|
|
8002
|
-
const verdict = options.verdictFile ?
|
|
8094
|
+
const verdict = options.verdictFile ? readFileSync6(options.verdictFile, "utf8") : options.verdict;
|
|
8003
8095
|
if (!verdict || !verdict.trim()) {
|
|
8004
8096
|
throw new Error("Usage: fluncle admin submissions triage <submissionId> (--verdict <text> | --verdict-file <file>) [--json]");
|
|
8005
8097
|
}
|
|
@@ -8084,7 +8176,7 @@ async function runTrackPreviewArchive(idOrLogId, options, previewArchiveUploadCo
|
|
|
8084
8176
|
console.log(` mime: ${result.mime}`);
|
|
8085
8177
|
}
|
|
8086
8178
|
async function runTrackObserve(idOrLogId, options, trackObserveCommand2) {
|
|
8087
|
-
const script = options.scriptFile ?
|
|
8179
|
+
const script = options.scriptFile ? readFileSync6(options.scriptFile, "utf8") : options.script;
|
|
8088
8180
|
if (!idOrLogId || !script || !script.trim()) {
|
|
8089
8181
|
throw new Error("Usage: fluncle admin tracks observe <track_id|log_id> (--script <text> | --script-file <file>) [--voice-id <id>] [--duration-ms <ms>] [--context-note <text>] [--json]");
|
|
8090
8182
|
}
|
|
@@ -8140,7 +8232,7 @@ async function runTrackContext(idOrLogId, options, trackContextCommand2) {
|
|
|
8140
8232
|
}
|
|
8141
8233
|
}
|
|
8142
8234
|
async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
|
|
8143
|
-
const note = options.scriptFile ?
|
|
8235
|
+
const note = options.scriptFile ? readFileSync6(options.scriptFile, "utf8") : options.script;
|
|
8144
8236
|
if (!idOrLogId || !note || !note.trim()) {
|
|
8145
8237
|
throw new Error("Usage: fluncle admin tracks note <track_id|log_id> (--script <text> | --script-file <file>) [--json]");
|
|
8146
8238
|
}
|
|
@@ -8453,7 +8545,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
8453
8545
|
return;
|
|
8454
8546
|
}
|
|
8455
8547
|
const candidate = path2.join(dir, name);
|
|
8456
|
-
return
|
|
8548
|
+
return existsSync6(candidate) ? candidate : undefined;
|
|
8457
8549
|
};
|
|
8458
8550
|
const resolveFile = (explicit, name) => {
|
|
8459
8551
|
if (explicit) {
|
|
@@ -8648,7 +8740,7 @@ async function runTrackUpdate(trackId, options, trackUpdateCommand3) {
|
|
|
8648
8740
|
if (options.analyzedFrom !== undefined && options.analyzedFrom !== "full" && options.analyzedFrom !== "preview") {
|
|
8649
8741
|
throw new Error(`Invalid --analyzed-from: ${options.analyzedFrom} (expected full or preview)`);
|
|
8650
8742
|
}
|
|
8651
|
-
const embeddingRaw = options.embeddingFile ?
|
|
8743
|
+
const embeddingRaw = options.embeddingFile ? readFileSync6(options.embeddingFile, "utf8") : options.embedding;
|
|
8652
8744
|
const embedding = parseEmbeddingArg(embeddingRaw);
|
|
8653
8745
|
const result = await trackUpdateCommand3(trackId, {
|
|
8654
8746
|
analyzedAt: options.analyzedAt,
|
|
@@ -8911,6 +9003,37 @@ async function runNewsletterList(options, newsletterListCommand2) {
|
|
|
8911
9003
|
console.log(`${label} ${edition.status} ${edition.id} ${subject}`);
|
|
8912
9004
|
}
|
|
8913
9005
|
}
|
|
9006
|
+
async function runLogbookGaps(options, logbookGapsCommand2) {
|
|
9007
|
+
const limit = options.limit === undefined ? undefined : Number.parseInt(options.limit, 10);
|
|
9008
|
+
const gaps = await logbookGapsCommand2(limit !== undefined && Number.isFinite(limit) ? limit : undefined);
|
|
9009
|
+
if (options.json) {
|
|
9010
|
+
printJson({ gaps, ok: true });
|
|
9011
|
+
return;
|
|
9012
|
+
}
|
|
9013
|
+
if (gaps.length === 0) {
|
|
9014
|
+
console.log("No gaps \u2014 every past sector-day with findings has an entry.");
|
|
9015
|
+
return;
|
|
9016
|
+
}
|
|
9017
|
+
for (const gap of gaps) {
|
|
9018
|
+
console.log(`sector ${gap.sector} ${gap.date.slice(0, 10)} ${gap.findings.length} findings`);
|
|
9019
|
+
}
|
|
9020
|
+
}
|
|
9021
|
+
async function runLogbookWrite(sector, options, writeCommand) {
|
|
9022
|
+
if (!sector) {
|
|
9023
|
+
throw new Error("Missing sector for the logbook entry");
|
|
9024
|
+
}
|
|
9025
|
+
const result = await writeCommand(sector, {
|
|
9026
|
+
body: options.body,
|
|
9027
|
+
bodyFile: options.bodyFile,
|
|
9028
|
+
title: options.title
|
|
9029
|
+
});
|
|
9030
|
+
if (options.json) {
|
|
9031
|
+
printJson(result);
|
|
9032
|
+
return;
|
|
9033
|
+
}
|
|
9034
|
+
const skipped = "skipped" in result && result.skipped;
|
|
9035
|
+
console.log(skipped ? `sector ${result.entry.sector}: an entry already stands \u2014 no-op (${result.entry.generatedBy})` : `sector ${result.entry.sector}: ${result.entry.title}`);
|
|
9036
|
+
}
|
|
8914
9037
|
async function runAdd(spotifyUrl, options, addCommand3) {
|
|
8915
9038
|
if (!spotifyUrl) {
|
|
8916
9039
|
throw new Error("Missing Spotify track URL");
|
package/package.json
CHANGED