fluncle 0.88.0 → 0.90.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 +89 -12
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -538,7 +538,7 @@ function parseVersion(version) {
|
|
|
538
538
|
var currentVersion;
|
|
539
539
|
var init_version = __esm(() => {
|
|
540
540
|
init_output();
|
|
541
|
-
currentVersion = "0.
|
|
541
|
+
currentVersion = "0.90.0".trim() ? "0.90.0".trim() : "0.1.0";
|
|
542
542
|
});
|
|
543
543
|
|
|
544
544
|
// src/update-notifier.ts
|
|
@@ -2168,7 +2168,7 @@ function parseVersion2(version) {
|
|
|
2168
2168
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2169
2169
|
var init_version2 = __esm(() => {
|
|
2170
2170
|
init_output();
|
|
2171
|
-
currentVersion2 = "0.
|
|
2171
|
+
currentVersion2 = "0.90.0".trim() ? "0.90.0".trim() : "0.1.0";
|
|
2172
2172
|
});
|
|
2173
2173
|
|
|
2174
2174
|
// ../../packages/registry/src/index.ts
|
|
@@ -2894,16 +2894,58 @@ __export(exports_track, {
|
|
|
2894
2894
|
trackNoteCommand: () => trackNoteCommand,
|
|
2895
2895
|
trackGetCommand: () => trackGetCommand,
|
|
2896
2896
|
trackDraftCommand: () => trackDraftCommand,
|
|
2897
|
-
trackContextCommand: () => trackContextCommand
|
|
2897
|
+
trackContextCommand: () => trackContextCommand,
|
|
2898
|
+
isPlatesOnlyUpload: () => isPlatesOnlyUpload,
|
|
2899
|
+
checkBundleCompleteness: () => checkBundleCompleteness
|
|
2898
2900
|
});
|
|
2899
2901
|
async function trackGetCommand(idOrLogId) {
|
|
2900
2902
|
return publicApiGet(`/api/tracks/${encodeURIComponent(idOrLogId)}`);
|
|
2901
2903
|
}
|
|
2902
|
-
|
|
2904
|
+
function isPlatesOnlyUpload(files) {
|
|
2905
|
+
const hasPlate = PLATE_FIELDS.some((option) => Boolean(files[option]));
|
|
2906
|
+
if (!hasPlate) {
|
|
2907
|
+
return false;
|
|
2908
|
+
}
|
|
2909
|
+
return Object.keys(files).every((option) => !files[option] || PLATE_FIELDS.includes(option) || NON_FILE_OPTIONS.includes(option));
|
|
2910
|
+
}
|
|
2911
|
+
function checkBundleCompleteness(files) {
|
|
2912
|
+
const uploadingFootage = FOOTAGE_FIELDS.some((option) => Boolean(files[option]));
|
|
2913
|
+
const missingFrom = (specs) => uploadingFootage ? specs.filter((spec) => !files[spec.option]).map((spec) => spec.file) : [];
|
|
2914
|
+
const plateWarnings = [];
|
|
2915
|
+
if (files.plateBackground && !files.plate) {
|
|
2916
|
+
plateWarnings.push("plate.background.png without plate.png — the background is the parallax layer OF a plate; pass --plate (or drop it in the --dir) too");
|
|
2917
|
+
}
|
|
2918
|
+
return {
|
|
2919
|
+
missingAdvisory: missingFrom(RERENDER_ADVISORY_FIELDS),
|
|
2920
|
+
missingContract: missingFrom(RERENDER_CONTRACT_FIELDS),
|
|
2921
|
+
plateWarnings,
|
|
2922
|
+
uploadingFootage
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
async function trackVideoCommand(idOrLogId, files, onProgress, options = {}) {
|
|
2926
|
+
const completeness = checkBundleCompleteness(files);
|
|
2927
|
+
if (completeness.uploadingFootage && completeness.missingContract.length > 0 && !options.allowPartial) {
|
|
2928
|
+
throw new CliError2("bundle_incomplete", `Refusing to upload a PARTIAL bundle: footage is being uploaded but the re-render contract is missing ${completeness.missingContract.join(", ")}. ` + `A footage-only upload leaves composition.tsx/props.json/render.json stale on R2 and desyncs the render.json from the DB ledger. ` + `Ship the complete bundle (re-run \`ship\` and upload with --dir), or pass --allow-partial for a deliberate partial refresh (e.g. poster-only).`);
|
|
2929
|
+
}
|
|
2930
|
+
if (completeness.uploadingFootage) {
|
|
2931
|
+
if (completeness.missingContract.length > 0) {
|
|
2932
|
+
onProgress?.(`warning: --allow-partial — uploading WITHOUT the re-render contract (${completeness.missingContract.join(", ")}); the R2 bundle will NOT be re-renderable`);
|
|
2933
|
+
}
|
|
2934
|
+
for (const missing of completeness.missingAdvisory) {
|
|
2935
|
+
onProgress?.(`warning: ${missing} missing (provenance/eval only) — shipping without it`);
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
for (const warning of completeness.plateWarnings) {
|
|
2939
|
+
onProgress?.(`warning: ${warning}`);
|
|
2940
|
+
}
|
|
2903
2941
|
const present = VIDEO_FIELDS.map((spec) => ({
|
|
2904
2942
|
field: spec.field,
|
|
2905
2943
|
path: files[spec.option]
|
|
2906
2944
|
})).filter((spec) => Boolean(spec.path));
|
|
2945
|
+
if (present.length === 0) {
|
|
2946
|
+
throw new CliError2("nothing_to_upload", "No bundle files resolved to upload (pass --dir <bundle> or explicit file flags).");
|
|
2947
|
+
}
|
|
2948
|
+
const platesOnly = isPlatesOnlyUpload(files);
|
|
2907
2949
|
const presign = await adminApiPost(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/video/uploads`, { fields: present.map((spec) => spec.field) });
|
|
2908
2950
|
const byField = new Map(presign.uploads.map((upload) => [upload.field, upload]));
|
|
2909
2951
|
const urls = {};
|
|
@@ -2924,6 +2966,10 @@ async function trackVideoCommand(idOrLogId, files, onProgress) {
|
|
|
2924
2966
|
}
|
|
2925
2967
|
urls[spec.field] = `${FOUND_BASE}/${upload.key}`;
|
|
2926
2968
|
}
|
|
2969
|
+
if (platesOnly) {
|
|
2970
|
+
onProgress?.(`plate pre-upload complete — compose against ${FOUND_BASE}/${presign.logId}/plate.png; finalize is deferred to the footage ship`);
|
|
2971
|
+
return { logId: presign.logId, ok: true, trackId: presign.trackId, urls };
|
|
2972
|
+
}
|
|
2927
2973
|
const manifest = files.render ? await readManifestFields(files.render) : {};
|
|
2928
2974
|
const videoModel = files.model?.trim().slice(0, 120) || manifest.model || DEFAULT_VIDEO_MODEL;
|
|
2929
2975
|
const videoModelReasoning = files.reasoning?.trim().slice(0, 120) || manifest.reasoning || DEFAULT_VIDEO_REASONING;
|
|
@@ -3033,7 +3079,7 @@ async function trackNoteCommand(idOrLogId, options) {
|
|
|
3033
3079
|
const body = { note: options.note };
|
|
3034
3080
|
return adminApiPost(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}/note`, body);
|
|
3035
3081
|
}
|
|
3036
|
-
var DEFAULT_VIDEO_MODEL = "anthropic/claude-opus-4-8", DEFAULT_VIDEO_REASONING = "high", FOUND_BASE = "https://found.fluncle.com", VIDEO_FIELDS;
|
|
3082
|
+
var DEFAULT_VIDEO_MODEL = "anthropic/claude-opus-4-8", DEFAULT_VIDEO_REASONING = "high", FOUND_BASE = "https://found.fluncle.com", VIDEO_FIELDS, RERENDER_CONTRACT_FIELDS, RERENDER_ADVISORY_FIELDS, FOOTAGE_FIELDS, PLATE_FIELDS, NON_FILE_OPTIONS;
|
|
3037
3083
|
var init_track = __esm(() => {
|
|
3038
3084
|
init_api();
|
|
3039
3085
|
init_output();
|
|
@@ -3045,13 +3091,35 @@ var init_track = __esm(() => {
|
|
|
3045
3091
|
{ field: "footage-landscape-social", option: "footageLandscapeSocial" },
|
|
3046
3092
|
{ field: "poster", option: "poster" },
|
|
3047
3093
|
{ field: "cover", option: "cover" },
|
|
3094
|
+
{ field: "plate", option: "plate" },
|
|
3095
|
+
{ field: "plate-background", option: "plateBackground" },
|
|
3048
3096
|
{ field: "note", option: "note" },
|
|
3049
3097
|
{ field: "composition", option: "composition" },
|
|
3050
3098
|
{ field: "props", option: "props" },
|
|
3051
3099
|
{ field: "render", option: "render" },
|
|
3052
3100
|
{ field: "intent", option: "intent" },
|
|
3053
|
-
{ field: "metrics", option: "metrics" }
|
|
3101
|
+
{ field: "metrics", option: "metrics" },
|
|
3102
|
+
{ field: "scene", option: "scene" }
|
|
3054
3103
|
];
|
|
3104
|
+
RERENDER_CONTRACT_FIELDS = [
|
|
3105
|
+
{ file: "composition.tsx", option: "composition" },
|
|
3106
|
+
{ file: "props.json", option: "props" },
|
|
3107
|
+
{ file: "render.json", option: "render" }
|
|
3108
|
+
];
|
|
3109
|
+
RERENDER_ADVISORY_FIELDS = [
|
|
3110
|
+
{ file: "intent.json", option: "intent" },
|
|
3111
|
+
{ file: "metrics.json", option: "metrics" },
|
|
3112
|
+
{ file: "scene.json", option: "scene" }
|
|
3113
|
+
];
|
|
3114
|
+
FOOTAGE_FIELDS = [
|
|
3115
|
+
"footage",
|
|
3116
|
+
"footageSocial",
|
|
3117
|
+
"footageNotext",
|
|
3118
|
+
"footageLandscape",
|
|
3119
|
+
"footageLandscapeSocial"
|
|
3120
|
+
];
|
|
3121
|
+
PLATE_FIELDS = ["plate", "plateBackground"];
|
|
3122
|
+
NON_FILE_OPTIONS = ["model", "reasoning"];
|
|
3055
3123
|
});
|
|
3056
3124
|
|
|
3057
3125
|
// src/commands/add.ts
|
|
@@ -7229,7 +7297,7 @@ function addAdminCommands(program2) {
|
|
|
7229
7297
|
const { trackUpdateCommand: trackUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7230
7298
|
await runTrackUpdate(trackId, options, trackUpdateCommand2);
|
|
7231
7299
|
});
|
|
7232
|
-
adminTrack.command("video").description("Upload a track's video bundle to R2 and link it").argument("[idOrLogId]").option("--composition <file>", "Composition source file").option("--cover <file>", "Cover image").option("--dir <dir>", "Bundle directory").option("--footage <file>", "Video footage (square crop source)").option("--footage-landscape <file>", "Clean landscape cut (optional escape hatch)").option("--footage-landscape-social <file>", "Landscape cut with baked text (optional)").option("--footage-notext <file>", "Portrait cut without the type layer (optional)").option("--footage-social <file>", "Portrait social cut (baked text)").option("--intent <file>", "Render-intent JSON (optional)").option("--json", "Print JSON", false).option("--metrics <file>", "Gate metrics JSON (optional)").option("--model <model>", "Authoring AI model (<provider>/<model>)").option("--note <file>", "Note file").option("--poster <file>", "Poster image").option("--props <file>", "Render props JSON").option("--reasoning <level>", "Authoring model reasoning effort (e.g. high)").option("--render <file>", "Render metadata JSON").allowExcessArguments().action(async (idOrLogId, options) => {
|
|
7300
|
+
adminTrack.command("video").description("Upload a track's video bundle to R2 and link it").argument("[idOrLogId]").option("--allow-partial", "Allow an intentionally partial upload (skip the re-render-contract check; e.g. poster-only)", false).option("--composition <file>", "Composition source file").option("--cover <file>", "Cover image").option("--dir <dir>", "Bundle directory").option("--footage <file>", "Video footage (square crop source)").option("--footage-landscape <file>", "Clean landscape cut (optional escape hatch)").option("--footage-landscape-social <file>", "Landscape cut with baked text (optional)").option("--footage-notext <file>", "Portrait cut without the type layer (optional)").option("--footage-social <file>", "Portrait social cut (baked text)").option("--intent <file>", "Render-intent JSON (optional)").option("--json", "Print JSON", false).option("--metrics <file>", "Gate metrics JSON (optional)").option("--model <model>", "Authoring AI model (<provider>/<model>)").option("--note <file>", "Note file").option("--plate <file>", "Plate-lane photographic plate (plate.png; uploadable pre-render)").option("--plate-background <file>", "The plate's subject-removed background (plate.background.png, optional)").option("--poster <file>", "Poster image").option("--props <file>", "Render props JSON").option("--reasoning <level>", "Authoring model reasoning effort (e.g. high)").option("--render <file>", "Render metadata JSON").option("--scene <file>", "Scene replay manifest JSON (fluncle.scene/1, optional)").allowExcessArguments().action(async (idOrLogId, options) => {
|
|
7233
7301
|
const { trackVideoCommand: trackVideoCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7234
7302
|
await runTrackVideo(idOrLogId, options, trackVideoCommand2);
|
|
7235
7303
|
});
|
|
@@ -7654,7 +7722,7 @@ async function runBackfillDiscogs(options, backfillDiscogsCommand2) {
|
|
|
7654
7722
|
}
|
|
7655
7723
|
async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
7656
7724
|
if (!idOrLogId) {
|
|
7657
|
-
throw new Error("Missing id. Usage: fluncle admin tracks video <track_id|log_id> (--dir <dir> | --footage <file> [--footage-social <file>] [--footage-notext <file>] [--footage-landscape <file>] [--footage-landscape-social <file>] [--poster <file>] [--cover <file>] [--note <file>] [--composition <file>] [--props <file>] [--render <file>] [--intent <file>] [--metrics <file>])");
|
|
7725
|
+
throw new Error("Missing id. Usage: fluncle admin tracks video <track_id|log_id> (--dir <dir> | --footage <file> [--footage-social <file>] [--footage-notext <file>] [--footage-landscape <file>] [--footage-landscape-social <file>] [--poster <file>] [--cover <file>] [--note <file>] [--composition <file>] [--props <file>] [--render <file>] [--intent <file>] [--metrics <file>] [--scene <file>] | --plate <file> [--plate-background <file>]) [--allow-partial]");
|
|
7658
7726
|
}
|
|
7659
7727
|
const dir = options.dir ? path2.resolve(process.cwd(), options.dir) : undefined;
|
|
7660
7728
|
const fromDir = (name) => {
|
|
@@ -7682,16 +7750,22 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
7682
7750
|
metrics: resolveFile(options.metrics, "metrics.json"),
|
|
7683
7751
|
model: options.model,
|
|
7684
7752
|
note: resolveFile(options.note, "note.txt"),
|
|
7753
|
+
plate: resolveFile(options.plate, "plate.png"),
|
|
7754
|
+
plateBackground: resolveFile(options.plateBackground, "plate.background.png"),
|
|
7685
7755
|
poster: resolveFile(options.poster, "poster.jpg"),
|
|
7686
7756
|
props: resolveFile(options.props, "props.json"),
|
|
7687
7757
|
reasoning: options.reasoning,
|
|
7688
|
-
render: resolveFile(options.render, "render.json")
|
|
7758
|
+
render: resolveFile(options.render, "render.json"),
|
|
7759
|
+
scene: resolveFile(options.scene, "scene.json")
|
|
7689
7760
|
};
|
|
7690
|
-
|
|
7691
|
-
|
|
7761
|
+
const { isPlatesOnlyUpload: isPlatesOnlyUpload2 } = await Promise.resolve().then(() => (init_track(), exports_track));
|
|
7762
|
+
if (!files.footage && !options.allowPartial && !isPlatesOnlyUpload2(files)) {
|
|
7763
|
+
throw new Error("A footage cut is required (--footage <file>, or --dir containing footage.mp4). Pass --allow-partial for a deliberate partial refresh (e.g. poster-only), or upload plates alone (--plate/--plate-background) for the plate-lane pre-upload.");
|
|
7692
7764
|
}
|
|
7693
7765
|
const onProgress = options.json ? undefined : (message) => console.log(message);
|
|
7694
|
-
const result = await trackVideoCommand2(idOrLogId, files, onProgress
|
|
7766
|
+
const result = await trackVideoCommand2(idOrLogId, files, onProgress, {
|
|
7767
|
+
allowPartial: options.allowPartial
|
|
7768
|
+
});
|
|
7695
7769
|
if (options.json) {
|
|
7696
7770
|
printJson(result);
|
|
7697
7771
|
return;
|
|
@@ -8485,12 +8559,15 @@ var stringOptions = new Set([
|
|
|
8485
8559
|
"--metrics",
|
|
8486
8560
|
"--mime",
|
|
8487
8561
|
"--note",
|
|
8562
|
+
"--plate",
|
|
8563
|
+
"--plate-background",
|
|
8488
8564
|
"--platform",
|
|
8489
8565
|
"--poster",
|
|
8490
8566
|
"--props",
|
|
8491
8567
|
"--query",
|
|
8492
8568
|
"--recorded-at",
|
|
8493
8569
|
"--render",
|
|
8570
|
+
"--scene",
|
|
8494
8571
|
"--scheduled-for",
|
|
8495
8572
|
"--soundcloud-url",
|
|
8496
8573
|
"--source",
|
package/package.json
CHANGED