fluncle 0.93.0 → 0.94.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 +263 -29
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -556,7 +556,7 @@ function parseVersion(version) {
|
|
|
556
556
|
var currentVersion;
|
|
557
557
|
var init_version = __esm(() => {
|
|
558
558
|
init_output();
|
|
559
|
-
currentVersion = "0.
|
|
559
|
+
currentVersion = "0.94.0".trim() ? "0.94.0".trim() : "0.1.0";
|
|
560
560
|
});
|
|
561
561
|
|
|
562
562
|
// src/update-notifier.ts
|
|
@@ -2186,7 +2186,7 @@ function parseVersion2(version) {
|
|
|
2186
2186
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2187
2187
|
var init_version2 = __esm(() => {
|
|
2188
2188
|
init_output();
|
|
2189
|
-
currentVersion2 = "0.
|
|
2189
|
+
currentVersion2 = "0.94.0".trim() ? "0.94.0".trim() : "0.1.0";
|
|
2190
2190
|
});
|
|
2191
2191
|
|
|
2192
2192
|
// ../../packages/registry/src/index.ts
|
|
@@ -3144,6 +3144,224 @@ var init_track = __esm(() => {
|
|
|
3144
3144
|
NON_FILE_OPTIONS = ["model", "reasoning"];
|
|
3145
3145
|
});
|
|
3146
3146
|
|
|
3147
|
+
// src/commands/helm.ts
|
|
3148
|
+
var exports_helm = {};
|
|
3149
|
+
__export(exports_helm, {
|
|
3150
|
+
resolveHelmDir: () => resolveHelmDir,
|
|
3151
|
+
helmUninstallCommand: () => helmUninstallCommand,
|
|
3152
|
+
helmOpenCommand: () => helmOpenCommand,
|
|
3153
|
+
helmInstallCommand: () => helmInstallCommand,
|
|
3154
|
+
buildLaunchAgentPlist: () => buildLaunchAgentPlist
|
|
3155
|
+
});
|
|
3156
|
+
import { existsSync, mkdirSync as mkdirSync2, openSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3157
|
+
import { homedir as homedir4 } from "node:os";
|
|
3158
|
+
import { dirname as dirname3, join as join4, resolve } from "node:path";
|
|
3159
|
+
function launchAgentPlistPath() {
|
|
3160
|
+
return join4(homedir4(), "Library/LaunchAgents", `${LAUNCH_AGENT_LABEL}.plist`);
|
|
3161
|
+
}
|
|
3162
|
+
function helmLogPath() {
|
|
3163
|
+
return join4(homedir4(), "Library/Logs/fluncle-helm.log");
|
|
3164
|
+
}
|
|
3165
|
+
function resolveHelmDir() {
|
|
3166
|
+
const fromEnv = process.env.FLUNCLE_HELM_DIR;
|
|
3167
|
+
if (fromEnv) {
|
|
3168
|
+
if (!existsSync(join4(fromEnv, "src/server.ts"))) {
|
|
3169
|
+
throw new CliError2("helm_not_found", `FLUNCLE_HELM_DIR points at ${fromEnv}, but there's no helm there (src/server.ts missing).`);
|
|
3170
|
+
}
|
|
3171
|
+
return resolve(fromEnv);
|
|
3172
|
+
}
|
|
3173
|
+
let dir = import.meta.dir;
|
|
3174
|
+
for (let depth = 0;depth < 8; depth++) {
|
|
3175
|
+
const candidate = join4(dir, "apps/helm");
|
|
3176
|
+
if (existsSync(join4(candidate, "src/server.ts"))) {
|
|
3177
|
+
return candidate;
|
|
3178
|
+
}
|
|
3179
|
+
const parent = dirname3(dir);
|
|
3180
|
+
if (parent === dir) {
|
|
3181
|
+
break;
|
|
3182
|
+
}
|
|
3183
|
+
dir = parent;
|
|
3184
|
+
}
|
|
3185
|
+
throw new CliError2("helm_not_found", "No helm aboard. Run this from the fluncle repo, or set FLUNCLE_HELM_DIR to the apps/helm checkout.");
|
|
3186
|
+
}
|
|
3187
|
+
function resolveBun() {
|
|
3188
|
+
const bun = Bun.which("bun");
|
|
3189
|
+
if (!bun) {
|
|
3190
|
+
throw new CliError2("no_bun", "No bun on the PATH. The helm daemon runs under bun.");
|
|
3191
|
+
}
|
|
3192
|
+
return bun;
|
|
3193
|
+
}
|
|
3194
|
+
async function daemonHealthy() {
|
|
3195
|
+
try {
|
|
3196
|
+
const response = await fetch(`${HELM_URL}/api/health`, {
|
|
3197
|
+
signal: AbortSignal.timeout(1500)
|
|
3198
|
+
});
|
|
3199
|
+
return response.ok;
|
|
3200
|
+
} catch {
|
|
3201
|
+
return false;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
async function ensureBuilt(helmDir) {
|
|
3205
|
+
if (existsSync(join4(helmDir, "dist/index.html"))) {
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
console.log("The glass isn't built yet. Building it once…");
|
|
3209
|
+
const proc = Bun.spawn([resolveBun(), "run", "build"], {
|
|
3210
|
+
cwd: helmDir,
|
|
3211
|
+
stderr: "inherit",
|
|
3212
|
+
stdin: "ignore",
|
|
3213
|
+
stdout: "inherit"
|
|
3214
|
+
});
|
|
3215
|
+
const exitCode = await proc.exited;
|
|
3216
|
+
if (exitCode !== 0) {
|
|
3217
|
+
throw new CliError2("helm_build_failed", `The glass build failed (exit ${exitCode}).`);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
function bootDaemon(helmDir) {
|
|
3221
|
+
const logPath = helmLogPath();
|
|
3222
|
+
mkdirSync2(dirname3(logPath), { recursive: true });
|
|
3223
|
+
const logFd = openSync(logPath, "a");
|
|
3224
|
+
const proc = Bun.spawn([resolveBun(), "src/server.ts"], {
|
|
3225
|
+
cwd: helmDir,
|
|
3226
|
+
stderr: logFd,
|
|
3227
|
+
stdin: "ignore",
|
|
3228
|
+
stdout: logFd
|
|
3229
|
+
});
|
|
3230
|
+
proc.unref();
|
|
3231
|
+
}
|
|
3232
|
+
async function waitForHealth(timeoutMs) {
|
|
3233
|
+
const deadline = Date.now() + timeoutMs;
|
|
3234
|
+
while (Date.now() < deadline) {
|
|
3235
|
+
if (await daemonHealthy()) {
|
|
3236
|
+
return true;
|
|
3237
|
+
}
|
|
3238
|
+
await Bun.sleep(250);
|
|
3239
|
+
}
|
|
3240
|
+
return false;
|
|
3241
|
+
}
|
|
3242
|
+
async function openAppWindow() {
|
|
3243
|
+
if (process.platform === "darwin" && chromeInstalled()) {
|
|
3244
|
+
const proc = Bun.spawn(["open", "-na", "Google Chrome", "--args", `--app=${HELM_URL}`], {
|
|
3245
|
+
stderr: "ignore",
|
|
3246
|
+
stdin: "ignore",
|
|
3247
|
+
stdout: "ignore"
|
|
3248
|
+
});
|
|
3249
|
+
if (await proc.exited === 0) {
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
await openExternal(HELM_URL);
|
|
3254
|
+
}
|
|
3255
|
+
function chromeInstalled() {
|
|
3256
|
+
return existsSync("/Applications/Google Chrome.app") || existsSync(join4(homedir4(), "Applications/Google Chrome.app"));
|
|
3257
|
+
}
|
|
3258
|
+
async function helmOpenCommand() {
|
|
3259
|
+
if (await daemonHealthy()) {
|
|
3260
|
+
console.log(`The helm holds on :${HELM_PORT}. Opening the window.`);
|
|
3261
|
+
await openAppWindow();
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
const helmDir = resolveHelmDir();
|
|
3265
|
+
await ensureBuilt(helmDir);
|
|
3266
|
+
bootDaemon(helmDir);
|
|
3267
|
+
if (!await waitForHealth(15000)) {
|
|
3268
|
+
throw new CliError2("helm_no_answer", `The daemon didn't answer on :${HELM_PORT}. Its log is at ${helmLogPath()}.`);
|
|
3269
|
+
}
|
|
3270
|
+
console.log(`Helm raised on :${HELM_PORT}. Opening the window.`);
|
|
3271
|
+
await openAppWindow();
|
|
3272
|
+
}
|
|
3273
|
+
function xmlEscape(text) {
|
|
3274
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3275
|
+
}
|
|
3276
|
+
function buildLaunchAgentPlist(helmDir, bunPath, logPath) {
|
|
3277
|
+
const path2 = `${dirname3(bunPath)}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`;
|
|
3278
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
3279
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3280
|
+
<plist version="1.0">
|
|
3281
|
+
<dict>
|
|
3282
|
+
<key>EnvironmentVariables</key>
|
|
3283
|
+
<dict>
|
|
3284
|
+
<key>PATH</key>
|
|
3285
|
+
<string>${xmlEscape(path2)}</string>
|
|
3286
|
+
</dict>
|
|
3287
|
+
<key>KeepAlive</key>
|
|
3288
|
+
<dict>
|
|
3289
|
+
<key>SuccessfulExit</key>
|
|
3290
|
+
<false/>
|
|
3291
|
+
</dict>
|
|
3292
|
+
<key>Label</key>
|
|
3293
|
+
<string>${LAUNCH_AGENT_LABEL}</string>
|
|
3294
|
+
<key>ProgramArguments</key>
|
|
3295
|
+
<array>
|
|
3296
|
+
<string>${xmlEscape(bunPath)}</string>
|
|
3297
|
+
<string>src/server.ts</string>
|
|
3298
|
+
</array>
|
|
3299
|
+
<key>RunAtLoad</key>
|
|
3300
|
+
<true/>
|
|
3301
|
+
<key>StandardErrorPath</key>
|
|
3302
|
+
<string>${xmlEscape(logPath)}</string>
|
|
3303
|
+
<key>StandardOutPath</key>
|
|
3304
|
+
<string>${xmlEscape(logPath)}</string>
|
|
3305
|
+
<key>WorkingDirectory</key>
|
|
3306
|
+
<string>${xmlEscape(helmDir)}</string>
|
|
3307
|
+
</dict>
|
|
3308
|
+
</plist>
|
|
3309
|
+
`;
|
|
3310
|
+
}
|
|
3311
|
+
function currentUid() {
|
|
3312
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
3313
|
+
if (uid === undefined) {
|
|
3314
|
+
throw new CliError2("no_uid", "Couldn't read the user id — launchd needs a gui domain.");
|
|
3315
|
+
}
|
|
3316
|
+
return uid;
|
|
3317
|
+
}
|
|
3318
|
+
async function launchctl(args) {
|
|
3319
|
+
const proc = Bun.spawn(["launchctl", ...args], {
|
|
3320
|
+
stderr: "ignore",
|
|
3321
|
+
stdin: "ignore",
|
|
3322
|
+
stdout: "ignore"
|
|
3323
|
+
});
|
|
3324
|
+
return proc.exited;
|
|
3325
|
+
}
|
|
3326
|
+
async function helmInstallCommand() {
|
|
3327
|
+
if (process.platform !== "darwin") {
|
|
3328
|
+
throw new CliError2("unsupported_platform", "launchd is a macOS thing. Nothing to install here.");
|
|
3329
|
+
}
|
|
3330
|
+
const helmDir = resolveHelmDir();
|
|
3331
|
+
await ensureBuilt(helmDir);
|
|
3332
|
+
const plistPath = launchAgentPlistPath();
|
|
3333
|
+
mkdirSync2(dirname3(plistPath), { recursive: true });
|
|
3334
|
+
mkdirSync2(dirname3(helmLogPath()), { recursive: true });
|
|
3335
|
+
writeFileSync2(plistPath, buildLaunchAgentPlist(helmDir, resolveBun(), helmLogPath()));
|
|
3336
|
+
const uid = currentUid();
|
|
3337
|
+
await launchctl(["bootout", `gui/${uid}/${LAUNCH_AGENT_LABEL}`]);
|
|
3338
|
+
const exitCode = await launchctl(["bootstrap", `gui/${uid}`, plistPath]);
|
|
3339
|
+
if (exitCode !== 0) {
|
|
3340
|
+
throw new CliError2("launchctl_refused", `launchctl bootstrap refused (exit ${exitCode}). The plist is at ${plistPath}.`);
|
|
3341
|
+
}
|
|
3342
|
+
console.log(`LaunchAgent installed (${LAUNCH_AGENT_LABEL}).`);
|
|
3343
|
+
console.log(`The daemon rises at login and holds :${HELM_PORT}. Log: ${helmLogPath()}`);
|
|
3344
|
+
}
|
|
3345
|
+
async function helmUninstallCommand() {
|
|
3346
|
+
if (process.platform !== "darwin") {
|
|
3347
|
+
throw new CliError2("unsupported_platform", "launchd is a macOS thing. Nothing to remove here.");
|
|
3348
|
+
}
|
|
3349
|
+
const plistPath = launchAgentPlistPath();
|
|
3350
|
+
await launchctl(["bootout", `gui/${currentUid()}/${LAUNCH_AGENT_LABEL}`]);
|
|
3351
|
+
if (existsSync(plistPath)) {
|
|
3352
|
+
rmSync2(plistPath);
|
|
3353
|
+
console.log("LaunchAgent removed and the daemon stood down.");
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
console.log("No LaunchAgent on file. Nothing stood down.");
|
|
3357
|
+
}
|
|
3358
|
+
var HELM_PORT = 4190, HELM_URL, LAUNCH_AGENT_LABEL = "com.fluncle.helm";
|
|
3359
|
+
var init_helm = __esm(() => {
|
|
3360
|
+
init_open_external();
|
|
3361
|
+
init_output();
|
|
3362
|
+
HELM_URL = `http://127.0.0.1:${HELM_PORT}`;
|
|
3363
|
+
});
|
|
3364
|
+
|
|
3147
3365
|
// src/commands/add.ts
|
|
3148
3366
|
var exports_add = {};
|
|
3149
3367
|
__export(exports_add, {
|
|
@@ -3675,9 +3893,9 @@ var init_mixtape_youtube2 = __esm(() => {
|
|
|
3675
3893
|
|
|
3676
3894
|
// src/commands/mixtape-set-video.ts
|
|
3677
3895
|
import { randomUUID } from "node:crypto";
|
|
3678
|
-
import { existsSync, rmSync as
|
|
3896
|
+
import { existsSync as existsSync2, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
|
|
3679
3897
|
import { tmpdir } from "node:os";
|
|
3680
|
-
import { join as
|
|
3898
|
+
import { join as join5 } from "node:path";
|
|
3681
3899
|
function planMultipart(contentLength, partSize = DEFAULT_PART_SIZE) {
|
|
3682
3900
|
if (!Number.isInteger(contentLength) || contentLength <= 0) {
|
|
3683
3901
|
throw new CliError2("invalid_size", `The rendition size must be a positive integer (got ${contentLength})`);
|
|
@@ -3732,11 +3950,11 @@ function renditionFfmpegArgs(inputPath, outputPath) {
|
|
|
3732
3950
|
];
|
|
3733
3951
|
}
|
|
3734
3952
|
async function uploadRenditionMultipart(masterPath, presignPath, onProgress = () => {}) {
|
|
3735
|
-
if (!
|
|
3953
|
+
if (!existsSync2(masterPath)) {
|
|
3736
3954
|
throw new CliError2("file_not_found", `Set-video master not found: ${masterPath}`);
|
|
3737
3955
|
}
|
|
3738
3956
|
await assertFfmpeg();
|
|
3739
|
-
const renditionPath =
|
|
3957
|
+
const renditionPath = join5(tmpdir(), `fluncle-set-${randomUUID()}.mp4`);
|
|
3740
3958
|
onProgress("Set video: deriving the 1080p faststart rendition (ffmpeg)…");
|
|
3741
3959
|
await deriveRendition(masterPath, renditionPath);
|
|
3742
3960
|
try {
|
|
@@ -3765,7 +3983,7 @@ async function uploadRenditionMultipart(masterPath, presignPath, onProgress = ()
|
|
|
3765
3983
|
}
|
|
3766
3984
|
return { key: presign.key, url: `${FOUND_BASE2}/${presign.key}` };
|
|
3767
3985
|
} finally {
|
|
3768
|
-
|
|
3986
|
+
rmSync3(renditionPath, { force: true });
|
|
3769
3987
|
}
|
|
3770
3988
|
}
|
|
3771
3989
|
async function putPart(url, path2, part, onProgress = () => {}) {
|
|
@@ -3782,7 +4000,7 @@ async function putPart(url, path2, part, onProgress = () => {}) {
|
|
|
3782
4000
|
}
|
|
3783
4001
|
const backoffMs = 500 * 2 ** (attempt - 1);
|
|
3784
4002
|
onProgress(`Set video: part ${part.partNumber} dropped — retry ${attempt}/${MAX_PART_ATTEMPTS - 1} in ${backoffMs}ms…`);
|
|
3785
|
-
await new Promise((
|
|
4003
|
+
await new Promise((resolve2) => setTimeout(resolve2, backoffMs));
|
|
3786
4004
|
}
|
|
3787
4005
|
}
|
|
3788
4006
|
}
|
|
@@ -3863,7 +4081,7 @@ __export(exports_recordings, {
|
|
|
3863
4081
|
recordingDeleteCommand: () => recordingDeleteCommand,
|
|
3864
4082
|
recordingCreateCommand: () => recordingCreateCommand
|
|
3865
4083
|
});
|
|
3866
|
-
import { existsSync as
|
|
4084
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
3867
4085
|
async function recordingGet(id) {
|
|
3868
4086
|
const response = await adminApiGet(`/api/admin/recordings/${encodeURIComponent(id)}`);
|
|
3869
4087
|
return response.recording;
|
|
@@ -3932,7 +4150,7 @@ async function recordingCreateCommand(options = {}) {
|
|
|
3932
4150
|
if (!options.video) {
|
|
3933
4151
|
throw new CliError2("missing_video", "A recording needs a --video <file> to stage");
|
|
3934
4152
|
}
|
|
3935
|
-
if (!
|
|
4153
|
+
if (!existsSync3(options.video)) {
|
|
3936
4154
|
throw new CliError2("file_not_found", `Set-video master not found: ${options.video}`);
|
|
3937
4155
|
}
|
|
3938
4156
|
const created = await adminApiPost("/api/admin/recordings", {
|
|
@@ -3968,7 +4186,7 @@ async function recordingUpdateCommand(id, options = {}) {
|
|
|
3968
4186
|
body.parentId = options.parentId;
|
|
3969
4187
|
}
|
|
3970
4188
|
if (options.tracklistFile !== undefined) {
|
|
3971
|
-
if (!
|
|
4189
|
+
if (!existsSync3(options.tracklistFile)) {
|
|
3972
4190
|
throw new CliError2("file_not_found", `Tracklist file not found: ${options.tracklistFile}`);
|
|
3973
4191
|
}
|
|
3974
4192
|
try {
|
|
@@ -4017,7 +4235,7 @@ async function recordingReplaceCuesCommand(id, options = {}) {
|
|
|
4017
4235
|
if (!options.cuesFile) {
|
|
4018
4236
|
throw new CliError2("missing_cues_file", "replace-cues needs a --cues-file <file> (the ordered cue array)");
|
|
4019
4237
|
}
|
|
4020
|
-
if (!
|
|
4238
|
+
if (!existsSync3(options.cuesFile)) {
|
|
4021
4239
|
throw new CliError2("file_not_found", `Cues file not found: ${options.cuesFile}`);
|
|
4022
4240
|
}
|
|
4023
4241
|
let cues;
|
|
@@ -4057,9 +4275,9 @@ __export(exports_clips, {
|
|
|
4057
4275
|
CLIP_AUDIO_BITRATE: () => CLIP_AUDIO_BITRATE
|
|
4058
4276
|
});
|
|
4059
4277
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
4060
|
-
import { rmSync as
|
|
4278
|
+
import { rmSync as rmSync4, statSync as statSync4 } from "node:fs";
|
|
4061
4279
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4062
|
-
import { join as
|
|
4280
|
+
import { join as join6 } from "node:path";
|
|
4063
4281
|
function clipFootageKey(clipId) {
|
|
4064
4282
|
return `${clipId}/footage.mp4`;
|
|
4065
4283
|
}
|
|
@@ -4142,7 +4360,7 @@ async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
|
4142
4360
|
}
|
|
4143
4361
|
const source = await resolveClipSource(clip);
|
|
4144
4362
|
await assertFfmpeg2();
|
|
4145
|
-
const outputPath =
|
|
4363
|
+
const outputPath = join6(tmpdir2(), `fluncle-clip-${randomUUID2()}.mp4`);
|
|
4146
4364
|
try {
|
|
4147
4365
|
onProgress(`Clip ${clipId}: cutting [${clip.inMs}–${clip.outMs}ms]…`);
|
|
4148
4366
|
await runClipCut({
|
|
@@ -4168,7 +4386,7 @@ async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
|
4168
4386
|
url: `${FOUND_BASE3}/${presign.key}`
|
|
4169
4387
|
};
|
|
4170
4388
|
} finally {
|
|
4171
|
-
|
|
4389
|
+
rmSync4(outputPath, { force: true });
|
|
4172
4390
|
}
|
|
4173
4391
|
}
|
|
4174
4392
|
async function putClip(url, contentType, path2) {
|
|
@@ -4226,7 +4444,7 @@ __export(exports_recordings2, {
|
|
|
4226
4444
|
recordingDeleteCommand: () => recordingDeleteCommand2,
|
|
4227
4445
|
recordingCreateCommand: () => recordingCreateCommand2
|
|
4228
4446
|
});
|
|
4229
|
-
import { existsSync as
|
|
4447
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
4230
4448
|
async function recordingGet2(id) {
|
|
4231
4449
|
const response = await adminApiGet(`/api/admin/recordings/${encodeURIComponent(id)}`);
|
|
4232
4450
|
return response.recording;
|
|
@@ -4295,7 +4513,7 @@ async function recordingCreateCommand2(options = {}) {
|
|
|
4295
4513
|
if (!options.video) {
|
|
4296
4514
|
throw new CliError2("missing_video", "A recording needs a --video <file> to stage");
|
|
4297
4515
|
}
|
|
4298
|
-
if (!
|
|
4516
|
+
if (!existsSync4(options.video)) {
|
|
4299
4517
|
throw new CliError2("file_not_found", `Set-video master not found: ${options.video}`);
|
|
4300
4518
|
}
|
|
4301
4519
|
const created = await adminApiPost("/api/admin/recordings", {
|
|
@@ -4331,7 +4549,7 @@ async function recordingUpdateCommand2(id, options = {}) {
|
|
|
4331
4549
|
body.parentId = options.parentId;
|
|
4332
4550
|
}
|
|
4333
4551
|
if (options.tracklistFile !== undefined) {
|
|
4334
|
-
if (!
|
|
4552
|
+
if (!existsSync4(options.tracklistFile)) {
|
|
4335
4553
|
throw new CliError2("file_not_found", `Tracklist file not found: ${options.tracklistFile}`);
|
|
4336
4554
|
}
|
|
4337
4555
|
try {
|
|
@@ -4380,7 +4598,7 @@ async function recordingReplaceCuesCommand2(id, options = {}) {
|
|
|
4380
4598
|
if (!options.cuesFile) {
|
|
4381
4599
|
throw new CliError2("missing_cues_file", "replace-cues needs a --cues-file <file> (the ordered cue array)");
|
|
4382
4600
|
}
|
|
4383
|
-
if (!
|
|
4601
|
+
if (!existsSync4(options.cuesFile)) {
|
|
4384
4602
|
throw new CliError2("file_not_found", `Cues file not found: ${options.cuesFile}`);
|
|
4385
4603
|
}
|
|
4386
4604
|
let cues;
|
|
@@ -4411,7 +4629,7 @@ __export(exports_newsletter, {
|
|
|
4411
4629
|
newsletterDraftCommand: () => newsletterDraftCommand,
|
|
4412
4630
|
newsletterDeleteCommand: () => newsletterDeleteCommand
|
|
4413
4631
|
});
|
|
4414
|
-
import { existsSync as
|
|
4632
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
4415
4633
|
function buildBody2(options, { requireContent }) {
|
|
4416
4634
|
const body = {};
|
|
4417
4635
|
if (options.contentFile !== undefined) {
|
|
@@ -4431,7 +4649,7 @@ function buildBody2(options, { requireContent }) {
|
|
|
4431
4649
|
return body;
|
|
4432
4650
|
}
|
|
4433
4651
|
function readContentFile(filePath) {
|
|
4434
|
-
if (!
|
|
4652
|
+
if (!existsSync5(filePath)) {
|
|
4435
4653
|
throw new CliError2("file_not_found", `Content file not found: ${filePath}`);
|
|
4436
4654
|
}
|
|
4437
4655
|
const text = readFileSync4(filePath, "utf-8");
|
|
@@ -4823,7 +5041,7 @@ async function selectWithKeyboard2(items, options) {
|
|
|
4823
5041
|
let renderedLines = 0;
|
|
4824
5042
|
let done = false;
|
|
4825
5043
|
const wasRaw = stdin.isRaw === true;
|
|
4826
|
-
return await new Promise((
|
|
5044
|
+
return await new Promise((resolve2) => {
|
|
4827
5045
|
function cleanup() {
|
|
4828
5046
|
if (done) {
|
|
4829
5047
|
return;
|
|
@@ -4838,14 +5056,14 @@ async function selectWithKeyboard2(items, options) {
|
|
|
4838
5056
|
cleanup();
|
|
4839
5057
|
stdout.write(renderedLines > 0 ? `
|
|
4840
5058
|
` : "");
|
|
4841
|
-
|
|
5059
|
+
resolve2(item);
|
|
4842
5060
|
}
|
|
4843
5061
|
function cancel() {
|
|
4844
5062
|
cleanup();
|
|
4845
5063
|
clearRendered2(stdout, renderedLines);
|
|
4846
5064
|
stdout.write(`Cancelled.
|
|
4847
5065
|
`);
|
|
4848
|
-
|
|
5066
|
+
resolve2(undefined);
|
|
4849
5067
|
}
|
|
4850
5068
|
function render() {
|
|
4851
5069
|
clearRendered2(stdout, renderedLines);
|
|
@@ -4901,7 +5119,7 @@ async function paginateWithKeyboard(options) {
|
|
|
4901
5119
|
let busy = false;
|
|
4902
5120
|
let done = false;
|
|
4903
5121
|
const wasRaw = stdin.isRaw === true;
|
|
4904
|
-
return await new Promise((
|
|
5122
|
+
return await new Promise((resolve2) => {
|
|
4905
5123
|
function cleanup() {
|
|
4906
5124
|
if (done) {
|
|
4907
5125
|
return;
|
|
@@ -4916,7 +5134,7 @@ async function paginateWithKeyboard(options) {
|
|
|
4916
5134
|
cleanup();
|
|
4917
5135
|
stdout.write(`
|
|
4918
5136
|
`);
|
|
4919
|
-
|
|
5137
|
+
resolve2();
|
|
4920
5138
|
}
|
|
4921
5139
|
function render() {
|
|
4922
5140
|
clearRendered2(stdout, renderedLines);
|
|
@@ -5069,7 +5287,7 @@ var init_format2 = __esm(() => {
|
|
|
5069
5287
|
});
|
|
5070
5288
|
|
|
5071
5289
|
// src/cli.ts
|
|
5072
|
-
import { existsSync as
|
|
5290
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
5073
5291
|
import path2 from "path";
|
|
5074
5292
|
|
|
5075
5293
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -7215,6 +7433,7 @@ ${fluncleAsciiLogo}
|
|
|
7215
7433
|
addMetaCommands(program2);
|
|
7216
7434
|
addTrackCommands(program2);
|
|
7217
7435
|
addAdminCommands(program2);
|
|
7436
|
+
addHelmCommands(program2);
|
|
7218
7437
|
return program2;
|
|
7219
7438
|
}
|
|
7220
7439
|
async function main(args = process.argv.slice(2)) {
|
|
@@ -7313,6 +7532,21 @@ function addTrackCommands(program2) {
|
|
|
7313
7532
|
await runTrackGet(idOrLogId, options, trackGetCommand2);
|
|
7314
7533
|
});
|
|
7315
7534
|
}
|
|
7535
|
+
function addHelmCommands(program2) {
|
|
7536
|
+
const helm = configureCommand(program2.command("helm", { hidden: true }).description("Fluncle's Helm \u2014 the operator's mission control"));
|
|
7537
|
+
helm.action(async () => {
|
|
7538
|
+
const { helmOpenCommand: helmOpenCommand2 } = await Promise.resolve().then(() => (init_helm(), exports_helm));
|
|
7539
|
+
await helmOpenCommand2();
|
|
7540
|
+
});
|
|
7541
|
+
helm.command("install").description("Install the launchd LaunchAgent (the daemon rises at login)").action(async () => {
|
|
7542
|
+
const { helmInstallCommand: helmInstallCommand2 } = await Promise.resolve().then(() => (init_helm(), exports_helm));
|
|
7543
|
+
await helmInstallCommand2();
|
|
7544
|
+
});
|
|
7545
|
+
helm.command("uninstall").description("Remove the LaunchAgent and stand the daemon down").action(async () => {
|
|
7546
|
+
const { helmUninstallCommand: helmUninstallCommand2 } = await Promise.resolve().then(() => (init_helm(), exports_helm));
|
|
7547
|
+
await helmUninstallCommand2();
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7316
7550
|
function addAdminCommands(program2) {
|
|
7317
7551
|
const admin = configureCommand(program2.command("admin", { hidden: true }).description("Operator commands"));
|
|
7318
7552
|
admin.action(() => {
|
|
@@ -7792,7 +8026,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
7792
8026
|
return;
|
|
7793
8027
|
}
|
|
7794
8028
|
const candidate = path2.join(dir, name);
|
|
7795
|
-
return
|
|
8029
|
+
return existsSync6(candidate) ? candidate : undefined;
|
|
7796
8030
|
};
|
|
7797
8031
|
const resolveFile = (explicit, name) => {
|
|
7798
8032
|
if (explicit) {
|
package/package.json
CHANGED