From c943a2a229e797c889c697510b6b24ba16331c0e Mon Sep 17 00:00:00 2001 From: 0thernet <894119+0thernet@users.noreply.github.com> Date: Sat, 26 Sep 2026 23:21:09 -0400 Subject: [PATCH 1/5] Show render and credits status in the Slopcamera menu bar on menu kit v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The menu bar now reads a small status file the CLI writes around each rendering or generating command and after credits status, and shows what is running, how the last job ended (a failed job gets a plain reason and a โš ๏ธŽ row) and the last known balance, with low credits flagged. Outputs come from desktop-foundation 0.8's OutputsSection (three newest, no duplicate submenu), the login item moves to the shared LaunchAgent helper, and diagnostics sit behind โŒฅ on Updates & support. slopcamera menubar install now copies the build into place and hands over to the menu bar's own install; the old launchctl code and its com.hraness.slopcamera.menubar plist are retired. The command stays documented under help menubar but leaves root help until released packages include the binary. Nine menu fixtures, one per state, pass companion lint-menu --strict in CI. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 7 + AGENTS.md | 2 +- README.md | 5 +- apps/desktop/cli/args.ts | 4 +- apps/desktop/cli/commands.ts | 14 + apps/desktop/cli/help.ts | 21 +- apps/desktop/cli/menubar-status.test.ts | 62 ++ apps/desktop/cli/menubar-status.ts | 112 +++ apps/desktop/cli/menubar.test.ts | 222 +++-- apps/desktop/cli/menubar.ts | 214 ++--- apps/desktop/dist/cli/main.js | 632 +++++++------ .../src/docs/how-to/install-from-source.md | 2 +- desktop/AGENTS.md | 5 +- desktop/Cargo.lock | 20 +- desktop/menubar/Cargo.toml | 8 +- .../menubar/fixtures/menu-action-error.json | 109 +++ .../menubar/fixtures/menu-action-error.txt | 18 + desktop/menubar/fixtures/menu-empty.json | 69 ++ desktop/menubar/fixtures/menu-empty.txt | 12 + desktop/menubar/fixtures/menu-error.json | 78 ++ desktop/menubar/fixtures/menu-error.txt | 13 + desktop/menubar/fixtures/menu-first-run.json | 70 ++ desktop/menubar/fixtures/menu-first-run.txt | 12 + .../menubar/fixtures/menu-login-not-ours.json | 71 ++ .../menubar/fixtures/menu-login-not-ours.txt | 12 + .../menubar/fixtures/menu-low-credits.json | 84 ++ desktop/menubar/fixtures/menu-low-credits.txt | 14 + desktop/menubar/fixtures/menu-ready.json | 109 +++ desktop/menubar/fixtures/menu-ready.txt | 18 + desktop/menubar/fixtures/menu-rendering.json | 96 ++ desktop/menubar/fixtures/menu-rendering.txt | 16 + desktop/menubar/fixtures/menu-stale-job.json | 70 ++ desktop/menubar/fixtures/menu-stale-job.txt | 12 + desktop/menubar/src/lifecycle.rs | 858 ++++++++++++++++++ desktop/menubar/src/main.rs | 529 ++++++++--- desktop/menubar/src/menu.rs | 201 ++++ desktop/menubar/src/menu_fixture.rs | 408 +++++++++ desktop/menubar/src/status.rs | 181 ++++ docs/menubar-release.md | 34 +- 39 files changed, 3691 insertions(+), 733 deletions(-) create mode 100644 apps/desktop/cli/menubar-status.test.ts create mode 100644 apps/desktop/cli/menubar-status.ts create mode 100644 desktop/menubar/fixtures/menu-action-error.json create mode 100644 desktop/menubar/fixtures/menu-action-error.txt create mode 100644 desktop/menubar/fixtures/menu-empty.json create mode 100644 desktop/menubar/fixtures/menu-empty.txt create mode 100644 desktop/menubar/fixtures/menu-error.json create mode 100644 desktop/menubar/fixtures/menu-error.txt create mode 100644 desktop/menubar/fixtures/menu-first-run.json create mode 100644 desktop/menubar/fixtures/menu-first-run.txt create mode 100644 desktop/menubar/fixtures/menu-login-not-ours.json create mode 100644 desktop/menubar/fixtures/menu-login-not-ours.txt create mode 100644 desktop/menubar/fixtures/menu-low-credits.json create mode 100644 desktop/menubar/fixtures/menu-low-credits.txt create mode 100644 desktop/menubar/fixtures/menu-ready.json create mode 100644 desktop/menubar/fixtures/menu-ready.txt create mode 100644 desktop/menubar/fixtures/menu-rendering.json create mode 100644 desktop/menubar/fixtures/menu-rendering.txt create mode 100644 desktop/menubar/fixtures/menu-stale-job.json create mode 100644 desktop/menubar/fixtures/menu-stale-job.txt create mode 100644 desktop/menubar/src/lifecycle.rs create mode 100644 desktop/menubar/src/menu.rs create mode 100644 desktop/menubar/src/menu_fixture.rs create mode 100644 desktop/menubar/src/status.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 038aada2..3e1ea23b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -207,6 +207,13 @@ jobs: workspaces: desktop - name: Build the menu-bar companion run: cargo build --release --locked --manifest-path desktop/Cargo.toml + - name: Test the menu-bar companion and its menu fixtures + run: cargo test --locked --manifest-path desktop/Cargo.toml + - name: Lint menu fixtures + run: >- + npx --yes + --package=https://github.com/hraness/desktop-foundation/releases/download/v0.8.0/hraness-desktop-foundation-0.8.0.tgz + companion lint-menu --strict desktop/menubar/fixtures/*.json site: name: Slopcamera site diff --git a/AGENTS.md b/AGENTS.md index def42fd7..13080429 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,7 +53,7 @@ - The fixed `slopcamera.studio.run` operation is an explicit native-authoring exception: it may execute a previously retained, hash-bound Python or native-scene bundle through a closed Blender/Manim/CadQuery host adapter. It requires a separate invocation-scoped trusted-current-user envelope; source import, pure planning and inspection stay inert. Runtime paths and argv remain host-owned, and the profile declares no OS sandbox or hermetic dependency closure. - Preserve the root module and `./workflow` v0.8 imperative APIs. Add declarative graphs through `./code` and lower-level compiler contracts through `./code/advanced`; do not add `./code/testing` or `./code/workflows` exports. - Treat explicitly imported workflow modules as trusted current-user Bun code. Compile graphs only against the host's closed capability projection, reject an unsupported capability before executor or resource admission, and do not add an open operation-registration hook. -- Treat the portable SDK and core as the canonical graph contract. The complete local host consumes that contract through its own closed projection and owns durable media execution. The optional Rust menu-bar companion under `desktop/` is a disposable outputs-directory client with no product authority. +- Treat the portable SDK and core as the canonical graph contract. The complete local host consumes that contract through its own closed projection and owns durable media execution. The optional Rust menu-bar companion under `desktop/` is a disposable client of the outputs directory and the CLI's status file, with no product authority. - Model one project as immutable source plus explicit revisions, candidates, selections, and delivery variants. Keep ready work bounded by resource claims and serialize expensive encodes by default. - Keep imperative workflows as explicitly imported trusted Bun modules over the fixed operation registry. Parse runtime input, bound and uniquely identify steps, drain dispatched work before returning, retain completed-step receipts on every failure path, and do not load caller-selected source paths. - Keep semantic SDK, CLI, MCP, and workflow resource admission machine-global and crash-safe on macOS and Linux, with a truthful process-local fallback elsewhere. Preserve profile identity, FIFO admission for overlapping claims, callback-settlement ownership, and inherited lease descriptors through spawned vectorizer processes. diff --git a/README.md b/README.md index 54cbf5f1..ea160386 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,9 @@ Native engines install separately. The [source-install guide](docs/how-to/use-cu explains workspace placement and durable-run identity; the [capability reference](docs/reference/capabilities.md) names runtime requirements. -Slopcamera is a CLI. On macOS, an optional unbundled menu-bar companion can show -the files in the agent outputs directory; `slopcamera menubar` runs a prebuilt +Slopcamera is a CLI. On macOS, an optional unbundled menu-bar companion shows +what Slopcamera is rendering, how the last job ended, your last known credits +balance and the newest files in the agent outputs directory; `slopcamera menubar` runs a prebuilt companion directly and does not install or launch an application bundle. The companion is a thin product binary over the shared [desktop-foundation](https://github.com/hraness/desktop-foundation) crate, pinned diff --git a/apps/desktop/cli/args.ts b/apps/desktop/cli/args.ts index 022e3d78..5ee1bbd0 100644 --- a/apps/desktop/cli/args.ts +++ b/apps/desktop/cli/args.ts @@ -3659,8 +3659,10 @@ export function parseCliArgs(argv: readonly string[]): CliCommand { case "menubar": { const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--foreground": "flag", "--background": "flag" }); const positionals = parsed.positionals; - if (positionals.length > 1 || (positionals[0] !== undefined && !["install", "uninstall", "status"].includes(positionals[0]))) throw new CliError("usage", "Use slopcamera menubar [--foreground|--background] or slopcamera menubar install|uninstall|status. See slopcamera help menubar."); + if (positionals.length > 1 || (positionals[0] !== undefined && !["install", "uninstall", "status", "start"].includes(positionals[0]))) throw new CliError("usage", "Use slopcamera menubar [--foreground|--background] or slopcamera menubar install|uninstall|status|start. See slopcamera help menubar."); if (positionals[0] !== undefined && (optionFlag(parsed, "--foreground") || optionFlag(parsed, "--background"))) throw new CliError("usage", "Choose a menu-bar action or a run mode, not both."); + // `start` opens it now and returns, like --background; the login item's hints use it. + if (positionals[0] === "start") return { kind: "menubar", action: "run", mode: "background", json: optionFlag(parsed, "--json") }; return { kind: "menubar", action: (positionals[0] as "install" | "uninstall" | "status" | undefined) ?? "run", mode: optionFlag(parsed, "--background") ? "background" : "foreground", json: optionFlag(parsed, "--json") }; } case "outputs": { diff --git a/apps/desktop/cli/commands.ts b/apps/desktop/cli/commands.ts index cfcb5382..c6eeb5bc 100644 --- a/apps/desktop/cli/commands.ts +++ b/apps/desktop/cli/commands.ts @@ -1,3 +1,4 @@ +import { activityLabel, recordActivity, recordCredits } from "./menubar-status"; import { reportUsefulResult, type UsefulResultObserver } from "../../../src/support-completion"; import { SlopcameraCloudError } from "../../../src/cloud-errors"; import { @@ -5548,6 +5549,7 @@ async function handleCreditsStatus( } catch (error) { throw creditsErrorToCli(error); } + recordCredits(context.stateRoot, { usd: balance.balance.usd, low: balance.lowBalance, checkedAt: context.clock() }); const view = { configured: true, tokenSource: loaded.source, @@ -8419,6 +8421,7 @@ async function resolveMutationTarget( export async function runCli(argv: readonly string[], dependencies: CliDependencies = {}): Promise { const io = dependencies.io ?? processIo; let jsonRequested = argv.includes("--json") || argv.includes("--jsonl"); + let activity: { readonly stateRoot: string; readonly label: string; readonly startedAt: number } | undefined; try { const command = parseCliArgs(argv); jsonRequested = "json" in command ? command.json : command.kind === "events" && command.format !== "human"; @@ -8513,6 +8516,11 @@ export async function runCli(argv: readonly string[], dependencies: CliDependenc ? {} : { renderGalleryScene: dependencies.renderGalleryScene }), }; + const label = activityLabel(command); + if (label !== undefined) { + activity = { stateRoot, label, startedAt: context.clock() }; + recordActivity(stateRoot, { state: "running", label, startedAt: activity.startedAt }); + } const mutationTarget = await resolveMutationTarget(paths, stateRoot, command); await withCommandHostResources(context, command, async admittedContext => { if (mutationTarget === undefined) { @@ -8535,9 +8543,15 @@ export async function runCli(argv: readonly string[], dependencies: CliDependenc || command.kind === "project-render" && command.action === "run" && !command.dryRun) { reportUsefulResult(dependencies.onUsefulResult); } + if (activity !== undefined) { + recordActivity(activity.stateRoot, { state: "done", label: activity.label, startedAt: activity.startedAt, finishedAt: io.now().getTime() }); + } return 0; } catch (error) { const failure = asCliError(error); + if (activity !== undefined) { + recordActivity(activity.stateRoot, { state: "failed", label: activity.label, startedAt: activity.startedAt, finishedAt: io.now().getTime(), error: failure.code }); + } const payload = { error: { code: failure.code, diff --git a/apps/desktop/cli/help.ts b/apps/desktop/cli/help.ts index f4bb70b9..54b19896 100644 --- a/apps/desktop/cli/help.ts +++ b/apps/desktop/cli/help.ts @@ -35,9 +35,6 @@ Commands: credits status|topup|wait|forget Buy and inspect hosted generation credits media audio|color Apply local non-destructive audio and video effects - menubar [--foreground|--background] - Show your outputs in the menu bar - menubar install|uninstall|status Open it at login, stop that, or check it support [--json|protocol --json|offer --json|shown |release |dismiss|snooze|enable|status --json] Optional support; no feature requires payment outputs Print the agent outputs directory @@ -68,14 +65,15 @@ Optional support: slopcamera support ยท Turn off: HRANESS_SUPPORT_AUDIENCE=off`; const HELP: Readonly> = { menubar: `Usage: slopcamera menubar [--foreground|--background] [--json] - slopcamera menubar install|uninstall|status [--json] + slopcamera menubar install|uninstall|status|start [--json] -Shows ๐Ÿ“ท in the menu bar with your recent outputs. It only lists and opens -files in the outputs folder (slopcamera outputs). +Shows what Slopcamera is doing in the menu bar: a render in progress, how the +last job ended and your last known credits balance, plus your newest outputs. +It only reads that status and the outputs folder (slopcamera outputs). menubar Open it now; it closes when this command ends - menubar --background Open it now and return - menubar install Open it now and every time you log in + menubar start Open it now and return (same as --background) + menubar install Open it every time you log in menubar uninstall Stop opening it at login and remove the installed copy menubar status Check whether it opens at login and is running @@ -83,9 +81,10 @@ If it's already in your menu bar, starting it again says so and succeeds. macOS shows a notice that slopcamera-menubar can open at login. Turn it off any time in System Settings โ€บ General โ€บ Login Items & Extensions. -The menu bar isn't in released packages yet. In a Slopcamera checkout, build it -with cargo build --release --manifest-path desktop/Cargo.toml and slopcamera -finds it; elsewhere, set SLOPCAMERA_MENUBAR to the built file.`, +The menu bar isn't in released packages yet, so it isn't listed in +slopcamera help. In a Slopcamera checkout, build it with +cargo build --release --manifest-path desktop/Cargo.toml and slopcamera finds +it; elsewhere, set SLOPCAMERA_MENUBAR to the built file.`, capabilities: `Usage: slopcamera capabilities [--json] Print the exact statically assembled capability manifest for this CLI build. Each module owns named operation, workflow, MCP-tool, command, and runtime-profile identities. The manifest includes trust classes, resource/effect policies, runtime requirements, qualification status, and a canonical SHA-256. It reads no workspace source, loads no plugin, probes no runtime, and grants no authority. Use doctor separately to inspect this machine's currently available executables.`, diff --git a/apps/desktop/cli/menubar-status.test.ts b/apps/desktop/cli/menubar-status.test.ts new file mode 100644 index 00000000..683fd21a --- /dev/null +++ b/apps/desktop/cli/menubar-status.test.ts @@ -0,0 +1,62 @@ +import { lstatSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, test } from "bun:test"; + +import { parseCliArgs } from "./args"; +import { activityLabel, MENUBAR_STATUS_FILE, readMenubarStatus, recordActivity, recordCredits } from "./menubar-status"; +import { runCli } from "./commands"; +import type { CliIo } from "./io"; + +const roots: string[] = []; +function root(): string { + const dir = realpathSync(mkdtempSync(join(tmpdir(), "slopcamera-status-"))); + roots.push(dir); + return dir; +} +afterEach(() => { for (const dir of roots.splice(0)) rmSync(dir, { recursive: true, force: true }); }); + +describe("menu bar status file", () => { + test("labels only the commands that make media", () => { + expect(activityLabel(parseCliArgs(["render", "run", "rec", "--output", "out.mp4"]))).toBe("Rendering a recording"); + expect(activityLabel(parseCliArgs(["render", "run", "rec", "--output", "out.mp4", "--dry-run"]))).toBeUndefined(); + expect(activityLabel(parseCliArgs(["doctor"]))).toBeUndefined(); + expect(activityLabel(parseCliArgs(["outputs"]))).toBeUndefined(); + }); + + test("keeps activity and credits side by side in one owner-only file", () => { + const dir = root(); + recordActivity(dir, { state: "running", label: "Rendering a recording", startedAt: 1 }); + recordCredits(dir, { usd: "$4.20", low: false, checkedAt: 2 }); + recordActivity(dir, { state: "failed", label: "Rendering a recording", startedAt: 1, finishedAt: 3, error: "subprocess" }); + expect(readMenubarStatus(dir)).toEqual({ + schemaVersion: 1, + activity: { state: "failed", label: "Rendering a recording", startedAt: 1, finishedAt: 3, error: "subprocess" }, + credits: { usd: "$4.20", low: false, checkedAt: 2 }, + }); + expect(lstatSync(join(dir, MENUBAR_STATUS_FILE)).mode & 0o777).toBe(0o600); + expect(readdirSync(dir)).toEqual([MENUBAR_STATUS_FILE]); + }); + + test("never throws and ignores files it didn't write", () => { + const dir = root(); + writeFileSync(join(dir, MENUBAR_STATUS_FILE), "not json"); + expect(readMenubarStatus(dir)).toEqual({ schemaVersion: 1 }); + recordActivity(join(dir, "missing"), { state: "done", label: "Rendering a recording", startedAt: 1 }); + rmSync(join(dir, MENUBAR_STATUS_FILE)); + symlinkSync("/etc/hosts", join(dir, MENUBAR_STATUS_FILE)); + expect(readMenubarStatus(dir)).toEqual({ schemaVersion: 1 }); + }); + + test("a failed media command records its fixed error code", async () => { + const dir = root(); + const io: CliIo = { cwd: () => dir, env: {}, now: () => new Date(5_000), platform: "darwin", stdout: () => undefined, stderr: () => undefined }; + const code = await runCli(["render", "run", "missing-recording", "--output", join(dir, "out.mp4")], { io, stateRoot: dir, clock: () => 4_000 }); + expect(code).not.toBe(0); + const status = JSON.parse(readFileSync(join(dir, MENUBAR_STATUS_FILE), "utf8")); + expect(status.activity.state).toBe("failed"); + expect(status.activity.label).toBe("Rendering a recording"); + expect(typeof status.activity.error).toBe("string"); + expect(status.activity.startedAt).toBe(4_000); + }); +}); diff --git a/apps/desktop/cli/menubar-status.ts b/apps/desktop/cli/menubar-status.ts new file mode 100644 index 00000000..cdb404f6 --- /dev/null +++ b/apps/desktop/cli/menubar-status.ts @@ -0,0 +1,112 @@ +import { chmodSync, lstatSync, openSync, closeSync, readFileSync, renameSync, rmSync, writeSync, constants } from "node:fs"; +import { randomUUID } from "node:crypto"; +import { join } from "node:path"; + +import type { CliCommand } from "./args"; + +/** + * The small status file the menu bar reads: what Slopcamera is doing now, + * how the last render ended, and the last credits balance a command saw. + * It holds plain labels, times and fixed error codes only โ€” never paths, + * prompts, tokens or account identifiers. Writes are best effort: a status + * that can't be saved never fails the command. + */ +export const MENUBAR_STATUS_FILE = "menubar-status.json"; +const MAX_STATUS_BYTES = 16 * 1024; + +export type ActivityState = "running" | "done" | "failed"; + +export interface MenubarActivity { + readonly state: ActivityState; + readonly label: string; + readonly startedAt: number; + readonly finishedAt?: number; + readonly error?: string; +} + +export interface MenubarCredits { + readonly usd: string; + readonly low: boolean; + readonly checkedAt: number; +} + +export interface MenubarStatus { + readonly schemaVersion: 1; + readonly activity?: MenubarActivity; + readonly credits?: MenubarCredits; +} + +/** Plain labels for the commands that make media. Everything else leaves the status alone. */ +export function activityLabel(command: CliCommand): string | undefined { + switch (command.kind) { + case "render-run": return command.dryRun ? undefined : "Rendering a recording"; + case "project-render": return command.action === "run" && !command.dryRun ? "Rendering a project" : undefined; + case "project-cinema": return "Rendering a project"; + case "html-render": return "Rendering an HTML scene"; + case "diagram-render": return "Rendering a diagram"; + case "image-vectorize": return "Vectorizing an image"; + case "ai-image-generate": return "Generating an image"; + case "ai-video-generate": return "Generating a video"; + case "ai-speech-generate": return "Generating speech"; + case "directing": return "Directing a clip"; + case "studio": return "Running a studio job"; + case "spatial-scene": return command.action === "render" ? "Rendering a 3D scene" : undefined; + case "workflows-run": return "Running a workflow"; + case "code-run": return "Running a workflow"; + default: return undefined; + } +} + +function parse(text: string): MenubarStatus { + try { + const value: unknown = JSON.parse(text); + if (typeof value !== "object" || value === null || (value as { schemaVersion?: unknown }).schemaVersion !== 1) return { schemaVersion: 1 }; + const record = value as Record; + const activity = record.activity as MenubarActivity | undefined; + const credits = record.credits as MenubarCredits | undefined; + return { + schemaVersion: 1, + ...(activity !== undefined && typeof activity === "object" && typeof activity.label === "string" ? { activity } : {}), + ...(credits !== undefined && typeof credits === "object" && typeof credits.usd === "string" ? { credits } : {}), + }; + } catch { + return { schemaVersion: 1 }; + } +} + +export function readMenubarStatus(stateRoot: string): MenubarStatus { + const path = join(stateRoot, MENUBAR_STATUS_FILE); + try { + const info = lstatSync(path); + if (!info.isFile() || info.size > MAX_STATUS_BYTES) return { schemaVersion: 1 }; + return parse(readFileSync(path, "utf8")); + } catch { + return { schemaVersion: 1 }; + } +} + +/** Replaces the status file atomically with an owner-only file. Never throws. */ +export function writeMenubarStatus(stateRoot: string, update: (current: MenubarStatus) => MenubarStatus): void { + const path = join(stateRoot, MENUBAR_STATUS_FILE); + const staged = join(stateRoot, `.${MENUBAR_STATUS_FILE}.${randomUUID()}.tmp`); + try { + const root = lstatSync(stateRoot); + if (!root.isDirectory()) return; + const text = `${JSON.stringify(update(readMenubarStatus(stateRoot)))}\n`; + if (Buffer.byteLength(text) > MAX_STATUS_BYTES) return; + const fd = openSync(staged, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600); + try { writeSync(fd, text); } finally { closeSync(fd); } + chmodSync(staged, 0o600); + renameSync(staged, path); + } catch { + try { rmSync(staged, { force: true }); } catch { /* best effort */ } + } +} + +export function recordActivity(stateRoot: string, activity: MenubarActivity): void { + writeMenubarStatus(stateRoot, (current) => ({ ...current, activity })); +} + +export function recordCredits(stateRoot: string, credits: MenubarCredits): void { + writeMenubarStatus(stateRoot, (current) => ({ ...current, credits })); +} diff --git a/apps/desktop/cli/menubar.test.ts b/apps/desktop/cli/menubar.test.ts index 83539831..0f81847d 100644 --- a/apps/desktop/cli/menubar.test.ts +++ b/apps/desktop/cli/menubar.test.ts @@ -1,10 +1,11 @@ -import { chmodSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { afterEach, describe, expect, test } from "bun:test"; import type { CliIo } from "./io"; -import { ALREADY_RUNNING_EXIT, installedBinaryPath, installLaunchAgent, interpretLaunchctlResult, launchAgentPath, launchAgentPlist, launchAgentState, launchMenubar, launchMessage, launchOutcome, loginItemNotice, manageMenubar, outputsRoot, resolveMenubarBinary, serviceRunning, stateMessage, uninstallLaunchAgent } from "./menubar"; +import { ALREADY_RUNNING_EXIT, helperEnvironment, installBinary, installedBinaryPath, launchAgentPath, launchAgentPlist, launchMenubar, launchMessage, launchOutcome, manageMenubar, outputsRoot, removeLegacyAgent, resolveMenubarBinary, type HelperResult } from "./menubar"; import { commandHelp } from "./help"; +import { parseCliArgs } from "./args"; const fixtures: string[] = []; function fixture() { @@ -12,21 +13,28 @@ function fixture() { fixtures.push(home); const binary = join(home, "new-build"); writeFileSync(binary, "prebuilt-v1", { mode: 0o700 }); - const env = { HOME: home, SLOPCAMERA_MENUBAR: binary }; - return { home, binary, env, stable: installedBinaryPath(env), plist: launchAgentPath(env) }; + const env = { HOME: home, SLOPCAMERA_MENUBAR: binary, LANG: "en_US.UTF-8" }; + return { home, binary, env, stable: installedBinaryPath(env), legacy: launchAgentPath(env) }; } afterEach(() => { for (const path of fixtures.splice(0)) rmSync(path, { recursive: true, force: true }); }); +function recorder(result: HelperResult) { + const calls: { binary: string; args: readonly string[]; audience: string | undefined }[] = []; + const run = async (binary: string, args: readonly string[], env: Readonly>) => { + calls.push({ binary, args, audience: env.HRANESS_AUDIENCE }); + return result; + }; + return { calls, run }; +} + +function io(home: string, env: Readonly>) { + const output: string[] = []; + const errors: string[] = []; + const value: CliIo = { cwd: () => home, env, now: () => new Date(), platform: "darwin", stdout: text => { output.push(text); }, stderr: text => { errors.push(text); } }; + return { io: value, output, errors }; +} + describe("Slopcamera menu-bar launcher", () => { - test("accepts Bun sync success without a signalCode field and refuses uncertain results", () => { - const success = { success: true, exitCode: 0, stdout: Buffer.from("owned job"), stderr: Buffer.alloc(0) }; - expect(interpretLaunchctlResult(success, 501, false)).toBe("owned job"); - expect(() => interpretLaunchctlResult({ ...success, success: false }, 501, false)).toThrow(); - expect(() => interpretLaunchctlResult({ ...success, stdout: Buffer.alloc(65_537) }, 501, false)).toThrow(); - const missing = { success: false, exitCode: 113, stdout: Buffer.alloc(0), stderr: Buffer.from('Bad request.\nCould not find service "com.hraness.slopcamera.menubar" in domain for user gui: 501\n') }; - expect(interpretLaunchctlResult(missing, 501, true)).toBe(null); - expect(() => interpretLaunchctlResult(missing, 502, true)).toThrow(); - }); test("keeps outputs beside the product state root", () => { expect(outputsRoot("/tmp/Slopcamera/cli")).toBe("/tmp/Slopcamera/outputs"); expect(outputsRoot("/tmp/Slopcamera")).toBe("/tmp/Slopcamera/outputs"); @@ -46,98 +54,96 @@ describe("Slopcamera menu-bar launcher", () => { expect(resolveMenubarBinary(home, { ...env, SLOPCAMERA_MENUBAR: link })).not.toBe(link); }); - test("preserves shared directory modes and stages away from predictable temporary names", () => { - const { binary, env, stable, plist } = fixture(); - mkdirSync(dirname(plist), { recursive: true, mode: 0o755 }); - chmodSync(dirname(plist), 0o755); - mkdirSync(dirname(stable), { recursive: true, mode: 0o700 }); - const sentinel = `${stable}.tmp-${process.pid}`; - writeFileSync(sentinel, "preserve", { mode: 0o600 }); - const calls: string[][] = []; - expect(installLaunchAgent(binary, env, "darwin", args => { calls.push([...args]); return null; })).toBe("installed"); - expect(calls.map(args => args[0])).toEqual(["print", "print", "bootstrap"]); + test("installs the build as an owner-only copy and replaces an earlier one", () => { + const { binary, env, stable } = fixture(); + expect(installBinary(binary, env)).toBe(stable); expect(readFileSync(stable, "utf8")).toBe("prebuilt-v1"); - expect(readFileSync(sentinel, "utf8")).toBe("preserve"); - expect(lstatSync(dirname(plist)).mode & 0o777).toBe(0o755); - expect(lstatSync(stable).mode & 0o777).toBe(0o700); - expect(readFileSync(plist, "utf8")).toBe(launchAgentPlist(stable)); + expect(statSync(stable).mode & 0o777).toBe(0o700); + writeFileSync(binary, "prebuilt-v2", { mode: 0o700 }); + installBinary(binary, env); + expect(readFileSync(stable, "utf8")).toBe("prebuilt-v2"); + chmodSync(stable, 0o722); + expect(() => installBinary(binary, env)).toThrow(); }); - test("retries a failed bootstrap and stops the owned service before an upgrade", () => { - const { binary, env, stable, plist } = fixture(); - expect(() => installLaunchAgent(binary, env, "darwin", args => { if (args[0] === "bootstrap") throw new Error("bootstrap failed"); return null; })).toThrow("bootstrap failed"); - writeFileSync(binary, "prebuilt-v2"); - const calls: string[] = []; - installLaunchAgent(binary, env, "darwin", (args, allowMissing) => { - if (args[0] === "print") return `gui/${process.getuid?.()}/com.hraness.slopcamera.menubar = {\n\tpath = ${plist}\n\tprogram = ${stable}\n\targuments = {\n\t\t${stable}\n\t}\n}\n`; - calls.push(args[0]!); - if (args[0] === "bootout") { - expect(allowMissing).toBe(true); - expect(readFileSync(stable, "utf8")).toBe("prebuilt-v1"); - } else expect(readFileSync(stable, "utf8")).toBe("prebuilt-v2"); - return null; - }); - expect(calls).toEqual(["bootout", "bootstrap"]); - expect(resolveMenubarBinary(env.HOME, { ...env, SLOPCAMERA_MENUBAR: `${binary}-missing` })).toBe(null); + test("refuses symbolic-link install parents", () => { + const { home, binary, env } = fixture(); + mkdirSync(join(home, "elsewhere")); + symlinkSync(join(home, "elsewhere"), join(home, "Library")); + expect(() => installBinary(binary, env)).toThrow(); }); - test("preserves loaded foreign services even beside a matching owned plist", () => { - const { binary, env, stable, plist } = fixture(); - installLaunchAgent(binary, env, "darwin", () => null); - const calls: string[] = []; - const foreign = (args: readonly string[]) => { - calls.push(args[0]!); - return `gui/${process.getuid?.()}/com.hraness.slopcamera.menubar = {\n\tpath = ${plist}\n\tprogram = /foreign\n\targuments = {\n\t\t/foreign\n\t}\n}\n`; - }; - expect(() => installLaunchAgent(binary, env, "darwin", foreign)).toThrow(); - expect(() => uninstallLaunchAgent(stable, env, "darwin", foreign)).toThrow(); - expect(calls).toEqual(["print", "print"]); - expect(readFileSync(stable, "utf8")).toBe("prebuilt-v1"); - expect(readFileSync(plist, "utf8")).toBe(launchAgentPlist(stable)); + test("removes only the exact login item earlier releases wrote", () => { + const { env, stable, legacy } = fixture(); + expect(removeLegacyAgent(env)).toBe("absent"); + mkdirSync(dirname(legacy), { recursive: true, mode: 0o700 }); + writeFileSync(legacy, "hand written\n", { mode: 0o600 }); + expect(removeLegacyAgent(env)).toBe("foreign"); + expect(existsSync(legacy)).toBe(true); + writeFileSync(legacy, launchAgentPlist(stable), { mode: 0o600 }); + expect(removeLegacyAgent(env)).toBe("removed"); + expect(existsSync(legacy)).toBe(false); }); - test("refuses foreign plist and unsafe file types without launchctl or writes", () => { - for (const kind of ["foreign", "symlink", "directory"] as const) { - const { binary, env, plist, stable } = fixture(); - mkdirSync(dirname(plist), { recursive: true, mode: 0o700 }); - if (kind === "foreign") writeFileSync(plist, "foreign", { mode: 0o600 }); - else if (kind === "symlink") symlinkSync(binary, plist); - else mkdirSync(plist); - const run = () => { throw new Error("must not call launchctl"); }; - expect(() => installLaunchAgent(binary, env, "darwin", run)).toThrow(); - expect(() => uninstallLaunchAgent(stable, env, "darwin", run)).toThrow(); - expect(existsSync(stable)).toBe(false); - if (kind === "foreign") expect(readFileSync(plist, "utf8")).toBe("foreign"); - expect(readFileSync(binary, "utf8")).toBe("prebuilt-v1"); - } + test("install copies the build, retires the old login item and hands over to the menu bar", async () => { + const { home, env, stable, legacy } = fixture(); + mkdirSync(dirname(legacy), { recursive: true, mode: 0o700 }); + writeFileSync(legacy, launchAgentPlist(stable), { mode: 0o600 }); + const { calls, run } = recorder({ code: 0, stdout: "โœ“ Slopcamera will open at login\n", stderr: "๐Ÿ” notice\n" }); + const terminal = io(home, env); + await manageMenubar(terminal.io, home, "install", false, run); + expect(calls).toEqual([{ binary: stable, args: ["install"], audience: "human" }]); + expect(terminal.output.join("")).toBe("โœ“ Slopcamera will open at login\n"); + expect(terminal.errors.join("")).toBe("๐Ÿ” notice\n"); + expect(existsSync(legacy)).toBe(false); + expect(existsSync(stable)).toBe(true); }); - test("refuses symbolic-link install parents and unowned orphan binaries", () => { - const { home, binary, env, stable } = fixture(); - const elsewhere = join(home, "elsewhere"); - mkdirSync(elsewhere); - symlinkSync(elsewhere, join(home, "Library")); - expect(() => installLaunchAgent(binary, env, "darwin", () => null)).toThrow(); - rmSync(join(home, "Library")); - mkdirSync(dirname(stable), { recursive: true, mode: 0o700 }); - writeFileSync(stable, "unmanaged", { mode: 0o700 }); - expect(() => installLaunchAgent(binary, env, "darwin", () => null)).toThrow(); - expect(readFileSync(stable, "utf8")).toBe("unmanaged"); + test("a refused change surfaces the menu bar's own sentence once", async () => { + const { home, env } = fixture(); + const { run } = recorder({ code: 1, stdout: "", stderr: "โœ— Slopcamera's login item was changed outside slopcamera, so it was left alone.\nโ†’ slopcamera menubar install\n" }); + const terminal = io(home, env); + await expect(manageMenubar(terminal.io, home, "install", false, run)).rejects.toThrow( + "Slopcamera's login item was changed outside slopcamera, so it was left alone.\nโ†’ slopcamera menubar install", + ); + expect(terminal.errors).toEqual([]); }); - test("status and uninstall survive removal of the original build", async () => { - const { home, binary, env, stable, plist } = fixture(); - installLaunchAgent(binary, env, "darwin", () => null); + test("status and uninstall work without the original build", async () => { + const { home, binary, env, stable } = fixture(); + installBinary(binary, env); rmSync(binary); - expect(launchAgentState(stable, env)).toBe("installed"); - const output: string[] = []; - const io: CliIo = { cwd: () => home, env, now: () => new Date(), platform: "darwin", stdout: text => { output.push(text); }, stderr: () => null }; - await manageMenubar(io, home, "status", true, () => null, () => false); - expect(JSON.parse(output.join(""))).toEqual({ launchAgent: "installed", running: false }); - expect(uninstallLaunchAgent(stable, env, "darwin", () => null)).toBe("absent"); - expect(existsSync(plist)).toBe(false); + const { calls, run } = recorder({ code: 0, stdout: "{\"login\":\"on\"}\n", stderr: "" }); + const agent = io(home, { ...env, CLAUDECODE: "1" }); + await manageMenubar(agent.io, home, "status", true, run); + expect(calls[0]).toEqual({ binary: stable, args: ["status", "--json"], audience: undefined }); + expect(agent.output.join("")).toBe("{\"login\":\"on\"}\n"); + await manageMenubar(agent.io, home, "uninstall", false, run); + expect(calls[1]?.args).toEqual(["uninstall"]); expect(existsSync(stable)).toBe(false); - expect(launchAgentState(stable, env)).toBe("absent"); + }); + + test("status says plainly when the menu bar isn't installed", async () => { + const { home, env } = fixture(); + const { calls, run } = recorder({ code: 0, stdout: "", stderr: "" }); + const terminal = io(home, env); + await manageMenubar(terminal.io, home, "status", false, run); + expect(calls).toEqual([]); + expect(terminal.output.join("")).toBe("Slopcamera's menu bar isn't installed.\nโ†’ slopcamera menubar install\n"); + const json = io(home, env); + await manageMenubar(json.io, home, "status", true, run); + expect(JSON.parse(json.output.join(""))).toEqual({ login: "off", running: false, installed: false }); + }); + + test("the helper sees a person as human and an agent as an agent", () => { + expect(helperEnvironment({ HOME: "/x" }).HRANESS_AUDIENCE).toBe("human"); + expect(helperEnvironment({ HOME: "/x", CLAUDECODE: "1" }).HRANESS_AUDIENCE).toBeUndefined(); + expect(helperEnvironment({ HOME: "/x", HRANESS_AUDIENCE: "quiet" }).HRANESS_AUDIENCE).toBe("quiet"); + }); + + test("start opens it now and returns", () => { + expect(parseCliArgs(["menubar", "start"])).toEqual({ kind: "menubar", action: "run", mode: "background", json: false }); + expect(() => parseCliArgs(["menubar", "stop"])).toThrow("install|uninstall|status|start"); }); }); @@ -166,40 +172,14 @@ describe("Slopcamera menu-bar copy", () => { } }); - test("install shows the login-item notice first and status checks liveness", async () => { - const { home, env } = fixture(); - const output: string[] = []; - const errors: string[] = []; - const io: CliIo = { cwd: () => home, env: { ...env, ...UTF8 }, now: () => new Date(), platform: "darwin", stdout: text => { output.push(text); }, stderr: text => { errors.push(text); } }; - await manageMenubar(io, home, "install", false, () => null, () => true); - expect(errors.join("")).toBe(loginItemNotice(UTF8)); - expect(errors.join("")).toContain("That's Slopcamera's menu bar."); - expect(output.join("")).toBe("โœ“ Slopcamera opens in your menu bar now and at every login. Look for ๐Ÿ“ท.\n Remove it any time: slopcamera menubar uninstall\n"); - output.length = 0; - await manageMenubar(io, home, "status", false, () => "gui/501/com.hraness.slopcamera.menubar = {\n\tstate = running\n}\n", () => false); - expect(output.join("")).toBe("โœ“ Slopcamera opens at login and is in your menu bar now.\n"); - // A copy started by hand holds the menu bar while the login item exited. - output.length = 0; - await manageMenubar(io, home, "status", true, () => null, () => true); - expect(JSON.parse(output.join(""))).toEqual({ launchAgent: "installed", running: true }); - }); - - test("status explains a login item that isn't running", () => { - expect(serviceRunning(() => null)).toBe(false); - expect(serviceRunning(() => "x = {\n\tstate = not running\n}\n")).toBe(false); - expect(stateMessage("status", "installed", false, UTF8)).toBe( - "โš  Slopcamera is set to open at login but isn't running. It may be turned off in System Settings โ€บ General โ€บ Login Items & Extensions.\nโ†’ slopcamera menubar install", - ); - expect(stateMessage("status", "absent", false, UTF8)).toBe("Slopcamera doesn't open at login.\nโ†’ slopcamera menubar install"); - expect(stateMessage("uninstall", "absent", false, UTF8)).toBe("โœ“ Slopcamera no longer opens in your menu bar at login."); - }); - test("help menubar has its own page and matches the usage error", () => { const page = commandHelp(["menubar"]); expect(page).not.toBe(commandHelp([])); expect(page.startsWith("Usage:\n slopcamera menubar [--foreground|--background] [--json]\n")).toBe(true); expect(page).toContain("already in your menu bar"); - expect(commandHelp([])).toContain("menubar [--foreground|--background]"); + expect(page).toContain("isn't listed in"); + // Hidden until released packages include the menu bar. + expect(commandHelp([])).not.toContain("menubar"); expect(commandHelp([])).not.toContain("Agents: after useful work"); expect(commandHelp([])).not.toContain("host-owned typed"); }); diff --git a/apps/desktop/cli/menubar.ts b/apps/desktop/cli/menubar.ts index 71068e4e..b09a72b8 100644 --- a/apps/desktop/cli/menubar.ts +++ b/apps/desktop/cli/menubar.ts @@ -7,6 +7,7 @@ import { writeJson, writeLine } from "./io"; import { ensurePrivateDirectory } from "./paths"; const SETTLE_MS = 400; +/** The label earlier releases used; the menu bar now manages its own login item. */ const LABEL = "com.hraness.slopcamera.menubar"; function xml(value: string): string { return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'"); } @@ -109,109 +110,78 @@ function readAgent(path: string, environment: Environment): string | null { return readRegular(path, 16 * 1024).toString("utf8"); } -/** Configuration state only; it does not claim the launchd service is running. */ -export type LaunchAgentState = "absent" | "installed" | "conflict"; -export type LaunchctlRunner = (args: readonly string[], allowMissing?: boolean) => string | null; -function launchctl(args: readonly string[], allowMissing = false): string | null { - const uid = process.getuid?.(); - if (uid === undefined) throw new CliError("unavailable", "The current user has no launchd GUI domain."); - const result = Bun.spawnSync(["/bin/launchctl", ...args.map((arg) => arg.replaceAll("{uid}", String(uid)))], { stdin: "ignore", stdout: "pipe", stderr: "pipe", timeout: 10_000, maxBuffer: 64 * 1024 }); - return interpretLaunchctlResult(result, uid, allowMissing); -} -export function interpretLaunchctlResult(result: { success: boolean; exitCode: number; stdout: Uint8Array; stderr: Uint8Array }, uid: number, allowMissing: boolean): string | null { - if (result.stdout.byteLength + result.stderr.byteLength > 64 * 1024) throw new CliError("unavailable", "launchctl exceeded the bounded response size."); - if (result.success && result.exitCode === 0) return Buffer.from(result.stdout).toString(); - if (allowMissing && result.exitCode === 113 && result.stdout.length === 0 && Buffer.from(result.stderr).toString() === `Bad request.\nCould not find service "${LABEL}" in domain for user gui: ${uid}\n`) return null; - throw new CliError("unavailable", "launchctl could not reconcile the Slopcamera menu-bar LaunchAgent. Run menubar install again to retry."); -} - -function loadedOwned(path: string, binary: string, current: string | null, run: LaunchctlRunner): boolean { - const output = run(["print", `gui/{uid}/${LABEL}`], true); - if (output === null) return false; - const uid = process.getuid?.(); - if (current === null || Buffer.byteLength(output) > 64 * 1024 || !output.startsWith(`gui/${uid}/${LABEL} = {\n`)) { - throw new CliError("conflict", "The loaded menu-bar service has an unverified identity."); - } - const lines = output.split("\n").map(line => line.trim()); - const field = (key: string): string | null => { - const values = lines.filter(line => line.startsWith(`${key} = `)); - return values.length === 1 ? values[0]!.slice(key.length + 3) : null; - }; - const argumentsStart = lines.indexOf("arguments = {"); - const argumentsEnd = lines.indexOf("}", argumentsStart + 1); - if (field("path") !== path || field("program") !== binary || argumentsStart < 0 || argumentsEnd !== argumentsStart + 2 || lines[argumentsStart + 1] !== binary) { - throw new CliError("conflict", "The loaded menu-bar service does not match this install; it was left untouched."); - } - return true; -} -export function launchAgentState(binary: string, environment: Environment = process.env): LaunchAgentState { - const content = readAgent(launchAgentPath(environment), environment); - if (content === null) return "absent"; - checkParents(binary, environment); - assertOwnedFile(binary); - return content === launchAgentPlist(binary) && exists(binary) ? "installed" : "conflict"; -} -function assertMac(platform: NodeJS.Platform): void { - if (platform !== "darwin") throw new CliError("unsupported-plan", "Slopcamera menu-bar LaunchAgents are supported only on macOS."); -} -export function installLaunchAgent(binary: string, environment: Environment = process.env, platform: NodeJS.Platform = process.platform, runLaunchctl: LaunchctlRunner = launchctl): LaunchAgentState { - assertMac(platform); - if (!isAbsolute(binary)) throw new CliError("unsafe-path", "LaunchAgent binary must be an absolute path."); +/** + * Copies the built menu bar into its installed location, replacing an + * earlier copy. Existing shared directory permissions are preserved; + * symbolic links and files other users can write are refused. + */ +export function installBinary(binary: string, environment: Environment = process.env): string { + if (!isAbsolute(binary)) throw new CliError("unsafe-path", "The menu-bar build must be an absolute path."); const stable = installedBinaryPath(environment); - const path = launchAgentPath(environment); - const current = readAgent(path, environment); - const expected = launchAgentPlist(stable); checkParents(stable, environment); assertOwnedFile(stable); - if (current !== null && current !== expected || current === null && infoAt(stable) !== null) { - throw new CliError("conflict", "The existing Slopcamera menu-bar install is not owned by this command."); - } const bytes = readRegular(binary, MAX_BINARY_BYTES, true); - loadedOwned(path, stable, current, runLaunchctl); - checkParents(path, environment, true); checkParents(stable, environment, true); - // Unique private staging directories prevent predictable temporary-file clobbering. - const binaryStage = mkdtempSync(join(dirname(stable), ".install-")); - let agentStage: string | undefined; + const stage = mkdtempSync(join(dirname(stable), ".install-")); try { - agentStage = mkdtempSync(join(dirname(path), ".slopcamera-install-")); - const stagedBinary = join(binaryStage, "binary"); - const stagedAgent = join(agentStage, "agent.plist"); - writeFileSync(stagedBinary, bytes, { mode: 0o700, flag: "wx" }); - writeFileSync(stagedAgent, expected, { mode: 0o600, flag: "wx" }); - if (readAgent(path, environment) !== current) throw new CliError("conflict", "The menu-bar install changed concurrently."); + const staged = join(stage, "binary"); + writeFileSync(staged, bytes, { mode: 0o700, flag: "wx" }); assertOwnedFile(stable); - // Retry bootstrap even when the plist matches: a previous bootstrap may have failed. - if (loadedOwned(path, stable, current, runLaunchctl)) runLaunchctl(["bootout", `gui/{uid}/${LABEL}`], true); - renameSync(stagedBinary, stable); - renameSync(stagedAgent, path); - runLaunchctl(["bootstrap", "gui/{uid}", path]); - return "installed"; + renameSync(staged, stable); + return stable; } finally { - rmSync(binaryStage, { recursive: true }); - if (agentStage !== undefined) rmSync(agentStage, { recursive: true }); + rmSync(stage, { recursive: true }); } } -export function uninstallLaunchAgent(_binary: string, environment: Environment = process.env, platform: NodeJS.Platform = process.platform, runLaunchctl: LaunchctlRunner = launchctl): LaunchAgentState { - assertMac(platform); - const stable = installedBinaryPath(environment); + +/** + * Removes the login item earlier releases wrote under the old label, but + * only when it is exactly the file they wrote. It never calls launchctl: a + * copy that is already running keeps running until you quit it or log out. + */ +export function removeLegacyAgent(environment: Environment = process.env): "absent" | "removed" | "foreign" { const path = launchAgentPath(environment); const current = readAgent(path, environment); if (current === null) return "absent"; - if (current !== launchAgentPlist(stable)) throw new CliError("conflict", "The existing Slopcamera menu-bar LaunchAgent is not owned by this command."); - checkParents(stable, environment); - assertOwnedFile(stable); - if (loadedOwned(path, stable, current, runLaunchctl)) runLaunchctl(["bootout", `gui/{uid}/${LABEL}`], true); - if (readAgent(path, environment) !== current) throw new CliError("conflict", "The menu-bar install changed concurrently."); - assertOwnedFile(stable); + if (current !== launchAgentPlist(installedBinaryPath(environment))) return "foreign"; rmSync(path); - if (infoAt(stable) !== null) rmSync(stable); - return "absent"; + return "removed"; +} + +function assertMac(platform: NodeJS.Platform): void { + if (platform !== "darwin") throw new CliError("unsupported-plan", "The Slopcamera menu bar runs only on macOS."); +} + +export interface HelperResult { readonly code: number; readonly stdout: string; readonly stderr: string } +/** Runs the installed menu bar's own install, uninstall or status command. */ +export type HelperRunner = (binary: string, args: readonly string[], env: Environment) => Promise; + +async function runHelper(binary: string, args: readonly string[], env: Environment): Promise { + const child = Bun.spawn([binary, ...args], { stdin: "ignore", stdout: "pipe", stderr: "pipe", env: { ...env } as Record }); + const [stdout, stderr, code] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]); + return { code, stdout: stdout.slice(0, 64 * 1024), stderr: stderr.slice(0, 64 * 1024) }; +} + +const AGENT_MARKERS = ["AI_AGENT", "CLAUDECODE", "CODEX_SANDBOX", "CODEX_SANDBOX_NETWORK_DISABLED", "CURSOR_AGENT", "GEMINI_CLI"]; + +/** + * The helper decides between human and agent output from its environment. + * Its output is captured here, so a person's terminal is passed on as + * `HRANESS_AUDIENCE=human`; agents and explicit settings pass through. + */ +export function helperEnvironment(env: Environment): Environment { + const agent = AGENT_MARKERS.some((name) => (env[name] ?? "") !== ""); + return env.HRANESS_AUDIENCE !== undefined || agent ? env : { ...env, HRANESS_AUDIENCE: "human" }; +} + +function helperFailure(result: HelperResult): CliError { + const text = result.stderr.trim().replace(/^(โœ—|FAIL)\s+/u, ""); + return new CliError(result.code === 2 ? "usage" : "unavailable", text === "" ? "The Slopcamera menu bar couldn't change its login item." : text); } + /** `slopcamera-menubar` exits with this code when another copy already holds the menu bar. */ export const ALREADY_RUNNING_EXIT = 3; const NOT_BUILT = "The Slopcamera menu bar isn't in this release yet. Build it with cargo build --release --manifest-path desktop/Cargo.toml in a Slopcamera checkout; slopcamera finds it there, or set SLOPCAMERA_MENUBAR to the built file."; -const LOGIN_ITEMS = "System Settings โ€บ General โ€บ Login Items & Extensions"; export type Launched = "running" | "already-running" | "exited"; export type BinaryRunner = (binary: string, foreground: boolean) => Promise; @@ -240,11 +210,6 @@ export function marks(env: Readonly>): Marks return ascii ? { ok: "OK", warn: "WARN", note: "NOTE", next: "->" } : { ok: "โœ“", warn: "โš ", note: "๐Ÿ”", next: "โ†’" }; } -/** The LOGIN_ITEM notice (notifies, so there is no confirm line). The requester is the bare companion until Slopcamera ships as an app. */ -export function loginItemNotice(env: Readonly>): string { - return `${marks(env).note} macOS will show a notice that slopcamera-menubar can open at login. That's Slopcamera's menu bar.\n It lists your outputs and opens when you log in. Nothing else runs in the background. Turn it off any time in ${LOGIN_ITEMS}.\n`; -} - export function launchMessage(outcome: Launched, env: Readonly>): string { const mark = marks(env); if (outcome === "already-running") return `${mark.ok} Slopcamera is already in your menu bar. Look for ๐Ÿ“ท.`; @@ -252,42 +217,6 @@ export function launchMessage(outcome: Launched, env: Readonly>): string { - const mark = marks(env); - if (state === "conflict") return `${mark.warn} Another menu-bar setup that this command didn't create is in the way. It was left untouched.\n Remove ~/Library/LaunchAgents/${LABEL}.plist yourself if you don't need it.`; - if (state === "absent") return action === "uninstall" ? `${mark.ok} Slopcamera no longer opens in your menu bar at login.` : `Slopcamera doesn't open at login.\n${mark.next} slopcamera menubar install`; - if (action === "install") return `${mark.ok} Slopcamera opens in your menu bar now and at every login. Look for ๐Ÿ“ท.\n Remove it any time: slopcamera menubar uninstall`; - return running - ? `${mark.ok} Slopcamera opens at login and is in your menu bar now.` - : `${mark.warn} Slopcamera is set to open at login but isn't running. It may be turned off in ${LOGIN_ITEMS}.\n${mark.next} slopcamera menubar install`; -} - -/** Whether launchd reports the loaded companion as running. Reads state only. */ -export function serviceRunning(runLaunchctl: LaunchctlRunner): boolean { - const output = runLaunchctl(["print", `gui/{uid}/${LABEL}`], true); - return output !== null && output.split("\n").some((line) => line.trim() === "state = running"); -} - -export type ProcessProbe = () => boolean; -/** Whether any copy of the companion runs for this user, including one started by hand. */ -function companionProcessRunning(): boolean { - const uid = process.getuid?.(); - if (uid === undefined) return false; - try { - return Bun.spawnSync(["/usr/bin/pgrep", "-x", "-U", String(uid), "slopcamera-menubar"], { stdin: "ignore", stdout: "ignore", stderr: "ignore", timeout: 5_000 }).exitCode === 0; - } catch { return false; } -} - -/** The menu bar is showing when launchd runs the login item or another copy holds it. */ -async function menubarShowing(runLaunchctl: LaunchctlRunner, probe: ProcessProbe, waitMs: number): Promise { - const deadline = Date.now() + waitMs; - for (;;) { - if (serviceRunning(runLaunchctl) || probe()) return true; - if (Date.now() >= deadline) return false; - await Bun.sleep(200); - } -} - export async function launchMenubar(io: CliIo, repositoryRoot: string, asJson: boolean, mode: "foreground" | "background" = "foreground", runBinary: BinaryRunner = spawnBinary): Promise { const binary = resolveMenubarBinary(repositoryRoot, io.env); if (binary === null) throw new CliError("unavailable", NOT_BUILT); @@ -295,23 +224,36 @@ export async function launchMenubar(io: CliIo, repositoryRoot: string, asJson: b if (asJson) writeJson(io, { running: outcome !== "exited", foreground: mode === "foreground", alreadyRunning: outcome === "already-running" }); else writeLine(io, launchMessage(outcome, io.env)); } -export async function manageMenubar(io: CliIo, repositoryRoot: string, action: "install" | "uninstall" | "status", asJson: boolean, runLaunchctl: LaunchctlRunner = launchctl, probe: ProcessProbe = companionProcessRunning): Promise { +export async function manageMenubar(io: CliIo, repositoryRoot: string, action: "install" | "uninstall" | "status", asJson: boolean, run: HelperRunner = runHelper): Promise { assertMac(io.platform); const stable = installedBinaryPath(io.env); - let state: LaunchAgentState; + const args = asJson ? [action, "--json"] : [action]; + const env = helperEnvironment(io.env); + const forward = (result: HelperResult): void => { + if (result.stdout !== "") io.stdout(result.stdout); + if (result.code !== 0) throw helperFailure(result); + if (result.stderr !== "") io.stderr(result.stderr); + }; if (action === "install") { const binary = resolveMenubarBinary(repositoryRoot, io.env); if (binary === null) throw new CliError("unavailable", NOT_BUILT); - if (!asJson) io.stderr(loginItemNotice(io.env)); - state = installLaunchAgent(binary, io.env, io.platform, runLaunchctl); - } else { - state = action === "uninstall" ? uninstallLaunchAgent(stable, io.env, io.platform, runLaunchctl) : launchAgentState(stable, io.env); + const installed = binary === stable ? stable : installBinary(binary, io.env); + removeLegacyAgent(io.env); + forward(await run(installed, args, env)); + return; } - // A fresh login item may take a moment to start, so install waits briefly. - const running = state === "installed" && await menubarShowing(runLaunchctl, probe, action === "install" ? 1_000 : 0); - if (asJson) writeJson(io, { launchAgent: state, running }); - else writeLine(io, stateMessage(action, state, running, io.env)); + if (action === "uninstall") removeLegacyAgent(io.env); + if (!exists(stable)) { + if (asJson) writeJson(io, { login: "off", running: false, installed: false }); + else if (action === "uninstall") writeLine(io, `${marks(io.env).ok} Slopcamera won't open at login.`); + else writeLine(io, `Slopcamera's menu bar isn't installed.\n${marks(io.env).next} slopcamera menubar install`); + return; + } + const result = await run(stable, args, env); + forward(result); + if (action === "uninstall") rmSync(stable); } + export async function reportOutputsRoot(io: CliIo, stateRoot: string, asJson: boolean): Promise { const directory = outputsRoot(stateRoot); await ensurePrivateDirectory(directory); diff --git a/apps/desktop/dist/cli/main.js b/apps/desktop/dist/cli/main.js index 3538c821..d6303618 100755 --- a/apps/desktop/dist/cli/main.js +++ b/apps/desktop/dist/cli/main.js @@ -1,43 +1,44 @@ #!/usr/bin/env bun // @bun -var Y6=($,_)=>()=>($&&(_=$($=0)),_);var m6=import.meta.require;var yx,F0;var k_=Y6(()=>{yx=["balanced","detailed","photo"];F0=class F0 extends Error{code;details;constructor($,_,F={},Q){super(_,Q);this.name="VectorizeError",this.code=$,this.details=F}}});function mW($){let _={maxDecodedPixels:$?.maxDecodedPixels??DK.maxDecodedPixels,maxDimension:$?.maxDimension??DK.maxDimension,maxDurationMs:$?.maxDurationMs??DK.maxDurationMs,maxInputBytes:$?.maxInputBytes??DK.maxInputBytes,maxOutputBytes:$?.maxOutputBytes??DK.maxOutputBytes,maxPaths:$?.maxPaths??DK.maxPaths};for(let F of zu0){let Q=_[F],Z=A_[F];if(!Number.isInteger(Q)||Q<1||Q>Z)throw new F0("invalid_input",`${F} must be a positive integer no greater than ${Z}.`,{hardLimit:Z,name:F,value:Q})}return Object.freeze(_)}class tw{#$;constructor($){this.#$=performance.now()+$}assert($){if(this.remainingMs()<=0)throw new F0("timeout",`Vectorization timed out during ${$}.`,{stage:$})}remainingMs(){return Math.max(0,Math.ceil(this.#$-performance.now()))}}var A_,DK,zu0;var uW=Y6(()=>{k_();A_=Object.freeze({maxDecodedPixels:16777216,maxDimension:4096,maxDurationMs:120000,maxInputBytes:16777216,maxOutputBytes:2000000,maxPaths:12000}),DK=Object.freeze({...A_,maxDurationMs:30000}),zu0=Object.keys(A_)});import{createHash as ku0}from"crypto";function AZ($){return ku0("sha256").update($).digest("hex")}function gx($,_){if($.length===0||$.length%4!==0)throw new F0("invalid_input","Trace sanitation requires nonempty RGBA pixels.");if(!Number.isInteger(_)||_<1||_>64)throw new F0("invalid_input","alphaCutoff must be an integer from 1 through 64.");let F=Uint8Array.from($);for(let Q=0;Q=_)continue;F[Q]=0,F[Q+1]=0,F[Q+2]=0,F[Q+3]=0}return F}function v60($){if($.length===0||$.length%4!==0)throw new F0("invalid_input","An alpha trace requires nonempty RGBA pixels.");let _=new Uint8Array($.length);for(let F=0;F<$.length;F+=4){let Q=$[F+3];_[F]=Q,_[F+1]=Q,_[F+2]=Q,_[F+3]=255}return _}function x60($,_){if($.length===0||$.length%4!==0)throw new F0("invalid_input","Low-alpha measurement requires nonempty RGBA pixels.");if(!Number.isInteger(_)||_<1||_>255)throw new F0("invalid_input","The low-alpha cutoff must be a byte.");let F=0,Q=0;for(let Z=3;Z<$.length;Z+=4){let J=$[Z];if(Q+=J,J>0&&J<_)F+=J}return Q===0?0:F/Q}function h60($){for(let _=3;_<$.length;_+=4){let F=$[_];if(F>0&&F<255)return!0}return!1}function m60($,_){l60($,_,"RMSE");let F=0;for(let Q=0;Q<$.length;Q+=4){let Z=$[Q+3]/255,J=_[Q+3]/255;for(let Y=0;Y<3;Y+=1){let H=$[Q+Y]*Z-_[Q+Y]*J;F+=H*H}let X=$[Q+3]-_[Q+3];F+=X*X}return Math.sqrt(F/$.length)/255}function u60($,_){l60($,_,"Alpha RMSE");let F=0;for(let Q=3;Q<$.length;Q+=4){let Z=$[Q]-_[Q];F+=Z*Z}return Math.sqrt(F/($.length/4))/255}function l60($,_,F){if($.length===0||$.length!==_.length||$.length%4!==0)throw new F0("invalid_input",`${F} inputs must be equally sized nonempty RGBA buffers.`)}function d60($,_,F,Q,Z=1){if(!Number.isInteger(F)||!Number.isInteger(Q)||F<1||Q<1||$.length!==F*Q*4||_.length!==$.length)throw new F0("invalid_input","Support metrics require equally sized rectangular RGBA buffers.");if(!Number.isInteger(Z)||Z<0||Z>4)throw new F0("invalid_input","Support dilation must be an integer from 0 to 4.");let J=new Uint8Array(F*Q);for(let j=0;j=0&&D=0&&w0){N=!0;break}}J[j*F+W]=N?1:0}let X=0,Y=0,H=0,K=0;for(let j=0;j_.toString(16).padStart(2,"0")).join("")}`}function c60([$,_,F]){let Q=(j)=>{let W=j/255;return W<=0.04045?W/12.92:((W+0.055)/1.055)**2.4},Z=Q($),J=Q(_),X=Q(F),Y=Math.cbrt(0.4122214708*Z+0.5363325363*J+0.0514459929*X),H=Math.cbrt(0.2119034982*Z+0.6806995451*J+0.1073969566*X),K=Math.cbrt(0.0883024619*Z+0.2817188376*J+0.6299787005*X);return[0.2104542553*Y+0.793617785*H-0.0040720468*K,1.9779984951*Y-2.428592205*H+0.4505937099*K,0.0259040371*Y+0.7827717662*H-0.808675766*K]}function vx($,_){return Math.hypot($[0]-_[0],$[1]-_[1],$[2]-_[2])}function Au0($,_){let F=$.map(({color:V,mass:O})=>({distance:vx(V,_),mass:O})),Q=Math.max(...F.map(({distance:V})=>V));if(Q===0)return Number.POSITIVE_INFINITY;let Z=Array.from({length:256},()=>({mass:0,moment:0}));for(let{distance:V,mass:O}of F){let D=Math.min(255,Math.floor(V/Q*255));Z[D].mass+=O,Z[D].moment+=O*V}let J=Z.reduce((V,O)=>V+O.mass,0),X=Z.reduce((V,O)=>V+O.moment,0),Y=0,H=0,K=-1,j=254;for(let V=0;V<255;V+=1){let O=Z[V];Y+=O.mass,H+=O.moment;let D=J-Y;if(Y===0||D===0)continue;let w=H/Y,L=(X-H)/D,C=Y*D*(w-L)**2;if(C>K)K=C,j=V}let W=Q*(j+1)/255;if((()=>F.reduce((V,{distance:O,mass:D})=>V+(O<=W?D:0),0)/J)()<0.5){let V=0;for(let{distance:O,mass:D}of[...F].sort((w,L)=>w.distance-L.distance))if(V+=D,W=O,V>=J/2)break}return W}function p60($){if($.length===0||$.length%4!==0)throw new F0("invalid_input","A duotone model requires nonempty RGBA pixels.");let _=new Map;for(let Y=0;Y<$.length;Y+=4){let H=$[Y+3]/255;if(H===0)continue;let K=$[Y],j=$[Y+1],W=$[Y+2],N=K>>4<<8|j>>4<<4|W>>4,V=_.get(N)??{blue:0,green:0,mass:0,red:0};V.mass+=H,V.red+=K*H,V.green+=j*H,V.blue+=W*H,_.set(N,V)}if(_.size===0)throw new F0("invalid_input","A duotone model requires visible pixels.");let F=[..._.entries()].map(([Y,H])=>({color:c60([H.red/H.mass,H.green/H.mass,H.blue/H.mass]),key:Y,mass:H.mass})).sort((Y,H)=>H.mass-Y.mass||Y.key-H.key),Q=F[0].color,Z=Au0(F,Q),J=F.reduce((Y,{mass:H})=>Y+H,0),X=F.reduce((Y,{color:H,mass:K})=>Y+(vx(H,Q)<=Z?K:0),0);return{cutoff:Z,primary:Q,primaryShare:X/J}}function xx($,_){return vx(c60($),_.primary)<=_.cutoff}var lW=Y6(()=>{k_()});import{constants as PM}from"fs";import{open as Mu0,realpath as Tu0}from"fs/promises";import{resolve as Ru0}from"path";import UX from"sharp";function r60(){if(UX.concurrency(s60)!==s60)throw new F0("trace_failed","The vectorization worker could not bind its Sharp CPU budget.")}async function IM($,_,F){F.assert("input read");let Q=await Pu0($,_.maxInputBytes,F);F.assert("input metadata");try{let Z=await UX(Q,{failOn:"error",limitInputPixels:_.maxDecodedPixels,sequentialRead:!0}).metadata(),J=Z.format;if(J===void 0||!qu0.has(J))throw new F0("invalid_input",`Expected a supported raster image, received ${J??"an unknown format"}.`);if((Z.pages??1)!==1)throw new F0("invalid_input","Animated and multipage raster inputs are rejected.");if(Z.width===void 0||Z.height===void 0||Z.width<1||Z.height<1)throw new F0("invalid_input","Raster dimensions are missing or invalid.");n60(Z.width,Z.height,_);let X=await UX(Q,{failOn:"error",limitInputPixels:_.maxDecodedPixels,sequentialRead:!0}).rotate().ensureAlpha().raw().toBuffer({resolveWithObject:!0}),{channels:Y,height:H,width:K}=X.info;if(Y!==4)throw new F0("invalid_input","Raster decoding did not produce RGBA pixels.");n60(K,H,_);let j=Uint8Array.from(X.data);if(!bu0(j))throw new F0("invalid_input","A fully transparent image cannot be vectorized.");F.assert("raster decode");let W=Math.min(1,Eu0/Math.max(K,H)),N=Math.max(1,Math.round(K*W)),V=Math.max(1,Math.round(H*W)),O=N===K&&V===H?j:Uint8Array.from(await UX(j,{raw:{channels:4,height:H,width:K}}).resize(N,V,{fit:"fill",kernel:"lanczos3"}).raw().toBuffer());return F.assert("metric sample"),{bytes:Q,format:J,height:H,inputBytes:Q.byteLength,pixels:j,scoreHeight:V,scorePixels:O,scoreWidth:N,sourceSha256:AZ(Q),width:K}}catch(Z){if(Z instanceof F0)throw Z;throw new F0("invalid_input","Raster input could not be decoded safely.",{},{cause:Z})}}async function Pu0($,_,F){if(typeof $==="string")return Iu0(Ru0($),_,F);let Q=$ instanceof ArrayBuffer?new Uint8Array($):$;if(Q.byteLength===0)throw new F0("invalid_input","Raster input is empty.");if(Q.byteLength>_)throw new F0("input_limit",`Raster input exceeds the ${_}-byte limit.`,{bytes:Q.byteLength,maximumBytes:_});F.assert("input read");let Z=Uint8Array.from(Q);return F.assert("input read"),Z}async function Iu0($,_,F){let Q;try{F.assert("input read");let Z=await Tu0($);F.assert("input read"),Q=await Mu0(Z,Su0());let J=await Q.stat();if(!J.isFile())throw new F0("invalid_input",`Raster input is not a file: ${$}`);if(J.size<1)throw new F0("invalid_input","Raster input is empty.");if(J.size>_)throw new F0("input_limit",`Raster input exceeds the ${_}-byte limit.`,{bytes:J.size,maximumBytes:_});let X=Buffer.allocUnsafe(Math.min(65536,_+1)),Y=[],H=0;while(!0){F.assert("input read");let K=Math.min(X.byteLength,_-H+1),{bytesRead:j}=await Q.read(X,0,K,null);if(j===0)break;if(H+=j,H>_)throw new F0("input_limit",`Raster input exceeds the ${_}-byte limit.`,{bytes:H,maximumBytes:_});Y.push(Buffer.from(X.subarray(0,j)))}if(H===0)throw new F0("invalid_input","Raster input is empty.");return F.assert("input read"),Buffer.concat(Y,H)}catch(Z){if(Z instanceof F0)throw Z;throw new F0("invalid_input","Raster input could not be read safely.",{},{cause:Z})}finally{await Q?.close().catch(()=>{return})}}function n60($,_,F){if(!Number.isInteger($)||!Number.isInteger(_)||$<1||_<1||$>F.maxDimension||_>F.maxDimension||$*_>F.maxDecodedPixels)throw new F0("input_limit",`Raster dimensions must fit ${F.maxDimension}px and ${F.maxDecodedPixels} decoded pixels.`,{height:_,width:$})}function bu0($){for(let _=3;_<$.length;_+=4)if($[_]>0)return!0;return!1}function Su0(){if(process.platform==="win32")return PM.O_RDONLY;return PM.O_RDONLY|PM.O_NONBLOCK|PM.O_NOFOLLOW}async function $D($,_,F){return Uint8Array.from(await UX($,{raw:{channels:4,height:F,width:_}}).png({adaptiveFiltering:!1,compressionLevel:9,palette:!1}).toBuffer())}async function o60($,_,F,Q){try{return Uint8Array.from(await UX(Buffer.from($),{density:72,failOn:"error",limitInputPixels:Q}).resize(_,F,{fit:"fill"}).ensureAlpha().raw().toBuffer())}catch(Z){throw new F0("trace_failed","Canonical SVG could not be rendered for quality measurement.",{},{cause:Z})}}function i60(){let $=fu0(UX.versions),_=$.sharp,F=$.vips;if(_===void 0||F===void 0)throw new F0("tool_version","Sharp must report its own and libvips version metadata.");return{sharp:_,sharpVersions:$,vips:F}}function fu0($){let _=Object.entries($).filter((F)=>F[1]!==void 0).sort(([F],[Q])=>F.localeCompare(Q));if(_.length===0||_.some(([F,Q])=>F.length===0||Q.length===0))throw new F0("tool_version","Pixel toolchain versions must be nonempty strings.");return Object.freeze(Object.fromEntries(_))}var qu0,Eu0=512,s60=1;var bM=Y6(()=>{lW();k_();qu0=new Set(["avif","gif","heif","jpeg","png","tiff","webp"])});function hx($=process.env){let _={...$};for(let F of Object.keys(_))if(yu0.has(F.toLocaleUpperCase("en-US")))delete _[F];return _}var yu0;var a60=Y6(()=>{yu0=new Set(["AI_GATEWAY_API_KEY","VERCEL_OIDC_TOKEN"])});import{AsyncLocalStorage as gu0}from"async_hooks";import{constants as MZ}from"fs";import{lstat as SM,mkdtemp as vu0,open as mx,rmdir as t60,unlink as xu0}from"fs/promises";import{join as ux}from"path";function J_0($){let _=$??[];if(_.length>lu0||_.some((F,Q)=>!Number.isSafeInteger(F)||F<0||F>2147483647||_.indexOf(F)!==Q))throw new F0("invalid_input","Inherited vectorizer descriptors must be unique bounded integers.");return Object.freeze([..._])}async function X_0($,_){let F=J_0($);return await Z_0.run(F,async()=>await _())}function Y_0(){if(process.platform==="win32"||__0)return;__0=!0;let $=[["SIGHUP",129],["SIGINT",130],["SIGTERM",143]];for(let[_,F]of $)process.once(_,()=>{for(let Q of lx)j_0(Q,"SIGKILL");process.exit(F)})}async function vM($,_,F,Q={}){return sx($,_,F,Q)}async function H_0($,_,F,Q){if(process.platform==="win32")throw new F0("tool_platform","Bounded pathname output is unavailable on Windows.",{platform:process.platform});if(K_0(Q.maxOutputBytes,"The command primary-output limit must be positive."),_<1)throw new F0("timeout","VTracer exceeded the conversion time limit.");let Z=performance.now(),J,X,Y,H,K,j=$_0;try{Y=await vu0(ux(Q.temporaryRoot,"slopcamera-command-output-")),X=await mx(Y,MZ.O_RDONLY|MZ.O_DIRECTORY|MZ.O_NOFOLLOW),await cu0(Y,X);let N=ux(Y,"output.svg");await du0(N,F_0(Z,_),F),J=await mx(N,MZ.O_RDWR|MZ.O_NONBLOCK|MZ.O_NOFOLLOW),H=await mx(N,MZ.O_RDONLY|MZ.O_NONBLOCK|MZ.O_NOFOLLOW),await pu0(N,J,H),await X.chmod(320);let V=await sx($(N),F_0(Z,_),F,Q,{handle:H,maximumBytes:Q.maxOutputBytes});if(V.primaryOutput===void 0)throw new F0(F,"VTracer did not expose its bounded primary output.");K={output:V.primaryOutput,stderr:V.stderr,stdout:V.stdout}}catch(N){j=N instanceof F0?N:px(F,N)}let W=await su0(Y,Y===void 0?void 0:ux(Y,"output.svg"),H,J,X);if(j!==$_0){if(W===void 0)throw j;throw new F0(j instanceof F0?j.code:F,`${j instanceof Error?j.message:String(j)} The temporary primary-output endpoint also could not be removed.`,{...j instanceof F0?j.details:{},cleanup:String(W)},{cause:j instanceof Error?j:void 0})}if(W!==void 0)throw new F0(F,"The temporary primary-output endpoint could not be removed.",{cleanup:String(W)},{cause:W instanceof Error?W:void 0});if(K===void 0)throw new F0(F,"The bounded command completed without a result.");return K}async function sx($,_,F,Q,Z){if($.length===0)throw new F0("invalid_input","A bounded command requires a command.");if(_<1)throw new F0("timeout","VTracer exceeded the conversion time limit.");let J=Q.maxStdoutBytes??fM;K_0(J,"The command stdout limit must be positive.");let X=J_0(Q.inheritedFileDescriptors??Z_0.getStore()),Y,H=process.platform!=="win32";try{Y=Bun.spawn([...$],{detached:H,env:hx(),stdio:[Q.stdin??"ignore","pipe","pipe",...X],windowsHide:!0})}catch(C){throw px(F,C)}if(process.platform!=="win32")lx.add(Y.pid);let K=new AbortController,j=!1,W=Y.exited.then((C)=>{if(j=!0,H&&process.platform!=="win32")dx(Y,"SIGKILL");return C}),N=gM(Y.stdout,J,K.signal,"VTracer emitted too much primary output."),V=gM(Y.stderr,fM,K.signal,"VTracer emitted too much diagnostic output."),O=Z===void 0?Promise.resolve(void 0):ou0(Z.handle,Z.maximumBytes,K.signal,()=>j),D=Promise.all([W,N,V,O]).then(([C,B,U,z])=>{if(C!==0)throw new F0(F,[`Command failed (${C}): ${$[0]??"unknown"}`,U.trim()].filter(Boolean).join(` -`),{exitCode:C});return z===void 0?{stderr:U,stdout:B}:{primaryOutput:z,stderr:U,stdout:B}}),w,L=new Promise((C,B)=>{w=setTimeout(()=>B(e60),_)});try{return await Promise.race([D,L])}catch(C){let B;try{await nu0(Y,H)}catch(U){B=U}finally{K.abort(),await nx(Promise.allSettled([N,V,O]),_D)}if(C===e60)throw new F0("timeout",B===void 0?"VTracer exceeded the conversion time limit.":"VTracer exceeded the conversion time limit and did not terminate cleanly.",B===void 0?{}:{cleanup:String(B)});if(C instanceof F0)throw C;if(B instanceof F0)throw B;throw px(F,C)}finally{if(w!==void 0)clearTimeout(w);lx.delete(Y.pid)}}function K_0($,_){if(!Number.isSafeInteger($)||$<1)throw new F0("invalid_input",_)}function F_0($,_){return Math.floor(_-(performance.now()-$))}async function du0($,_,F){await sx(["mkfifo","-m","600",$],_,F,{})}async function cu0($,_){let[F,Q]=await Promise.all([SM($),_.stat()]);if(!F.isDirectory()||!Q.isDirectory()||F.dev!==Q.dev||F.ino!==Q.ino)throw new F0("trace_failed","The bounded primary-output directory changed during setup.")}async function pu0($,_,F){let[Q,Z,J]=await Promise.all([SM($),_.stat(),F.stat()]);if(!Q.isFIFO()||!Z.isFIFO()||!J.isFIFO()||Q.dev!==Z.dev||Q.ino!==Z.ino||Z.dev!==J.dev||Z.ino!==J.ino)throw new F0("trace_failed","The bounded primary-output endpoint changed during setup.")}async function su0($,_,F,Q,Z){let J;if($!==void 0&&Z===void 0)try{await t60($)}catch(Y){J??=Y}if($!==void 0&&Z!==void 0){await Z.chmod(448).catch((Y)=>{J??=Y});try{let[Y,H]=await Promise.all([SM($),Z.stat()]);if(!Y.isDirectory()||!H.isDirectory()||Y.dev!==H.dev||Y.ino!==H.ino)throw new F0("trace_failed","The bounded primary-output directory changed before cleanup.");if(_!==void 0&&J===void 0){let K=await SM(_).catch((W)=>{if(W_0(W,"ENOENT"))return;throw W}),j=await(F??Q)?.stat();if(K===void 0&&j!==void 0)throw new F0("trace_failed","The bounded primary-output endpoint disappeared before cleanup.");if(K!==void 0){if(!K.isFIFO()||j!==void 0&&(K.dev!==j.dev||K.ino!==j.ino))throw new F0("trace_failed","The bounded primary-output endpoint changed before cleanup.");await xu0(_)}}if(J===void 0)await t60($)}catch(Y){J??=Y}}let X=await Promise.allSettled([F?.close(),Q?.close(),Z?.close()]);return J??=X.find((Y)=>Y.status==="rejected")?.reason,J}async function nu0($,_){if(process.platform==="win32")await ru0($.pid);else if(!_)Q_0($,"SIGTERM"),await cx(yM),Q_0($,"SIGKILL");else dx($,"SIGTERM"),await cx(yM),dx($,"SIGKILL");if(await nx($.exited,_D))return;throw new F0("trace_failed","VTracer did not exit after forced termination.")}function Q_0($,_){try{$.kill(_)}catch{}}function dx($,_){if(!j_0($.pid,_))try{$.kill(_)}catch{}}function j_0($,_){try{return process.kill(-$,_),!0}catch{return!1}}async function ru0($){let _;try{_=Bun.spawn(["taskkill.exe","/PID",String($),"/T","/F"],{detached:!1,env:hx(),stderr:"pipe",stdin:"ignore",stdout:"pipe",windowsHide:!0})}catch{return}let F=Promise.all([gM(_.stdout,fM,AbortSignal.timeout(_D),"Process-tree cleanup emitted too much output."),gM(_.stderr,fM,AbortSignal.timeout(_D),"Process-tree cleanup emitted too much output.")]);if(!await nx(Promise.all([_.exited,F]),_D))try{_.kill("SIGKILL")}catch{}}async function nx($,_){return new Promise((F)=>{let Q=!1,Z=setTimeout(()=>J(!1),_);$.then(()=>J(!0),()=>J(!0));function J(X){if(Q)return;Q=!0,clearTimeout(Z),F(X)}})}async function gM($,_,F,Q){let Z=$.getReader(),J=[],X=0,Y=()=>{Z.cancel().catch(()=>{return})};F.addEventListener("abort",Y,{once:!0});try{while(!0){let{done:H,value:K}=await Z.read();if(H)break;if(X+=K.byteLength,X>_)throw await Z.cancel(),new F0("output_limit",Q,{bytes:X,maximumBytes:_});J.push(K)}return Buffer.concat(J,X).toString("utf8")}finally{F.removeEventListener("abort",Y),Z.releaseLock()}}async function ou0($,_,F,Q){let Z=Buffer.allocUnsafe(Math.min(hu0,_+1)),J=[],X=0,Y=0;while(!F.aborted){let H=Math.min(Z.byteLength,_-X+1);try{let{bytesRead:K}=await $.read(Z,0,H,null);if(K>0){if(Y=0,X+=K,X>_)throw new F0("output_limit","VTracer emitted too much primary output.",{bytes:X,maximumBytes:_});J.push(Uint8Array.from(Z.subarray(0,K)));continue}}catch(K){if(!iu0(K))throw K}if(Q()){if(Y+=1,Y>=uu0)break}else Y=0;await cx(mu0)}return Buffer.concat(J,X).toString("utf8")}function iu0($){return W_0($)&&["EAGAIN","EINTR","EWOULDBLOCK"].includes(String($.code))}function W_0($,_){return $ instanceof Error&&"code"in $&&(_===void 0||$.code===_)}function cx($){return new Promise((_)=>{setTimeout(_,$)})}function px($,_){return new F0($,"VTracer could not be executed.",{},{cause:_ instanceof Error?_:Error(String(_))})}var fM=65536,hu0=65536,mu0=2,uu0=2,yM=50,_D=500,lu0=16,e60,$_0,lx,__0=!1,Z_0;var FD=Y6(()=>{a60();k_();e60=Symbol("bounded-command-timeout"),$_0=Symbol("no-command-failure"),lx=new Set,Z_0=new gu0});function rx($,_,F,Q){if(eu0.test($))throw new F0("unsafe_svg","VTracer output contains active or referenced SVG content.");let Z=[],J=/]*)\/?\s*>/gu;for(let X of $.matchAll(J)){if(Z.length>=Q)throw new F0("output_limit",`VTracer output exceeds the ${Q}-path limit.`);let Y=X[1],H=Y.match(/(?:^|\s)d="([^"]*)"/u)?.[1],K=Y.match(/(?:^|\s)fill="([^"]+)"/u)?.[1],j=Y.match(/(?:^|\s)transform="([^"]+)"/u)?.[1];if(H==="")continue;if(H===void 0||H.includes("&")||!au0.test(H))throw new F0("unsafe_svg","VTracer output contains unsupported path data.");if(K===void 0)throw new F0("unsafe_svg","VTracer output contains a path without a fill.");if(j!==void 0&&!tu0.test(j))throw new F0("unsafe_svg","VTracer output contains an unsupported path transform.");let W=T6(K);Z.push({d:H,fill:W,rgb:ew(W),...j===void 0?{}:{transform:j}})}if(Z.length===0)throw new F0("trace_failed","VTracer output did not contain visible paths.");return{paths:Z,svg:ox(Z,_,F)}}function ox($,_,F,Q){let Z=Q===void 0?void 0:[T6(Q.palette[0]),T6(Q.palette[1])],J=$.map(({d:Y,fill:H,rgb:K,transform:j})=>{let W=Q===void 0||Z===void 0?H:xx(K,Q.model)?Z[0]:Z[1];return` `}),X=[``,...J,"",""].join(` -`);return xM(X),X}function ix($,_,F,Q,Z){if(_.some(({rgb:K})=>K[0]!==K[1]||K[1]!==K[2]))throw new F0("unsafe_svg","An alpha mask may contain only grayscale fills.");let J=Z===void 0?void 0:[T6(Z.palette[0]),T6(Z.palette[1])],X=_.map(({d:K,fill:j,transform:W})=>`${K} -${j} -${W??""}`).join(` -`),Y=`alpha-${AZ(X).slice(0,16)}`,H=[``," ",` `,` `,..._.map(({d:K,fill:j,transform:W})=>` `)," "," ",` `,...$.map(({d:K,fill:j,rgb:W,transform:N})=>{let V=Z===void 0||J===void 0?j:xx(W,Z.model)?J[0]:J[1];return` `})," ","",""].join(` -`);return xM(H),H}function V_0($){return $.match(/{lW();k_();au0=/^[MmZzLlHhVvCcSsQqTtAaEe0-9+,.\s-]+$/u,tu0=/^translate\(\s*[-+0-9.eE]+\s*(?:,\s*|\s+)[-+0-9.eE]+\s*\)$/u,eu0=/W===0))break;let Z=ax(Q.subarray(0,100)),J=ax(Q.subarray(345,500)),X=J===""?Z:`${J}/${Z}`,Y=ax(Q.subarray(124,136)).trim();if(!/^[0-7]+$/u.test(Y))throw new F0("tool_integrity","VTracer tar contains an invalid size.");let H=Number.parseInt(Y,8);if(!Number.isSafeInteger(H)||H<0||H>hM)throw new F0("tool_integrity","VTracer tar entry exceeds its size limit.");let K=F+512,j=K+H;if(j>$.length)throw new F0("tool_integrity","VTracer tar entry is truncated.");if(X===_||X.endsWith(`/${_}`))return Uint8Array.from($.subarray(K,j));F=K+Math.ceil(H/512)*512}throw new F0("tool_integrity",`VTracer archive omitted ${_}.`)}function Ql0($,_){let F=Buffer.from($),Q=0;while(Q+30<=F.length){if(F.readUInt32LE(Q)!==67324752)break;let J=F.readUInt16LE(Q+6),X=F.readUInt16LE(Q+8),Y=F.readUInt32LE(Q+18),H=F.readUInt32LE(Q+22),K=F.readUInt16LE(Q+26),j=F.readUInt16LE(Q+28);if((J&1)!==0||(J&8)!==0)throw new F0("tool_integrity","VTracer zip uses encryption or an unsupported data descriptor.");if(H>hM)throw new F0("tool_integrity","VTracer zip entry exceeds its size limit.");let W=Q+30,N=W+K+j,V=N+Y;if(V>F.length)throw new F0("tool_integrity","VTracer zip entry is truncated.");let O=F.subarray(W,W+K).toString("utf8");if(O===_||O.endsWith(`/${_}`)){let D=F.subarray(N,V),w=X===0?Buffer.from(D):X===8?_l0(D,{maxOutputLength:hM}):void 0;if(w===void 0)throw new F0("tool_integrity",`VTracer zip uses unsupported compression method ${X}.`);if(w.length!==H)throw new F0("tool_integrity","VTracer zip size does not match its header.");return Uint8Array.from(w)}Q=V}throw new F0("tool_integrity",`VTracer archive omitted ${_}.`)}function ax($){let _=$.indexOf(0);return Buffer.from(_===-1?$:$.subarray(0,_)).toString("ascii")}var hM=8388608;var O_0=Y6(()=>{k_()});import{createHash as D_0,randomUUID as L_0}from"crypto";import{constants as mM}from"fs";import{chmod as C_0,mkdir as B_0,open as ex,realpath as U_0,rename as Zl0,rm as lM,writeFile as Jl0}from"fs/promises";import{homedir as tx}from"os";import{dirname as Xl0,join as dW,resolve as uM}from"path";function k_0($){return process.env[$]}async function A_0($,_,F){let Q=k_0("SLOPCAMERA_VTRACER_PATH");if(Q!==void 0)return w_0(uM(Q),uM(_),"override",$);let Z=`${process.platform}-${process.arch}`,J=Yl0[Z];if(J===void 0)throw new F0("tool_platform",`VTracer is not pinned for ${process.platform}/${process.arch}.`,{arch:process.arch,platform:process.platform});let X=uM(F??Kl0()),Y=process.platform==="win32"?".exe":"",H=dW(X,"tools",`vtracer-${TZ}-${process.platform}-${process.arch}${Y}`);if(await $h(H,$)!==J.binarySha256)await Vl0(H),await jl0(H,J,$);return w_0(H,uM(_),"official-release",$,J.binarySha256)}function Kl0(){let $=k_0("SLOPCAMERA_CACHE_DIR");if($!==void 0&&$.trim()!=="")return $;if(process.platform==="win32")return dW(process.env.LOCALAPPDATA??tx(),"slopcamera");if(process.platform==="darwin")return dW(tx(),"Library","Caches","slopcamera");return dW(process.env.XDG_CACHE_HOME??dW(tx(),".cache"),"slopcamera")}async function jl0($,_,F){F.assert("VTracer download");let Q=await Ol0(_.url,F,Hl0),Z=AZ(Q);if(Z!==_.archiveSha256)throw new F0("tool_integrity",`VTracer archive checksum mismatch: ${Z}`,{actual:Z,expected:_.archiveSha256});let J=G_0(Q,_.format);if(J.byteLength<1||J.byteLength>zX)throw new F0("tool_integrity","VTracer binary exceeds its verified installation limit.",{bytes:J.byteLength,maximumBytes:zX});let X=AZ(J);if(X!==_.binarySha256)throw new F0("tool_integrity",`VTracer binary checksum mismatch: ${X}`,{actual:X,expected:_.binarySha256});F.assert("VTracer installation"),await B_0(Xl0($),{recursive:!0});let Y=`${$}.${L_0()}.tmp`;try{if(await Jl0(Y,J,{flag:"wx",mode:448}),process.platform!=="win32")await C_0(Y,320);F.assert("VTracer installation");try{await Zl0(Y,$)}catch(K){if(await $h($,F)===_.binarySha256)await lM(Y,{force:!0});else throw K}}catch(K){if(await lM(Y,{force:!0}),K instanceof F0)throw K;throw new F0("tool_integrity","Could not publish the verified VTracer binary.",{},{cause:K})}let H=await $h($,F);if(H!==_.binarySha256)throw new F0("tool_integrity","Installed VTracer differs from the verified release binary.",{actual:H,expected:_.binarySha256})}async function w_0($,_,F,Q,Z){Q.assert("VTracer private copy"),await B_0(_,{mode:448,recursive:!0});let J=process.platform==="win32"?".exe":"",X=dW(_,`vtracer-${L_0()}${J}`),Y=F==="official-release"?"tool_integrity":"tool_version",H,K,j;try{let W=await U_0($);Q.assert("VTracer private copy"),H=await ex(W,M_0());let N=await H.stat();Gl0(N,$),K=await ex(X,"wx",320),j=await Nl0(H,K,zX,Q),await K.sync(),Q.assert("VTracer private copy")}catch(W){if(W instanceof F0)throw W;throw new F0(Y,"VTracer could not be copied into the private conversion directory.",{},{cause:W})}finally{await Promise.allSettled([H?.close(),K?.close()])}try{if(j.length===0)throw new F0(Y,"VTracer executable is empty.");if(Z!==void 0&&j!==Z)throw new F0("tool_integrity","VTracer changed before it could be copied for conversion.",{actual:j,expected:Z});if(process.platform!=="win32")await C_0(X,320);let W=await _h(X,zX,Q,Y);if(W!==j)throw new F0("tool_integrity","The private VTracer copy failed its integrity check.",{actual:W,expected:j});return Wl0(X,F,Q,j,Z)}catch(W){throw await lM(X,{force:!0}),W}}async function Wl0($,_,F,Q,Z){F.assert("VTracer inspection");let{stderr:J,stdout:X}=await vM([$,"--version"],F.remainingMs(),"tool_version");if(!new RegExp(`\\b${TZ.replaceAll(".","\\.")}\\b`,"u").test(`${X} -${J}`))throw new F0("tool_version",`Expected VTracer ${TZ}.`);let Y=await _h($,zX,F,"tool_integrity");if(Y!==Q||Z!==void 0&&Y!==Z)throw new F0("tool_integrity","The private VTracer executable changed during inspection.",{actual:Y,expected:Z??Q});return{path:$,sha256:Y,source:_,version:TZ}}async function $h($,_){try{return await _h($,zX,_,"tool_integrity")}catch(F){if(F instanceof F0&&F.code==="timeout")throw F;if(wl0(F,"ENOENT"))return;return}}async function Vl0($){try{await lM($,{force:!0})}catch(_){throw new F0("tool_integrity","Could not remove an invalid cached VTracer binary.",{},{cause:_})}}async function _h($,_,F,Q){let Z;try{F.assert("VTracer hash");let J=await U_0($);F.assert("VTracer hash"),Z=await ex(J,M_0());let X=await Z.stat();if(!X?.isFile()||X.size<1||X.size>_)throw new F0(Q,"VTracer must be a non-empty regular file within its size limit.",{bytes:X?.size??0,maximumBytes:_});let Y=D_0("sha256"),H=Buffer.allocUnsafe(Math.min(z_0,_+1)),K=0;while(!0){F.assert("VTracer hash");let j=Math.min(H.byteLength,_-K+1),{bytesRead:W}=await Z.read(H,0,j,null);if(W===0)break;if(K+=W,K>_)throw new F0(Q,"VTracer grew beyond its executable size limit while being hashed.",{bytes:K,maximumBytes:_});Y.update(H.subarray(0,W))}return F.assert("VTracer hash"),Y.digest("hex")}catch(J){if(J instanceof F0)throw J;throw new F0(Q,"VTracer executable could not be read safely.",{},{cause:J})}finally{await Z?.close().catch(()=>{return})}}async function Nl0($,_,F,Q){let Z=D_0("sha256"),J=Buffer.allocUnsafe(Math.min(z_0,F+1)),X=0;while(!0){Q.assert("VTracer private copy");let Y=Math.min(J.byteLength,F-X+1),{bytesRead:H}=await $.read(J,0,Y,null);if(H===0)break;if(X+=H,X>F)throw new F0("tool_integrity","VTracer grew beyond its executable size limit while being copied.",{bytes:X,maximumBytes:F});Z.update(J.subarray(0,H));let K=0;while(KzX)throw new F0("tool_version",`VTracer must be a non-empty regular file: ${_}`,{bytes:$?.size??0,maximumBytes:zX})}async function Ol0($,_,F){let Q=new AbortController,Z=setTimeout(()=>Q.abort(),_.remainingMs());try{let J=await fetch($,{headers:{"user-agent":"hraness-slopcamera-vectorizer"},redirect:"follow",signal:Q.signal});if(!J.ok||J.body===null)throw new F0("tool_download",`Could not download VTracer: ${J.status} ${J.statusText}`);let X=Number.parseInt(J.headers.get("content-length")??"",10);if(Number.isFinite(X)&&X>F)throw new F0("tool_download","VTracer archive exceeds its download limit.");let Y=[],H=J.body.getReader(),K=0;while(!0){_.assert("VTracer download");let{done:j,value:W}=await H.read();if(j)break;if(K+=W.byteLength,K>F)throw await H.cancel(),new F0("tool_download","VTracer archive exceeds its download limit.");Y.push(W)}return Uint8Array.from(Buffer.concat(Y,K))}catch(J){if(J instanceof F0)throw J;if(Q.signal.aborted)throw new F0("timeout","VTracer download exceeded the conversion time limit.");throw new F0("tool_download","Could not download VTracer.",{},{cause:J})}finally{clearTimeout(Z)}}function wl0($,_){return $ instanceof Error&&"code"in $&&$.code===_}function M_0(){if(process.platform==="win32")return mM.O_RDONLY;return mM.O_RDONLY|mM.O_NONBLOCK|mM.O_NOFOLLOW}var TZ="0.6.4",QD=($)=>Object.freeze($),Yl0,Hl0=4194304,zX=16777216,z_0=65536;var T_0=Y6(()=>{O_0();FD();lW();k_();Yl0=Object.freeze({"darwin-arm64":QD({archiveSha256:"4a597fd2df8b961d60620df40a7436109427d86e5c028758e6e8796b02d3d996",binarySha256:"77e495bbe212448240387fba3b6d8bc62ba20ecfb6f3c22967e51600f1cc6e66",format:"tar.gz",url:`https://github.com/visioncortex/vtracer/releases/download/${TZ}/vtracer-aarch64-apple-darwin.tar.gz`}),"darwin-x64":QD({archiveSha256:"f0d755292c2602d772d63d658a3498b23eca8b5620d4b92a991bd035d5abed16",binarySha256:"0f9f88f989b757e27973a5c4b42665153070183d0787656ee8af2249ab326b78",format:"tar.gz",url:`https://github.com/visioncortex/vtracer/releases/download/${TZ}/vtracer-x86_64-apple-darwin.tar.gz`}),"linux-arm64":QD({archiveSha256:"cbd05ad4f491d12dd139ada61485ca1d24db9f981cbe1658632a083cd0ac1a71",binarySha256:"a4b33b6c4066a6b9187802c6efc8b89e211318e12a17164b9d1dd1f29ac5e502",format:"tar.gz",url:`https://github.com/visioncortex/vtracer/releases/download/${TZ}/vtracer-aarch64-unknown-linux-musl.tar.gz`}),"linux-x64":QD({archiveSha256:"9290ba0c90e224d6d212836dff5491407c1718bcb72f80b2b5a4a01816df5e40",binarySha256:"6f31499257076bd94de3e976844cf7ca5643f1e194a2bf0599b13f3719452aec",format:"tar.gz",url:`https://github.com/visioncortex/vtracer/releases/download/${TZ}/vtracer-x86_64-unknown-linux-musl.tar.gz`}),"win32-x64":QD({archiveSha256:"6b5bc17a6b017129ee40461df254f65d16f3b494c001a8541d41861066b716bf",binarySha256:"4ad8d35e566cd15caf582063b8349bd082b8fa2bd461e99d116fc63ad8fdeca0",format:"zip",url:`https://github.com/visioncortex/vtracer/releases/download/${TZ}/vtracer-x86_64-pc-windows-msvc.zip`})})});var kX=1,LK,ZD;var Fh=Y6(()=>{uW();LK=Math.ceil(A_.maxInputBytes/3)*4+524288,ZD=A_.maxOutputBytes*2+524288});import{fileURLToPath as Dl0}from"url";import{mkdtemp as Ll0,rm as Cl0}from"fs/promises";import{tmpdir as Bl0}from"os";import{dirname as R_0,join as Qh}from"path";async function E_0($,_){let F=performance.now(),Q=mW(_.limits);if(process.platform==="win32")throw new F0("tool_platform","Bounded VTracer streaming is unavailable on Windows.",{platform:process.platform});let Z=Tl0($,Q.maxInputBytes),J=await Ll0(Qh(Bl0(),"slopcamera-vectorize-")),X;try{X=await Al0(Z,_,Q,F,J)}finally{await Ml0(J)}if(performance.now()-F>=Q.maxDurationMs)throw new F0("timeout","Vectorization exceeded the conversion time limit during cleanup.");return X}async function Al0($,_,F,Q,Z){let J=F.maxDurationMs-(performance.now()-Q);if(J<=dM+q_0)throw new F0("timeout","Vectorization has no remaining budget for isolated worker execution.");let X=Math.floor(J-dM-q_0),Y=El0(_.inheritedFileDescriptors),H=Y.map((O,D)=>D+3),K={input:$,options:ql0(_,X,H),protocol:kX,temporaryRoot:Z},j=Buffer.from(JSON.stringify(K));if(j.byteLength>LK)throw new F0("input_limit","The vectorization worker request exceeds its IPC limit.",{bytes:j.byteLength,maximumBytes:LK});let W=F.maxDurationMs-(performance.now()-Q);if(W<=dM)throw new F0("timeout","Vectorization has no remaining budget for isolated worker startup and cleanup.");let{stdout:N}=await vM([process.execPath,Pl0()],Math.floor(W-dM),"trace_failed",{...Y.length===0?{}:{inheritedFileDescriptors:Y},maxStdoutBytes:ZD,stdin:j});if(performance.now()-Q>=F.maxDurationMs)throw new F0("timeout","Vectorization exceeded the conversion time limit.");let V=Il0(N);if(!V.ok)throw new F0(V.error.code,V.error.message,V.error.details);if(bl0(V.result,F.maxOutputBytes),performance.now()-Q>=F.maxDurationMs)throw new F0("timeout","Vectorization exceeded the conversion time limit.");return V.result}async function Ml0($){try{await Cl0($,{force:!0,recursive:!0})}catch(_){throw new F0("trace_failed","The isolated vectorization directory could not be removed.",{temporaryRoot:$},{cause:_})}}function Tl0($,_){return typeof $==="string"?{kind:"path",value:$}:Rl0($,_)}function Rl0($,_){let F=$ instanceof ArrayBuffer?new Uint8Array($):$;if(F.byteLength<1)throw new F0("invalid_input","Raster input is empty.");if(F.byteLength>_)throw new F0("input_limit",`Raster input exceeds the ${_}-byte limit.`,{bytes:F.byteLength,maximumBytes:_});return{kind:"bytes",value:Buffer.from(F.buffer,F.byteOffset,F.byteLength).toString("base64")}}function ql0($,_,F){return{...$.alphaCutoff===void 0?{}:{alphaCutoff:$.alphaCutoff},...$.cacheDirectory===void 0?{}:{cacheDirectory:$.cacheDirectory},...$.duotone===void 0?{}:{duotone:[$.duotone[0],$.duotone[1]]},...F.length===0?{}:{inheritedFileDescriptors:F},limits:{...$.limits,maxDurationMs:_},...$.outputPath===void 0?{}:{outputPath:$.outputPath}}}function El0($){let _=$??[];if(_.length>16||_.some((F,Q)=>!Number.isSafeInteger(F)||F<0||F>2147483647||_.indexOf(F)!==Q))throw new F0("invalid_input","Inherited vectorizer descriptors must be unique bounded integers.");return Object.freeze([..._])}function Pl0(){let $=Dl0(import.meta.url);return $.endsWith(".ts")?Qh(R_0($),"worker.ts"):Qh(R_0($),"vectorize","worker.js")}function Il0($){let _;try{_=JSON.parse($)}catch(F){throw new F0("trace_failed","The vectorization worker returned malformed output.",{},{cause:F})}if(!JD(_)||_.protocol!==kX)throw new F0("trace_failed","The vectorization worker returned an incompatible response.");if(_.ok===!0&&Sl0(_.result))return _;if(_.ok===!1&&JD(_.error)&&typeof _.error.code==="string"&&Ul0.has(_.error.code)&&typeof _.error.message==="string"&&JD(_.error.details))return _;throw new F0("trace_failed","The vectorization worker returned an invalid response.")}function bl0($,_){let F=Buffer.byteLength($.svg);if(F<1||F>_||$.receipt.bytes!==F||$.receipt.svgSha256.length!==64)throw new F0("trace_failed","The vectorization worker response violates its output contract.",{bytes:F,maximumOutputBytes:_})}function Sl0($){if(!JD($)||$.outputPath!==null&&typeof $.outputPath!=="string"||typeof $.svg!=="string"||!JD($.receipt))return!1;let _=$.receipt.profile;return typeof $.receipt.bytes==="number"&&typeof $.receipt.svgSha256==="string"&&typeof _==="string"&&yx.includes(_)}function JD($){return typeof $==="object"&&$!==null&&!Array.isArray($)}var Ul0,zl0=250,kl0=2,dM,q_0=150;var P_0=Y6(()=>{FD();uW();Fh();k_();Ul0=new Set(["input_limit","invalid_input","output_limit","quality_limit","timeout","tool_download","tool_integrity","tool_platform","tool_version","trace_failed","unsafe_svg"]),dM=kl0*(yM+zl0)});import{randomUUID as fl0}from"crypto";import{mkdir as yl0,rename as gl0,rm as vl0,writeFile as Yh}from"fs/promises";import{dirname as xl0,join as I_0,resolve as hl0}from"path";function EF($,_={}){return E_0($,_)}async function b_0($,_={},F){let Q=mW(_.limits),Z=new tw(Q.maxDurationMs),J=_.alphaCutoff??ml0;if(!Number.isInteger(J)||J<1||J>64)throw new F0("invalid_input","alphaCutoff must be an integer from 1 through 64.");let X=_.duotone===void 0?void 0:[T6(_.duotone[0]),T6(_.duotone[1])],Y=await IM($,Q,Z);if(process.platform==="win32")throw new F0("tool_platform","Bounded VTracer streaming is unavailable on Windows.",{platform:process.platform});let H=await A_0(Z,F,_.cacheDirectory),K=[],j=[],W=dl0(Y.pixels,J);for(let z of W){Z.assert(`${z.name} trace`);let k=I_0(F,`${z.name}.png`);await Yh(k,await $D(z.pixels,Y.width,Y.height));let M=await Jh(Y,z,Zh[0],k,H,F,Q,Z,K);if(M!==void 0)j.push(M);if(M!==void 0&&sl0(M.quality))continue;let q=await Jh(Y,z,Zh[1],k,H,F,Q,Z,K);if(q!==void 0)j.push(q);if([M,q].some((A)=>A!==void 0&&Xh(A.quality)))continue;let E=await Jh(Y,z,Zh[2],k,H,F,Q,Z,K);if(E!==void 0)j.push(E)}let N=[...j].sort(nl0)[0];if(N!==void 0&&h60(Y.pixels)&&N.quality.alphaRmse>0.06){let z=await pl0(Y,N,H,F,Q,Z,K);if(z!==void 0)j.push(z)}let V=j.filter((z)=>z.bytes<=Q.maxOutputBytes&&z.pathCount<=Q.maxPaths&&Xh(z.quality));if(V.length===0)throw new F0("quality_limit","No adaptive vector candidate passed the fidelity and output gates.",{candidates:j.map(ol0),errors:K});let O=rl0(V),D=X===void 0?void 0:{model:p60(Y.pixels),palette:X},w=O.maskPaths===void 0?ox(O.artworkPaths,Y.width,Y.height,D):ix(O.artworkPaths,O.maskPaths,Y.width,Y.height,D);xM(w);let L=Buffer.byteLength(w),C=V_0(w);if(L>Q.maxOutputBytes||C>Q.maxPaths)throw new F0("output_limit",`Canonical SVG exceeds ${Q.maxOutputBytes} bytes or ${Q.maxPaths} paths.`,{bytes:L,pathCount:C});Z.assert("output publication");let B=_.outputPath===void 0?null:await il0(_.outputPath,w),U=i60();return{outputPath:B,receipt:{alphaCutoff:O.alphaCutoff,bytes:L,candidatesEvaluated:j.length,format:Y.format,height:Y.height,inputBytes:Y.inputBytes,outputMode:D===void 0?"color":"duotone",pathCount:C,profile:O.profile,provenance:{arch:process.arch,platform:process.platform,sharp:U.sharp,sharpVersions:U.sharpVersions,vips:U.vips,vtracerSha256:H.sha256,vtracerSource:H.source,vtracerVersion:H.version},quality:y_0(O.quality),receiptVersion:1,representation:O.representation,sourceSha256:Y.sourceSha256,svgSha256:AZ(w),width:Y.width},svg:w}}function dl0($,_){let F=gx($,_),Q=[];if(cl0(F))Q.push({alphaCutoff:_,name:`alpha-${_}`,pixels:F});if(_>1&&(Q.length===0||x60($,_)>=ul0))Q.push({alphaCutoff:1,name:"alpha-1",pixels:gx($,1)});if(Q.length===0)throw new F0("invalid_input","Alpha sanitation removed every visible pixel.");return Q}function cl0($){for(let _=3;_<$.length;_+=4)if($[_]>0)return!0;return!1}async function Jh($,_,F,Q,Z,J,X,Y,H){try{let K=await f_0(Z,Q,F.args,J,X.maxOutputBytes,Y);Y.assert(`${F.name} trace`);let j=rx(K,$.width,$.height,X.maxPaths),W=Buffer.byteLength(j.svg),N=j.paths.length;if(W>X.maxOutputBytes||N>X.maxPaths)throw new F0("output_limit",`${F.name} exceeds vector output limits.`);let V=await S_0(j.svg,$,X.maxDecodedPixels,Y);return{alphaCutoff:_.alphaCutoff,artworkPaths:j.paths,bytes:W,pathCount:N,profile:F.name,quality:V,representation:"color-paths",svg:j.svg}}catch(K){if(K instanceof F0&&K.code==="timeout")throw K;H.push(`${_.name}/${F.name}: ${K instanceof Error?K.message:String(K)}`);return}}async function pl0($,_,F,Q,Z,J,X){try{let Y=v60($.pixels),H=I_0(Q,"alpha-plane.png");await Yh(H,await $D(Y,$.width,$.height));let K=await f_0(F,H,ll0,Q,Z.maxOutputBytes,J),j=rx(K,$.width,$.height,Z.maxPaths),W=_.artworkPaths.length+j.paths.length;if(W>Z.maxPaths)throw new F0("output_limit","Alpha-masked output exceeds the path limit.");let N=ix(_.artworkPaths,j.paths,$.width,$.height),V=Buffer.byteLength(N);if(V>Z.maxOutputBytes)throw new F0("output_limit","Alpha-masked output exceeds the byte limit.");let O=await S_0(N,$,Z.maxDecodedPixels,J);return{alphaCutoff:_.alphaCutoff,artworkPaths:_.artworkPaths,bytes:V,maskPaths:j.paths,pathCount:W,profile:_.profile,quality:O,representation:"alpha-mask",svg:N}}catch(Y){if(Y instanceof F0&&Y.code==="timeout")throw Y;X.push(`alpha-mask: ${Y instanceof Error?Y.message:String(Y)}`);return}}async function S_0($,_,F,Q){let Z=await o60($,_.scoreWidth,_.scoreHeight,F);Q.assert("quality measurement");let J=d60(_.scorePixels,Z,_.scoreWidth,_.scoreHeight,1);return{alphaRmse:u60(_.scorePixels,Z),colorRmse:m60(_.scorePixels,Z),outsideAlphaRatio:J.outsideAlphaRatio,sampleHeight:_.scoreHeight,sampleWidth:_.scoreWidth,supportRecall:J.supportRecall}}async function f_0($,_,F,Q,Z,J){let{output:X}=await H_0((Y)=>[$.path,"--input",_,"--output",Y,...F],J.remainingMs(),"trace_failed",{maxOutputBytes:Z,temporaryRoot:Q});if(X.length===0)throw new F0("trace_failed","VTracer did not emit an SVG.");return X}function sl0($){return Xh($)&&$.colorRmse<=0.12&&$.alphaRmse<=0.18&&$.outsideAlphaRatio<=0.02&&$.supportRecall>=0.97}function Xh($){return $.colorRmse<=0.3&&$.alphaRmse<=0.3&&$.outsideAlphaRatio<=0.15&&$.supportRecall>=0.8}function nl0($,_){return $.quality.colorRmse-_.quality.colorRmse||$.quality.alphaRmse-_.quality.alphaRmse||$.bytes-_.bytes||$.pathCount-_.pathCount||$.profile.localeCompare(_.profile)}function rl0($){let _=Math.min(...$.map(({quality:Q})=>Q.colorRmse));return[...$.filter(({quality:Q})=>Q.colorRmse<=_+0.005)].sort((Q,Z)=>Q.bytes-Z.bytes||Q.pathCount-Z.pathCount||Q.quality.alphaRmse-Z.quality.alphaRmse||Q.profile.localeCompare(Z.profile)||Q.representation.localeCompare(Z.representation))[0]}function y_0($){let _=(F)=>Number(F.toFixed(8));return{alphaRmse:_($.alphaRmse),colorRmse:_($.colorRmse),outsideAlphaRatio:_($.outsideAlphaRatio),sampleHeight:$.sampleHeight,sampleWidth:$.sampleWidth,supportRecall:_($.supportRecall)}}function ol0($){return{alphaCutoff:$.alphaCutoff,bytes:$.bytes,pathCount:$.pathCount,profile:$.profile,quality:y_0($.quality),representation:$.representation}}async function il0($,_){let F=hl0($);await yl0(xl0(F),{recursive:!0});let Q=`${F}.${fl0()}.tmp`;try{await Yh(Q,_,{flag:"wx"}),await gl0(Q,F)}catch(Z){throw await vl0(Q,{force:!0}),new F0("output_limit",`Could not atomically write ${F}.`,{},{cause:Z})}return F}var ml0=8,ul0=0.002,Zh,ll0;var XD=Y6(()=>{FD();uW();lW();bM();N_0();T_0();P_0();k_();Zh=[{args:["--colormode","color","--hierarchical","stacked","--mode","spline","--filter_speckle","0","--color_precision","8","--gradient_step","4","--segment_length","4.5","--path_precision","4"],name:"balanced"},{args:["--colormode","color","--hierarchical","stacked","--mode","spline","--filter_speckle","0","--color_precision","8","--gradient_step","2","--segment_length","3.5","--path_precision","5"],name:"detailed"},{args:["--preset","photo","--hierarchical","stacked","--filter_speckle","0"],name:"photo"}],ll0=["--colormode","color","--hierarchical","cutout","--mode","spline","--filter_speckle","0","--color_precision","8","--gradient_step","2","--segment_length","3.5","--path_precision","5","--corner_threshold","30","--splice_threshold","20"]});var f91={};import{lstat as k91,realpath as Nj0}from"fs/promises";import{tmpdir as A91}from"os";import{basename as M91,dirname as T91}from"path";async function q91(){let $;try{r60();let F=await E91(),Q=P91(F),Z=I91(Q.input),J=await b91(Q.temporaryRoot),X=await X_0(Q.options.inheritedFileDescriptors,async()=>await b_0(Z,Q.options,J));$={ok:!0,protocol:kX,result:X}}catch(F){$={error:S91(F),ok:!1,protocol:kX}}let _=JSON.stringify($);if(Buffer.byteLength(_)>ZD)_=JSON.stringify({error:{code:"output_limit",details:{maximumBytes:ZD},message:"The vectorization worker response exceeds its IPC limit."},ok:!1,protocol:kX});await Bun.write(Bun.stdout,_)}async function E91(){let $=Bun.stdin.stream().getReader(),_=[],F=0;try{while(!0){let{done:Q,value:Z}=await $.read();if(Q)break;if(F+=Z.byteLength,F>LK)throw await $.cancel(),new F0("input_limit","The vectorization worker request exceeds its IPC limit.",{bytes:F,maximumBytes:LK});_.push(Z)}}finally{$.releaseLock()}return Buffer.concat(_,F).toString("utf8")}function P91($){let _;try{_=JSON.parse($)}catch(F){throw new F0("invalid_input","The vectorization worker request is malformed.",{},{cause:F})}if(!ed(_)||_.protocol!==kX||!ed(_.input)||!ed(_.options)||typeof _.temporaryRoot!=="string"||_.temporaryRoot.length===0||typeof _.input.kind!=="string"||typeof _.input.value!=="string"||!["bytes","path"].includes(_.input.kind))throw new F0("invalid_input","The vectorization worker request is invalid.");return _}function I91($){if($.kind==="path")return $.value;if($.value.length>Math.ceil(LK/3)*4||$.value.length%4!==0)throw new F0("input_limit","Encoded raster input exceeds its IPC limit.");let _=Buffer.from($.value,"base64");if(_.toString("base64")!==$.value)throw new F0("invalid_input","Encoded raster input is not canonical base64.");return _}async function b91($){let _,F;try{let Q=await Promise.all([Nj0($),Nj0(A91())]);_=Q[0],F=Q[1];let Z=await k91($);if(!Z.isDirectory()||Z.isSymbolicLink()||T91(_)!==F||!/^slopcamera-vectorize-[A-Za-z0-9_-]{6,}$/u.test(M91(_)))throw new F0("invalid_input","The supervisor temporary directory is invalid.")}catch(Q){if(Q instanceof F0)throw Q;throw new F0("invalid_input","The supervisor temporary directory could not be verified.",{},{cause:Q})}return _}function S91($){let _=$ instanceof F0&&R91.has($.code)?$.code:"trace_failed",F=$ instanceof Error?$.message:"The vectorization worker failed.",Q=$ instanceof F0&&Buffer.byteLength(JSON.stringify($.details))<=65536?$.details:{};return{code:_,details:Q,message:F}}function ed($){return typeof $==="object"&&$!==null&&!Array.isArray($)}var R91;var Gj0=Y6(async()=>{Fh();k_();FD();XD();bM();R91=new Set(["input_limit","invalid_input","output_limit","quality_limit","timeout","tool_download","tool_integrity","tool_platform","tool_version","trace_failed","unsafe_svg"]);Y_0();await q91()});import{maybeShowSupportInvitation as Uj0,runSupportCommand as zj0}from"@hraness/support-foundation/node";var Zc={id:"slopcamera",name:"Slopcamera",valueProposition:"Support ongoing development of local media tools for agents.",updates:!1};function Jc(){let $={...process.env};return process.env.HRANESS_SUPPORT_AUDIENCE="off",process.env.HRANESS_SUPPORT_EMAIL="off",$}async function Xc($,_={}){let F=await zj0(Zc,$,{command:["slopcamera"],..._,gitEmail:!1});if(F.stdout!=="")process.stdout.write(F.stdout);if(F.stderr!=="")process.stderr.write(F.stderr);return F.exitCode}async function Yc($={}){try{await Uj0(Zc,{usefulResult:!0,command:["slopcamera"],...$,gitEmail:!1})}catch{}}class l0 extends Error{code;details;constructor($,_,F){super(_);this.name="SlopcameraCodeError",this.code=$,this.details=F===void 0?void 0:Object.freeze({...F})}}function HE($){return $ instanceof Error?$.message:String($)}function Hc($){if($ instanceof l0)return $;return new l0("internal",HE($))}function KE($,_=Number.MAX_SAFE_INTEGER){if(!Number.isSafeInteger(_)||_<0)return;let F=2;if(F>_)return;for(let Q=0;Q<$.length;Q+=1){let Z=$.charCodeAt(Q),J;if(Z===34||Z===92||Z===8||Z===9||Z===10||Z===12||Z===13)J=2;else if(Z<32)J=6;else if(Z<128)J=1;else if(Z<2048)J=2;else if(Z>=55296&&Z<=56319){let X=$.charCodeAt(Q+1);if(X>=56320&&X<=57343)J=4,Q+=1;else J=6}else if(Z>=56320&&Z<=57343)J=6;else J=3;if(J>_-F)return;F+=J}return F}function Kc($,_=Number.MAX_SAFE_INTEGER){if(!Number.isSafeInteger(_)||_<0)return;let F=0;for(let Q=0;Q<$.length;Q+=1){let Z=$.charCodeAt(Q),J;if(Z<128)J=1;else if(Z<2048)J=2;else if(Z>=55296&&Z<=56319){let X=$.charCodeAt(Q+1);if(X>=56320&&X<=57343)J=4,Q+=1;else J=3}else J=3;if(J>_-F)return;F+=J}return F}import{sha256 as kj0}from"@noble/hashes/sha2.js";import{bytesToHex as Aj0}from"@noble/hashes/utils.js";var Mj0=new TextEncoder;function A8(){if(typeof Bun<"u"){let _=new Bun.CryptoHasher("sha256");return{digestHex:()=>_.digest("hex"),update:(F)=>{_.update(F)}}}let $=kj0.create();return{digestHex:()=>Aj0($.digest()),update:(_)=>{$.update(typeof _==="string"?Mj0.encode(_):_)}}}function X0($){let _=A8();return _.update($),_.digestHex()}var Tj0=128,Rj0=1e6,qj0=65536;function o2($,_){throw new l0("invalid-data",$,_)}function jE($,_){if(!Number.isSafeInteger($)||$<1)return o2(`${_} must be a positive safe integer.`);return $}function _C($){return Object.getOwnPropertySymbols($).some((_)=>Reflect.get($,_)?.enumerable===!0)}function WE($,_,F){if($===void 0){F(_);return}if(Array.isArray($.target))$.target[$.key]=_;else Object.defineProperty($.target,$.key,{configurable:!0,enumerable:!0,value:_,writable:!0})}function VE($){if(typeof $==="number"){if(!Number.isFinite($))return o2("JSON snapshots do not support non-finite numbers.");return JSON.stringify(Object.is($,-0)?0:$)}return JSON.stringify($)}function FC($,_,F,Q={},Z){let J=jE(_,"JSON snapshot byte limit"),X=jE(Q.maximumDepth??Tj0,"JSON snapshot depth limit"),Y=jE(Q.maximumValues??Rj0,"JSON snapshot value limit"),H=new WeakSet,K=Z.hash?A8():void 0,j=Z.hashPrefix??"",W=()=>{if(K===void 0||j.length===0)return;K.update(j),j=""},N=Z.captureText,V=[],O="",D=[{depth:0,kind:"visit",value:$}],w=0,L=1,C,B=(k,M=k.length)=>{if(M>J-w)return o2(`${F} contains more than ${String(J)} bytes.`,{actualLowerBound:w+M,maximumBytes:J});if(K!==void 0){if(j+=k,j.length>=qj0)W()}if(N){if(O+=k,O.length>=65536)V.push(O),O=""}w+=M},U=(k)=>{if(k>Y-L)return o2(`${F} contains more than ${String(Y)} JSON values.`,{actualLowerBound:L+k,maximumValues:Y});L+=k},z=(k)=>{C=k};try{while(D.length>0){let k=D.pop();if(k===void 0)break;if(k.kind==="array"){if(k.index===k.values){if(B("]"),H.delete(k.source),k.clone!==void 0)Object.freeze(k.clone);continue}if(k.index>0)B(",");let I=k.descriptors[String(k.index)];if(I===void 0||I.get!==void 0||I.set!==void 0)return o2(`${F} arrays must contain plain data elements.`);D.push({...k,index:k.index+1}),D.push({...k.clone===void 0?{}:{assignment:{key:k.index,target:k.clone}},depth:k.depth+1,kind:"visit",value:I.value});continue}if(k.kind==="object"){if(k.index===k.keys.length){if(B("}"),H.delete(k.source),k.clone!==void 0)Object.freeze(k.clone);continue}let I=k.keys[k.index];if(I===void 0)return o2(`${F} lost an object key during traversal.`);if(k.index>0)B(",");let g=KE(I,J-w);if(g===void 0)return o2(`${F} contains more than ${String(J)} bytes.`,{actualLowerBound:J+1,maximumBytes:J});B(VE(I),g),B(":");let b=k.descriptors[I];if(b===void 0||b.get!==void 0||b.set!==void 0)return o2(`${F} properties must be plain data properties.`);D.push({...k,index:k.index+1}),D.push({...k.clone===void 0?{}:{assignment:{key:I,target:k.clone}},depth:k.depth+1,kind:"visit",value:b.value});continue}if(k.depth>X)return o2(`${F} nesting exceeds ${String(X)} levels.`,{actual:k.depth,maximumDepth:X});let M=k.value;if(M===null||typeof M==="boolean"||typeof M==="number"||typeof M==="string"){let I=typeof M==="number"&&Object.is(M,-0)?0:M;if(typeof M==="string"){let g=KE(M,J-w);if(g===void 0)return o2(`${F} contains more than ${String(J)} bytes.`,{actualLowerBound:J+1,maximumBytes:J});B(VE(M),g)}else B(VE(M));if(Z.captureValue)WE(k.assignment,I,z);continue}if(typeof M!=="object")return o2(`${F} does not support ${typeof M} values.`);if(H.has(M))return o2(`${F} does not support cyclic values.`);let q=Array.isArray(M)?M.length:void 0;if(q!==void 0&&q>Y-L)return o2(`${F} contains more than ${String(Y)} JSON values.`,{actualLowerBound:L+q,maximumValues:Y});if(q!==void 0){let I=2+Math.max(0,q-1);if(I>J-w)return o2(`${F} contains more than ${String(J)} bytes.`,{actualLowerBound:w+I,maximumBytes:J})}if(H.add(M),Array.isArray(M)){let I=q;if(I===void 0)return o2(`${F} lost an array length during traversal.`);L+=I;let g=Object.getOwnPropertyDescriptors(M);if(!Z.ignoreNonIndexArrayProperties){if(_C(g))return o2(`${F} cannot contain enumerable symbol properties.`);let y=Object.keys(g).filter((r)=>g[r]?.enumerable===!0);if(y.length!==I||y.some((r,h)=>r!==String(h)))return o2(`${F} arrays must be dense and cannot have named properties.`)}let b=Z.captureValue?[]:void 0;if(b!==void 0)WE(k.assignment,b,z);B("["),D.push({clone:b,depth:k.depth,descriptors:g,index:0,kind:"array",source:M,values:I});continue}let E=Object.getPrototypeOf(M);if(E!==Object.prototype&&E!==null)return o2(`${F} accepts only arrays and plain objects.`);let A=Object.getOwnPropertyDescriptors(M);if(_C(A))return o2(`${F} cannot contain enumerable symbol properties.`);let R=Object.keys(A).filter((I)=>A[I]?.enumerable===!0).sort();U(R.length);let P=Z.captureValue?{}:void 0;if(P!==void 0)WE(k.assignment,P,z);B("{"),D.push({clone:P,depth:k.depth,descriptors:A,index:0,keys:R,kind:"object",source:M})}}catch(k){if(k instanceof l0)throw k;throw new l0("invalid-data",`${F} could not be safely inspected.`,{cause:k instanceof Error?k.message:String(k)})}if(Z.captureValue&&C===void 0)return o2(`${F} did not contain a JSON value.`);if(N&&O.length>0)V.push(O);return W(),Object.freeze({bytes:w,...N?{canonicalText:V.join("")}:{},...K===void 0?{}:{sha256:K.digestHex()},...Z.captureValue?{value:C}:{},values:L})}function a0($,_,F,Q={}){let Z=FC($,_,F,Q,{captureText:!1,captureValue:!0,hash:!1,ignoreNonIndexArrayProperties:!1});if(Z.value===void 0)return o2(`${F} did not produce a complete JSON value snapshot.`);return Z}function t4($,_,F,Q={}){let Z=FC($,_,F,Q,{captureText:Q.captureCanonicalText===!0,captureValue:!0,hash:!0,ignoreNonIndexArrayProperties:!1});if(Z.sha256===void 0||Z.value===void 0)return o2(`${F} did not produce a complete JSON snapshot.`);return Z}function jc($,_,F,Q={}){let Z=FC($,_,F,Q,{captureText:!0,captureValue:!1,hash:!1,ignoreNonIndexArrayProperties:!0});if(Z.canonicalText===void 0)return o2(`${F} did not produce canonical JSON text.`);return Z.canonicalText}function NE($,_,F,Q={},Z){let J=FC($,_,F,Q,{captureText:!1,captureValue:!1,hash:!0,ignoreNonIndexArrayProperties:!0,...Z===void 0?{}:{hashPrefix:Z}});if(J.sha256===void 0)return o2(`${F} did not produce a canonical JSON identity.`);return Object.freeze({bytes:J.bytes,sha256:J.sha256})}function Wc($,_,F,Q={},Z){return NE($,_,F,Q,Z).sha256}function $0($){let _=new WeakSet,F=new WeakSet,Q=[{exiting:!1,value:$}];try{while(Q.length>0){let Z=Q.pop();if(Z===void 0)break;let J=Z.value;if(J===null||typeof J==="boolean"||typeof J==="string")continue;if(typeof J==="number"){if(!Number.isFinite(J))return o2("JSON snapshots do not support non-finite numbers.");continue}if(typeof J!=="object")return o2(`JSON snapshots do not support ${typeof J} values.`);if(Z.exiting){_.delete(J),F.add(J),Object.freeze(J);continue}if(F.has(J))continue;if(_.has(J))return o2("JSON snapshots do not support cycles.");if(_.add(J),Q.push({exiting:!0,value:J}),Array.isArray(J)){let K=J.length,j=Object.getOwnPropertyDescriptors(J);if(_C(j))return o2("JSON snapshots cannot contain enumerable symbol properties.");let W=Object.keys(j).filter((N)=>j[N]?.enumerable===!0);if(W.length!==K||W.some((N,V)=>N!==String(V)))return o2("JSON snapshot arrays must be dense and cannot have named properties.");for(let N=K-1;N>=0;N-=1){let V=j[String(N)];if(V===void 0||V.get!==void 0||V.set!==void 0)return o2("JSON snapshot arrays must contain plain data elements.");Q.push({exiting:!1,value:V.value})}continue}let X=Object.getPrototypeOf(J);if(X!==Object.prototype&&X!==null)return o2("JSON snapshots accept only arrays and plain objects.");let Y=Object.getOwnPropertyDescriptors(J);if(_C(Y))return o2("JSON snapshots cannot contain enumerable symbol properties.");let H=Object.keys(Y).filter((K)=>Y[K]?.enumerable===!0);for(let K=H.length-1;K>=0;K-=1){let j=H[K];if(j===void 0)continue;let W=Y[j];if(W===void 0||W.get!==void 0||W.set!==void 0)return o2("JSON snapshot properties must be plain data properties.");if(W.value===void 0)return o2(`JSON snapshot property ${j} is undefined.`);Q.push({exiting:!1,value:W.value})}}}catch(Z){if(Z instanceof l0)throw Z;throw new l0("invalid-data","JSON snapshot could not be safely inspected.",{cause:Z instanceof Error?Z.message:String(Z)})}return $}var{MAX_SAFE_INTEGER:QC,MAX_SAFE_INTEGER:Ej0,MAX_SAFE_INTEGER:Pj0}=Number;function GE($,_){return $<_?-1:$>_?1:0}function OE($){return{maximumDepth:$?.maximumDepth??Ej0,maximumValues:$?.maximumValues??Pj0}}function Vc($,_){return jc($,_.maximumBytes,_.name??"Canonical JSON",OE(_))}function $9($,_,F){return Wc($,_.maximumBytes,_.name??"Canonical JSON",OE(_),F)}function ZC($,_,F){return NE($,_.maximumBytes,_.name??"Canonical JSON",OE(_),F)}function T($){return Vc($,{maximumBytes:QC})}function m($){return $9($,{maximumBytes:QC})}function UN($,_){return $9(_,{maximumBytes:QC},$)}function Y3($,_){return ZC($,{maximumBytes:QC},_)}import{z as PE}from"zod";import{z as A0}from"zod";var kN="slopcamera-workflow-graph-v2",AN="slopcamera-workflow-ref-v1",LY="slopcamera-workflow-graph-abi-v2",MN="slopcamera-requirement-envelope-v2",wE=1,HC="slopcamera-workflow-compilation-v1",Ij0="studio-workflow-graph-v2",bj0="studio-workflow-ref-v1",Nc="studio-workflow-graph-abi-v2",Sj0="studio-requirement-envelope-v2",M8=4096,zN=4096,cZ=32,CY=4096,Gc=2097152,Oc=2097152,wc=300000,fj0=/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$/u,yj0=/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/u,gj0=/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*(?:\/[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*)*$/u,Dc=/^[A-Za-z0-9][A-Za-z0-9._:/@-]*$/u,vj0=/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*)+$/u,xj0=/^[a-f0-9]{64}$/u,DE=A0.string().min(1).max(128).regex(fj0),KC=A0.string().min(1).max(64).regex(yj0),m4=A0.string().min(1).max(255).regex(gj0),I_=A0.string().min(1).max(192).regex(Dc),jC=A0.string().min(1).max(192).regex(Dc),LE=A0.string().min(3).max(192).regex(vj0),V2=A0.string().regex(xj0),K$=A0.number().int().positive().max(Number.MAX_SAFE_INTEGER),r5=A0.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),hj0=A0.union([A0.string().min(1).max(128),r5]),CE=A0.string().refine(($)=>$!=="__proto__","JSON object keys must not use the reserved __proto__ name."),mj0=Symbol("reserved-json-object");function BE($){return typeof $==="object"&&$!==null&&!Array.isArray($)&&Object.hasOwn($,"__proto__")?mj0:$}var L1=A0.lazy(()=>A0.preprocess(BE,A0.union([A0.null(),A0.boolean(),A0.number().finite(),A0.string(),A0.array(L1),A0.record(CE,L1)]))),WC=A0.strictObject({$ref:A0.strictObject({nodeKey:m4,path:A0.array(hj0).max(cZ).optional(),schemaId:I_}),version:A0.union([A0.literal(AN),A0.literal(bj0)])}),uj0=A0.lazy(()=>A0.record(CE,UE).superRefine(($,_)=>{if(Object.hasOwn($,"$ref"))_.addIssue({code:"custom",message:"The reserved $ref field must contain a complete versioned workflow reference."})})),UE=A0.lazy(()=>A0.preprocess(BE,A0.union([WC,A0.null(),A0.boolean(),A0.number().finite(),A0.string(),A0.array(UE),uj0]))),lj0=A0.lazy(()=>A0.record(CE,zE).superRefine(($,_)=>{if(Object.hasOwn($,"$ref"))_.addIssue({code:"custom",message:"The reserved $ref field must contain a complete versioned workflow reference."})})),zE=A0.lazy(()=>A0.preprocess(BE,A0.union([WC,A0.array(zE),lj0]))),kE=A0.strictObject({kind:LE,version:K$}),TN=A0.strictObject({bounds:A0.strictObject({maxDurationMs:K$.max(wc),maxInputBytes:K$.max(Gc),maxOutputBytes:K$.max(Oc)}),key:jC,version:A0.literal(wE)}),RN=A0.discriminatedUnion("kind",[A0.strictObject({kind:A0.literal("operation"),operation:kE}),A0.strictObject({compute:TN,kind:A0.literal("compute")})]),VC=A0.strictObject({controlDependencies:A0.array(m4).max(zN).optional(),dependencies:A0.array(m4).max(zN),executor:RN,input:UE,inputSchemaId:I_,key:m4,label:A0.string().min(1).max(160).optional(),outputSchemaId:I_});function A4($){return $.executor.kind==="operation"}function F8($){return $.executor.kind==="compute"}var qN=A0.strictObject({id:DE,inputSchemaId:I_,version:K$}),u6=A0.strictObject({nodes:A0.array(VC).min(1).max(M8),outputs:zE,version:A0.union([A0.literal(kN),A0.literal(Ij0)]),workflow:qN}),Lc=Object.freeze(["pure","local-read","local-derived-write","project-mutation","paid-cloud","live-control"]),JC=Object.freeze([...Lc,"trusted-code"]),Cc=Object.freeze(["deterministic","verified-receipt","recoverable-transaction","ambiguous-after-dispatch","non-resumable-live"]),XC=Object.freeze([...Cc,"explicit-code-replay"]),H3=Object.freeze(["project-state","recording-metadata","screen-capture","camera","microphone","system-audio","typed-text","window-metadata","local-media","provider-options"]),dj0=Object.freeze(["pure","local-artifact","project-transaction","paid-dispatch","live-control"]),pZ=Object.freeze(["cpu","local-io","ffmpeg","vision","whisper","network","paid-call","project-render","project-publication","output-publication","capture-device","browser"]),Bc=A0.strictObject({amount:K$,resource:A0.enum(pZ)}),K7=A0.strictObject({cache:A0.enum(["none","exact-run","content-addressed"]),cancellable:A0.boolean(),effect:A0.enum(Lc),maxDurationMs:K$,maxFanOut:r5,maxInputBytes:r5,maxOutputBytes:r5,preparation:A0.array(A0.enum(H3)).max(H3.length),resources:A0.array(Bc).max(pZ.length),resume:A0.enum(Cc)}),Uc=A0.strictObject({cache:A0.literal("exact-run"),cancellable:A0.literal(!0),effect:A0.literal("trusted-code"),maxDurationMs:K$.max(wc),maxFanOut:A0.literal(0),maxInputBytes:K$.max(Gc),maxOutputBytes:K$.max(Oc),preparation:A0.tuple([]),resources:A0.tuple([A0.strictObject({amount:A0.literal(1),resource:A0.literal("cpu")})]),resume:A0.literal("explicit-code-replay")}),j7=A0.union([K7,Uc]);function W7($){return Uc.parse({cache:"exact-run",cancellable:!0,effect:"trusted-code",maxDurationMs:$.bounds.maxDurationMs,maxFanOut:0,maxInputBytes:$.bounds.maxInputBytes,maxOutputBytes:$.bounds.maxOutputBytes,preparation:[],resources:[{amount:1,resource:"cpu"}],resume:"explicit-code-replay"})}var K3=A0.strictObject({inputSchemaId:I_,kind:LE,lifecycle:A0.enum(dj0),outputSchemaId:I_,policy:K7,version:K$}),NC=A0.strictObject({discovery:A0.array(K3).max(CY),id:I_,projectionSha256:V2,trustedCompute:A0.boolean()}),V7=A0.strictObject({maxDepth:K$,maxEdges:r5,maxFanOut:r5,maxNodes:K$.max(M8),maxTotalOperationFanOut:r5}),YC=Object.freeze(["dependency-outputs","exact-media-hashes","exact-sensitive-metadata-hashes","prices","project-generations","render-plans"]),cj0=A0.strictObject({depth:r5,edges:r5,localMediaUploadNodes:r5,maxDurationMs:r5,maxInputBytes:r5,maxOutputBytes:r5,nodes:r5,paidCallClaims:r5,structuralFanOut:r5,totalOperationFanOut:r5}),EN=A0.strictObject({bounds:cj0,computeKeys:A0.array(jC).max(M8),effects:A0.array(A0.enum(JC)).max(JC.length),operationFamilies:A0.array(KC).max(M8),operationKinds:A0.array(LE).max(M8),preparation:A0.array(A0.enum(H3)).max(H3.length),resources:A0.array(Bc).max(pZ.length),resumeClasses:A0.array(A0.enum(XC)).max(XC.length),unresolved:A0.array(A0.enum(YC)).max(YC.length),version:A0.union([A0.literal(MN),A0.literal(Sj0)])}),zc=A0.strictObject({compilationSha256:V2,envelope:EN,graph:u6,graphSha256:V2,limits:V7,projection:NC,topologicalWaves:A0.array(A0.array(m4).min(1)).max(M8),version:A0.literal(HC)}),AE=Symbol.for("slopcamera.trusted-compute-definition"),kc=Symbol.for("studio.trusted-compute-definition"),GC=Symbol.for("slopcamera.workflow-ref");function V5($,_,F){let Q=$.safeParse(_);if(!Q.success)throw new l0("invalid-data",`Invalid ${F}.`,{issues:Q.error.issues.map((Z)=>({code:Z.code,message:Z.message,path:Z.path.map(String)}))});return Q.data}import{z as u1}from"zod";var pj0=4096,Ac=32768,Tc=67108864,Rc=16777216,qc=2000000,sj0=67108864,E9=u1.string().min(1).max(pj0).refine(($)=>!$.includes("\x00"),"Paths must not contain NUL bytes."),wC=u1.string().regex(/^[a-f0-9]{64}$/u),N7=u1.string().min(1).max(256),Mc=u1.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),BY=u1.number().int().positive().max(Number.MAX_SAFE_INTEGER);function ME($){return $}var Ec=u1.string().min(3).max(256).regex(/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu),nj0=u1.strictObject({path:E9}),rj0=ME(u1.strictObject({outDirectory:E9.optional(),path:E9,scale:u1.number().finite().positive().max(4).optional()})),oj0=ME(u1.strictObject({alphaCutoff:u1.number().int().min(1).max(64).optional(),duotone:u1.tuple([u1.string().regex(/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu),u1.string().regex(/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu)]).optional(),inputPath:E9,outputPath:E9.refine(($)=>$.toLowerCase().endsWith(".svg"),"Vector output paths must end in .svg."),timeoutMs:u1.number().int().min(1).max(300000).optional()})),ij0=u1.string().superRefine(($,_)=>{if(Kc($,Ac)===void 0){_.addIssue({code:"custom",message:`Prompts must contain at most ${String(Ac)} UTF-8 bytes.`});return}if($.trim().length===0){_.addIssue({code:"custom",message:"Prompts must not be blank."});return}if(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test($))_.addIssue({code:"custom",message:"Prompts must not contain control characters."})}),aj0=ME(u1.strictObject({model:Ec,outputPath:E9.refine(($)=>/\.(?:jpe?g|png|webp)$/iu.test($),"Generated image output paths must end in .png, .jpg, .jpeg, or .webp."),prompt:ij0})),Pc=u1.strictObject({code:u1.string().min(1).max(160),message:u1.string().min(1).max(4096),shapeIds:u1.array(u1.string().min(1).max(256)).max(4096)}),tj0=u1.strictObject({configPath:u1.null(),findings:u1.array(Pc).max(4096)}),ej0=u1.strictObject({darkPng:E9,darkSvg:E9,lightPng:E9,lightSvg:E9,spec:E9,tldr:E9}),$W0=u1.strictObject({artifacts:ej0,configPath:u1.null(),findings:u1.array(Pc).max(4096)}),_W0=u1.strictObject({alphaRmse:u1.number().finite().nonnegative(),colorRmse:u1.number().finite().nonnegative(),outsideAlphaRatio:u1.number().finite().min(0).max(1),sampleHeight:BY,sampleWidth:BY,supportRecall:u1.number().finite().min(0).max(1)}),FW0=u1.strictObject({arch:N7,platform:N7,sharp:N7,sharpVersions:u1.record(u1.string().min(1).max(128),N7),vips:N7,vtracerSha256:wC,vtracerSource:u1.enum(["official-release","override"]),vtracerVersion:N7}),QW0=u1.strictObject({alphaCutoff:u1.number().int().min(1).max(64),bytes:Mc.max(qc),candidatesEvaluated:BY,format:u1.string().min(1).max(80),height:BY.max(4096),inputBytes:BY.max(Rc),outputMode:u1.enum(["color","duotone"]),pathCount:Mc.max(12000),profile:u1.enum(["balanced","detailed","photo"]),provenance:FW0,quality:_W0,receiptVersion:u1.literal(1),representation:u1.enum(["color-paths","alpha-mask"]),sourceSha256:wC,svgSha256:wC,width:BY.max(4096)}),ZW0=u1.strictObject({outputPath:E9,receipt:QW0}),JW0=u1.strictObject({bytes:BY.max(Tc),mediaType:u1.enum(["image/jpeg","image/png","image/webp"]),model:Ec,outputPath:E9,provider:u1.literal("vercel-ai-gateway"),requestId:u1.string().min(1).max(256).refine(($)=>!/[\u0000-\u001f\u007f]/u.test($),"Request ids must not contain control characters."),sha256:wC,warnings:u1.array(u1.string().min(1).max(256)).max(100)}),G7=Object.freeze(["slopcamera.diagram.check","slopcamera.diagram.render","slopcamera.image.generate","slopcamera.image.vectorize"]);function XW0($){let _=Object.freeze([...$.preparation]),F=Object.freeze($.resources.map((Q)=>Object.freeze({...Q})));return Object.freeze({...$,preparation:_,resources:F})}function OC($){return Object.freeze({...$,policy:XW0($.policy)})}var sZ=Object.freeze({"slopcamera.diagram.check":OC({inputSchema:nj0,inputSchemaId:"slopcamera.operation.diagram.check.input/v2",kind:"slopcamera.diagram.check",lifecycle:"pure",outputSchema:tj0,outputSchemaId:"slopcamera.operation.diagram.check.output/v2",policy:{cache:"content-addressed",cancellable:!1,effect:"local-read",maxDurationMs:30000,maxFanOut:0,maxInputBytes:4096,maxOutputBytes:262144,preparation:["local-media"],resources:[{amount:1,resource:"cpu"},{amount:1,resource:"local-io"}],resume:"deterministic"},version:2}),"slopcamera.diagram.render":OC({inputSchema:rj0,inputSchemaId:"slopcamera.operation.diagram.render.input/v2",kind:"slopcamera.diagram.render",lifecycle:"local-artifact",outputSchema:$W0,outputSchemaId:"slopcamera.operation.diagram.render.output/v2",policy:{cache:"none",cancellable:!1,effect:"local-derived-write",maxDurationMs:120000,maxFanOut:5,maxInputBytes:8192,maxOutputBytes:5*sj0,preparation:["local-media"],resources:[{amount:1,resource:"cpu"},{amount:1,resource:"local-io"}],resume:"ambiguous-after-dispatch"},version:2}),"slopcamera.image.generate":OC({inputSchema:aj0,inputSchemaId:"slopcamera.operation.image.generate.input/v2",kind:"slopcamera.image.generate",lifecycle:"paid-dispatch",outputSchema:JW0,outputSchemaId:"slopcamera.operation.image.generate.output/v2",policy:{cache:"exact-run",cancellable:!1,effect:"paid-cloud",maxDurationMs:120000,maxFanOut:1,maxInputBytes:16384,maxOutputBytes:Tc,preparation:["provider-options"],resources:[{amount:1,resource:"local-io"},{amount:1,resource:"network"},{amount:1,resource:"paid-call"}],resume:"ambiguous-after-dispatch"},version:2}),"slopcamera.image.vectorize":OC({inputSchema:oj0,inputSchemaId:"slopcamera.operation.image.vectorize.input/v2",kind:"slopcamera.image.vectorize",lifecycle:"local-artifact",outputSchema:ZW0,outputSchemaId:"slopcamera.operation.image.vectorize.output/v2",policy:{cache:"none",cancellable:!1,effect:"local-derived-write",maxDurationMs:300000,maxFanOut:1,maxInputBytes:Rc,maxOutputBytes:qc,preparation:["local-media"],resources:[{amount:1,resource:"cpu"},{amount:1,resource:"local-io"}],resume:"ambiguous-after-dispatch"},version:2})});var YW0="slopcamera.workflow.registry-projection/v1",HW0="slopcamera.workflow.registry.public/v1",Ic=new WeakSet,bc=17+H3.length+3*pZ.length,RE=1+CY*bc,qE=33554432,Sc=8,KW0=qE+4096,jW0=Sc+1,WW0=RE+5;function VW0($){return Array.isArray($)?$:$.list()}function EE($,_="operation discovery list"){if(!Array.isArray($))throw new l0("invalid-data",`${_} must be an array.`);let F=$.length;if(F>CY)throw new l0("invalid-data",`${_} cannot exceed ${String(CY)} entries.`);let Q=Object.getOwnPropertyDescriptors($);if(Object.getOwnPropertySymbols(Q).some((K)=>Reflect.get(Q,K)?.enumerable===!0))throw new l0("invalid-data",`${_} cannot contain enumerable symbol properties.`);let Z=Object.keys(Q).filter((K)=>Q[K]?.enumerable===!0);if(Z.length!==F||Z.some((K,j)=>K!==String(j)))throw new l0("invalid-data",`${_} must be dense and cannot have named properties.`);let X=2+Math.max(0,F-1),Y=1,H=[];for(let K=0;KRE)throw new l0("invalid-data",`${_} contains more than ${String(RE)} JSON values.`);H.push(N.value)}return Object.freeze(H)}function TE($,_){return`${$}@${String(_)}`}function NW0($){return[...new Set($)].sort((_,F)=>_.localeCompare(F))}function DC($){if($==="studio")return"slopcamera";return $.replace(/^studio\./u,"slopcamera.")}function fc($){let _=EE($).map((Q)=>{let Z=V5(K3,Q,"operation discovery entry"),J=NW0(Z.policy.preparation);if(J.length!==Z.policy.preparation.length)throw new l0("invalid-data",`Duplicate preparation requirement for ${TE(Z.kind,Z.version)}.`,{kind:Z.kind,version:Z.version});let X=[...Z.policy.resources].sort((Y,H)=>Y.resource.localeCompare(H.resource));if(new Set(X.map((Y)=>Y.resource)).size!==X.length)throw new l0("invalid-data",`Duplicate resource claim for ${TE(Z.kind,Z.version)}.`,{kind:Z.kind,version:Z.version});return V5(K3,{...Z,policy:{...Z.policy,preparation:J,resources:X}},"normalized operation discovery entry")}).sort((Q,Z)=>Q.kind.localeCompare(Z.kind)||Q.version-Z.version),F=new Set;for(let Q of _){let Z=TE(Q.kind,Q.version);if(F.has(Z))throw new l0("conflict",`Duplicate operation discovery entry: ${Z}`,{kind:Q.kind,version:Q.version});F.add(Z)}return _}function GW0($){return fc($).map((_)=>V5(K3,{..._,inputSchemaId:DC(_.inputSchemaId),kind:DC(_.kind),outputSchemaId:DC(_.outputSchemaId)},"canonical operation discovery entry"))}function OW0($){return Object.freeze($.map((_)=>Object.freeze({..._,policy:Object.freeze({..._.policy,preparation:Object.freeze([..._.policy.preparation]),resources:Object.freeze(_.policy.resources.map((F)=>Object.freeze({...F})))})})))}function yc($,_=YW0){return UN(`${_}\x00`,{discovery:$.discovery,id:$.id,trustedCompute:$.trustedCompute})}function LC($,_,F={}){let Q=DC(V5(I_,$,"registry projection id")),Z=GW0(VW0(_)),J=F.trustedCompute??!1,X=V5(NC,{discovery:Z,id:Q,projectionSha256:yc({discovery:Z,id:Q,trustedCompute:J}),trustedCompute:J},"workflow registry projection"),Y=Object.freeze({...X,discovery:OW0(X.discovery)});return Ic.add(Y),Y}function gc($){if(typeof $==="object"&&$!==null&&Ic.has($))return $;let _=a0($,KW0,"workflow registry projection",{maximumDepth:jW0,maximumValues:WW0}).value,F=V5(NC,_,"workflow registry projection"),Q=fc(F.discovery),Z={discovery:Q,id:F.id,trustedCompute:F.trustedCompute},J=yc(Z);if(F.projectionSha256!==J)throw new l0("invalid-data","Workflow registry projection hash does not match its contents.",{actualProjectionSha256:F.projectionSha256,expectedProjectionSha256:J,projectionId:F.id});if(m(F.discovery)!==m(Q))throw new l0("invalid-data","Workflow registry projection discovery is not normalized.",{projectionId:F.id});return LC(F.id,Q,{trustedCompute:F.trustedCompute})}function wW0(){return G7.map(($)=>{let _=sZ[$];return{inputSchemaId:_.inputSchemaId,kind:_.kind,lifecycle:_.lifecycle,outputSchemaId:_.outputSchemaId,policy:_.policy,version:_.version}})}function DW0(){return LC(HW0,wW0(),{trustedCompute:!1})}var UY=DW0();var LW0="slopcamera.workflow.graph/v1",CW0="slopcamera.workflow.compilation/v1",IE=Object.freeze({maxDepth:64,maxEdges:2048,maxFanOut:64,maxNodes:256,maxTotalOperationFanOut:4096}),BW0=67108864,UW0=160,mc=1100000,zW0=1024,kW0=2,AW0=16;var MW0=17+H3.length+3*pZ.length,TW0=5+CY*MW0,RW0=21+3*M8+JC.length+H3.length+3*pZ.length+XC.length+YC.length,qW0=1+2*M8,EW0=6,C61=mc+TW0+RW0+qW0+EW0+4;function Q8($,_){throw new l0("invalid-data",$,_)}function uc($){if(typeof $!=="object"||$===null||Object.isFrozen($))return $;for(let _ of Object.values($))uc(_);return Object.freeze($)}function CC($,_){return`${$}@${String(_)}`}function j3($){return[...new Set($)].sort((_,F)=>_.localeCompare(F))}function zY($){if($==="studio")return"slopcamera";return $.replace(/^studio\./u,"slopcamera.")}function nZ($,_,F){let Q=$+_;if(!Number.isSafeInteger(Q))return Q8(`${F} exceeds the safe integer range.`,{left:$,right:_});return Q}function PW0($){if($===void 0)return IE;let _=a0($,zW0,"graph compiler limits",{maximumDepth:kW0,maximumValues:AW0}).value;if(typeof _!=="object"||_===null||Array.isArray(_))return V5(V7,_,"graph compiler limits");return V5(V7,{...IE,..._},"graph compiler limits")}function IW0($,_){if(typeof $!=="object"||$===null||Array.isArray($))return;let F=Object.getOwnPropertyDescriptor($,"nodes")?.value;if(Array.isArray(F)&&F.length>_)return Q8(`Workflow has ${String(F.length)} nodes; the limit is ${String(_)}.`,{actual:F.length,limit:_})}function bW0($,_=M8){IW0($,_);let F=a0($,BW0,"Authored workflow graph",{maximumDepth:UW0,maximumValues:mc}).value,Q=V5(u6,F,"authored workflow graph");if(Q.nodes.length>_)return Q8(`Workflow has ${String(Q.nodes.length)} nodes; the limit is ${String(_)}.`,{actual:Q.nodes.length,limit:_});let Z={...Q,nodes:[...Q.nodes].sort((J,X)=>J.key.localeCompare(X.key))};return $0(Z)}function BC($){if(lc($))return{$ref:{...$.$ref,schemaId:zY($.$ref.schemaId)},version:AN};if(Array.isArray($))return $.map(BC);if(typeof $==="object"&&$!==null)return Object.fromEntries(Object.entries($).map(([_,F])=>[_,BC(F)]));return $}function SW0($){return $0(V5(u6,{...$,nodes:$.nodes.map((_)=>({..._,executor:_.executor.kind==="operation"?{kind:"operation",operation:{..._.executor.operation,kind:zY(_.executor.operation.kind)}}:{compute:{..._.executor.compute,key:zY(_.executor.compute.key)},kind:"compute"},input:BC(_.input),inputSchemaId:zY(_.inputSchemaId),outputSchemaId:zY(_.outputSchemaId)})),outputs:BC($.outputs),version:kN,workflow:{...$.workflow,id:zY($.workflow.id),inputSchemaId:zY($.workflow.inputSchemaId)}},"canonical authored workflow graph"))}function O7($,_=M8){return SW0(bW0($,_))}function fW0($){return UN(`${LW0}\x00`,$)}function lc($){return typeof $==="object"&&$!==null&&!Array.isArray($)&&Object.hasOwn($,"$ref")}function yW0($){return Array.isArray($)}function vc($){let _=[],F=[$];while(F.length>0){let Q=F.pop();if(Q===void 0)continue;if(lc(Q)){_.push(Q);continue}if(yW0(Q)){for(let Z of Q)F.push(Z);continue}if(typeof Q==="object"&&Q!==null)for(let Z of Object.values(Q))F.push(Z)}return _}function xc($,_){return $.length===_.length&&$.every((F,Q)=>F===_[Q])}function hc($,_,F){let Q=_.get($.$ref.nodeKey);if(Q===void 0)return Q8(`${F} contains a dangling reference to ${$.$ref.nodeKey}.`,{nodeKey:$.$ref.nodeKey,owner:F});if(Q.outputSchemaId!==$.$ref.schemaId)return Q8(`${F} expects schema ${$.$ref.schemaId} from ${Q.key}, but the producer declares ${Q.outputSchemaId}.`,{actualSchemaId:Q.outputSchemaId,expectedSchemaId:$.$ref.schemaId,nodeKey:Q.key,owner:F})}function gW0($,_){let F=new Map,Q=new Map,Z=0;for(let j of $){let W=new Set(j.dependencies);F.set(j.key,W),Z=nZ(Z,W.size,"Workflow edge count");for(let N of W){let V=Q.get(N)??new Set;V.add(j.key),Q.set(N,V)}}if(Z>_.maxEdges)return Q8(`Workflow has ${String(Z)} edges; the limit is ${String(_.maxEdges)}.`,{actual:Z,limit:_.maxEdges});let J=Math.max(0,...[...Q.values()].map((j)=>j.size));if(J>_.maxFanOut)return Q8(`Workflow fan-out is ${String(J)}; the limit is ${String(_.maxFanOut)}.`,{actual:J,limit:_.maxFanOut});let X=$.filter((j)=>(F.get(j.key)?.size??0)===0).map((j)=>j.key).sort((j,W)=>j.localeCompare(W)),Y=new Map($.map((j)=>[j.key,F.get(j.key)?.size??0])),H=[],K=0;while(X.length>0){let j=X;H.push(j),K+=j.length;let W=new Set;for(let N of j)for(let V of Q.get(N)??[]){let O=(Y.get(V)??0)-1;if(Y.set(V,O),O===0)W.add(V)}X=[...W].sort((N,V)=>N.localeCompare(V))}if(K!==$.length)return Q8("Workflow graph contains a dependency cycle.");if(H.length>_.maxDepth)return Q8(`Workflow depth is ${String(H.length)}; the limit is ${String(_.maxDepth)}.`,{actual:H.length,limit:_.maxDepth});return{depth:H.length,edges:Z,structuralFanOut:J,waves:H}}function vW0($,_,F){let Q=O7($,F.maxNodes);if(Q.nodes.length>F.maxNodes)return Q8(`Workflow has ${String(Q.nodes.length)} nodes; the limit is ${String(F.maxNodes)}.`,{actual:Q.nodes.length,limit:F.maxNodes});let Z=new Map;for(let H of Q.nodes){if(Z.has(H.key))return Q8(`Duplicate workflow node key: ${H.key}`,{nodeKey:H.key});Z.set(H.key,H)}let J=new Map(_.discovery.map((H)=>[CC(H.kind,H.version),H])),X=new Map,Y=0;for(let H of Q.nodes){let K;if(A4(H)){let O=H.executor.operation,D=J.get(CC(O.kind,O.version));if(D===void 0)throw new l0("unsupported-plan",`Unsupported operation: ${CC(O.kind,O.version)}`,{kind:O.kind,nodeKey:H.key,projectionId:_.id,version:O.version});if(H.inputSchemaId!==D.inputSchemaId||H.outputSchemaId!==D.outputSchemaId)return Q8(`Operation schema identity mismatch for ${CC(O.kind,O.version)} at node ${H.key}.`,{actualInputSchemaId:H.inputSchemaId,actualOutputSchemaId:H.outputSchemaId,expectedInputSchemaId:D.inputSchemaId,expectedOutputSchemaId:D.outputSchemaId,kind:O.kind,nodeKey:H.key,version:O.version});K=D.policy}else if(F8(H)){if(!_.trustedCompute)throw new l0("unsupported-plan",`Trusted compute is unsupported at node ${H.key}.`,{executorKind:"compute",nodeKey:H.key,projectionId:_.id});K=W7(H.executor.compute)}else throw new l0("internal",`Unknown node executor for ${H.key}.`);X.set(H.key,K),Y=nZ(Y,K.maxFanOut,"Total operation fan-out");let j=vc(H.input);for(let O of j)hc(O,Z,`Node ${H.key}`);let W=H.controlDependencies??[],N=j3(W);if(!xc(W,N))return Q8(`Node ${H.key} control dependencies must be unique and sorted.`,{nodeKey:H.key});for(let O of W){if(!Z.has(O))return Q8(`Node ${H.key} contains a dangling control dependency to ${O}.`,{dependency:O,nodeKey:H.key});if(O===H.key)return Q8(`Node ${H.key} cannot depend on itself.`,{nodeKey:H.key})}let V=j3([...j.map((O)=>O.$ref.nodeKey),...W]);if(!xc(H.dependencies,V))return Q8(`Node ${H.key} dependencies do not match its embedded references and explicit control dependencies. Expected [${V.join(", ")}].`,{expectedDependencies:V,nodeKey:H.key})}if(Y>F.maxTotalOperationFanOut)return Q8(`Workflow operation fan-out bound is ${String(Y)}; the limit is ${String(F.maxTotalOperationFanOut)}.`,{actual:Y,limit:F.maxTotalOperationFanOut});for(let H of vc(Q.outputs))hc(H,Z,"Workflow outputs");return{graph:Q,policiesByNode:X,topology:gW0(Q.nodes,F)}}function xW0($){let _=$.indexOf(".");if(_<1)throw new l0("internal",`Operation ${$} has no namespace family.`,{kind:$});return $.slice(0,_)}function hW0($,_,F){$.set(_,nZ($.get(_)??0,F,`Resource ${_} claim`))}function mW0($){let _=new Set,F=new Set,Q=new Set,Z=new Set,J=new Set,X=new Map,Y=new Set,H=0,K=0,j=0,W=0,N=0;for(let O of $.graph.nodes){let D=$.policiesByNode.get(O.key);if(D===void 0)throw new l0("internal",`Node ${O.key} lost its execution policy.`);if(F.add(D.effect),Y.add(D.resume),A4(O))Q.add(xW0(O.executor.operation.kind)),Z.add(O.executor.operation.kind);else if(F8(O))Q.add("compute"),_.add(O.executor.compute.key);for(let w of D.preparation)J.add(w);for(let w of D.resources)hW0(X,w.resource,w.amount);if(D.effect==="paid-cloud"&&D.preparation.includes("local-media"))H=nZ(H,1,"Local media upload node count");K=nZ(K,D.maxDurationMs,"Workflow duration bound"),j=nZ(j,D.maxInputBytes,"Workflow input byte bound"),W=nZ(W,D.maxOutputBytes,"Workflow output byte bound"),N=nZ(N,D.maxFanOut,"Workflow operation fan-out bound")}let V=new Set;if($.topology.edges>0)V.add("dependency-outputs");if(J.has("local-media")||J.has("project-state")||J.has("recording-metadata"))V.add("exact-media-hashes");if(J.has("recording-metadata")||J.has("typed-text")||J.has("window-metadata"))V.add("exact-sensitive-metadata-hashes");if(F.has("paid-cloud")||X.has("paid-call"))V.add("prices");if(F.has("project-mutation")||Z.has("render.project")||Z.has("render.project-plan"))V.add("project-generations");if(Q.has("render"))V.add("render-plans");return V5(EN,{bounds:{depth:$.topology.depth,edges:$.topology.edges,localMediaUploadNodes:H,maxDurationMs:K,maxInputBytes:j,maxOutputBytes:W,nodes:$.graph.nodes.length,paidCallClaims:X.get("paid-call")??0,structuralFanOut:$.topology.structuralFanOut,totalOperationFanOut:N},computeKeys:j3(_),effects:j3(F),operationFamilies:j3(Q),operationKinds:j3(Z),preparation:j3(J),resources:[...X].map(([O,D])=>({amount:D,resource:O})).sort((O,D)=>O.resource.localeCompare(D.resource)),resumeClasses:j3(Y),unresolved:j3(V),version:MN},"workflow requirement envelope")}function uW0($){if($.projection!==void 0){if($.registry!==void 0||$.projectionId!==void 0||$.trustedCompute!==void 0)throw new l0("invalid-data","Compile with either a projection or a registry projection source, not both.");return gc($.projection)}if($.registry!==void 0){if($.projectionId===void 0)throw new l0("invalid-data","A registry projection source requires an explicit projection id.");return LC($.projectionId,$.registry,{trustedCompute:$.trustedCompute??!1})}if($.projectionId!==void 0||$.trustedCompute!==void 0)throw new l0("invalid-data","A projection id or trusted-compute authority requires a registry projection source.");return UY}var PN=PE.unknown().refine(($)=>$!==void 0,"Required"),B61=PE.strictObject({compilationSha256:V2,envelope:PN,graph:PN,graphSha256:V2,limits:PN,projection:PN,topologicalWaves:PN,version:PE.literal(HC)});function lW0($){return UN(`${CW0}\x00`,$)}function dc($){let _=PW0($.limits),F=uW0($),Q=vW0($.graph,F,_),Z=fW0(Q.graph),J={envelope:mW0(Q),graph:Q.graph,graphSha256:Z,limits:_,projection:F,topologicalWaves:Q.topology.waves,version:HC};return uc(V5(zc,{...J,compilationSha256:lW0(J)},"compiled workflow graph"))}var UC=128,IN=1e6,cc=new WeakMap;function oc($){if($.consumed>=$.maximum)throw new l0("invalid-data",`Workflow authoring values exceed the ${String(IN)} value limit.`);$.consumed+=1}function ic($,_){bN($,_),$.consumed+=_}function bN($,_){if(_>$.maximum-$.consumed)throw new l0("invalid-data",`Workflow authoring values exceed the ${String(IN)} value limit.`)}function ac($){return{consumed:0,maximum:IN-$.authoredValues}}function pc($,_){if(_>IN-$.authoredValues)throw new l0("invalid-data",`Workflow authoring values exceed the ${String(IN)} value limit.`)}function dW0($){if($[AE]===!0)return $;if(Reflect.get($,kc)!==!0)return;let _=cc.get($);if(_!==void 0)return _;let F=Object.freeze({[AE]:!0,bounds:$.bounds,inputSchema:$.inputSchema,inputSchemaId:$.inputSchemaId,key:$.key,outputSchema:$.outputSchema,outputSchemaId:$.outputSchemaId,run:$.run});return cc.set($,F),F}function bE($,_){return`${$}@${String(_)}`}function cW0($){let _=$,F=typeof _.list==="function"?_.list():$.discovery;return EE(F)}function tc($){let _=Object.getPrototypeOf($);return _===Object.prototype||_===null}function SN($,_){if(Object.getOwnPropertySymbols($).some((F)=>Reflect.get($,F)?.enumerable===!0))throw new l0("invalid-data",`${_} cannot contain enumerable symbol properties.`)}function yE($){return $0(V5(WC,$.serialized,"workflow reference"))}function ec($){let _=$.$ref.path;return _===void 0?5:6+_.length}function gE($,_){return typeof $==="object"&&$!==null&&GC in $&&_.references.has($)}function SE($,_,F,Q,Z,J){if(Z>UC)throw new l0("invalid-data",`Workflow input nesting exceeds ${String(UC)} levels.`);if(gE($,_)){let X=yE($);return ic(J,ec(X)),F.add(X.$ref.nodeKey),X}if(oc(J),typeof $==="object"&&$!==null&&GC in $)throw new l0("invalid-data","Use a typed Ref value created by this workflow graph builder.");if($===null||typeof $==="boolean"||typeof $==="string")return $;if(typeof $==="number"){if(!Number.isFinite($))throw new l0("invalid-data","Workflow node input numbers must be finite.");return Object.is($,-0)?0:$}if(typeof $!=="object")throw new l0("invalid-data",`Workflow node input cannot contain ${typeof $} values.`);if(Q.has($))throw new l0("invalid-data","Workflow node input cannot contain cycles.");Q.add($);try{if(Array.isArray($)){let K=$.length;bN(J,K);let j=Object.getOwnPropertyDescriptors($);SN(j,"Workflow node input arrays");let W=Object.keys(j).filter((V)=>j[V]?.enumerable===!0);if(W.length!==K||W.some((V,O)=>V!==String(O)))throw new l0("invalid-data","Workflow node input arrays must be dense and cannot have named properties.");let N=[];for(let V=0;VX[K]?.enumerable===!0).sort();bN(J,Y.length);let H={};for(let K of Y){if(K==="__proto__")throw new l0("invalid-data","Workflow inputs cannot contain the reserved __proto__ object key.");let j=X[K];if(j===void 0||j.get!==void 0||j.set!==void 0)throw new l0("invalid-data","Workflow node input properties must be plain data properties.");H[K]=SE(j.value,_,F,Q,Z+1,J)}return H}finally{Q.delete($)}}function sc($,_){let F=new Set,Q=ac(_),Z=SE($,_,F,new Set,0,Q);return{dependencies:[...F].sort((J,X)=>J.localeCompare(X)),value:Z,values:Q.consumed}}function nc($,_){if($===void 0)return[];let F=new Set,Q=(Y)=>{if(!gE(Y,_))throw new l0("invalid-data","Operation control dependencies must be typed Ref values created by this workflow graph builder.");F.add(yE(Y).$ref.nodeKey)};if(!Array.isArray($))return Q($),[...F];let Z=$.length;if(Z>zN)throw new l0("invalid-data",`Operation control dependencies cannot exceed ${String(zN)} entries.`);let J=Object.getOwnPropertyDescriptors($);SN(J,"Operation control dependencies");let X=Object.keys(J).filter((Y)=>J[Y]?.enumerable===!0);if(X.length!==Z||X.some((Y,H)=>Y!==String(H)))throw new l0("invalid-data","Operation control dependencies must be a dense array without named properties.");for(let Y=0;YY.localeCompare(H))}function rc($,_){return[...new Set([...$,..._])].sort((F,Q)=>F.localeCompare(Q))}function fE($,_,F,Q,Z){if(Q>UC)throw new l0("invalid-data",`Workflow output nesting exceeds ${String(UC)} levels.`);if(gE($,_)){let J=yE($);return ic(Z,ec(J)),J}if(oc(Z),typeof $!=="object"||$===null)throw new l0("invalid-data","Workflow outputs must contain only typed references, arrays, and named objects.");if(F.has($))throw new l0("invalid-data","Workflow outputs cannot contain cycles.");F.add($);try{if(Array.isArray($)){let H=$.length;bN(Z,H);let K=Object.getOwnPropertyDescriptors($);SN(K,"Workflow output arrays");let j=Object.keys(K).filter((N)=>K[N]?.enumerable===!0);if(j.length!==H||j.some((N,V)=>N!==String(V)))throw new l0("invalid-data","Workflow output arrays must be dense and cannot have named properties.");let W=[];for(let N=0;NJ[H]?.enumerable===!0).sort();bN(Z,X.length);let Y={};for(let H of X){if(H==="__proto__")throw new l0("invalid-data","Workflow outputs cannot contain the reserved __proto__ object key.");let K=J[H];if(K===void 0||K.get!==void 0||K.set!==void 0)throw new l0("invalid-data","Workflow output properties must be plain data properties.");Y[H]=fE(K.value,_,F,Q+1,Z)}return Y}finally{F.delete($)}}function zC($,_,F,Q=[]){if(Q.length>cZ)throw new l0("invalid-data",`Workflow reference paths cannot exceed ${String(cZ)} segments.`);let Z=$0({$ref:{nodeKey:$,...Q.length===0?{}:{path:[...Q]},schemaId:_},version:AN}),J=Object.freeze({[GC]:()=>{throw new l0("internal","A workflow reference type marker is not executable.")},at:(X)=>{if(!Number.isSafeInteger(X)||X<0)throw new l0("invalid-data","Workflow reference array indexes must be nonnegative safe integers.");if(Q.length>=cZ)throw new l0("invalid-data",`Workflow reference paths cannot exceed ${String(cZ)} segments.`);return zC($,_,F,[...Q,X])},select:(X)=>{if(typeof X!=="string"||X.length<1||X.length>128)throw new l0("invalid-data","Workflow reference field names must contain 1\u2013128 characters.");if(Q.length>=cZ)throw new l0("invalid-data",`Workflow reference paths cannot exceed ${String(cZ)} segments.`);return zC($,_,F,[...Q,X])},serialized:Z});return F.references.add(J),J}function pW0($){let _=new Map;for(let F of cW0($)){let Q=V5(K3,F,"operation discovery entry"),Z=bE(Q.kind,Q.version);if(_.has(Z))throw new l0("conflict",`Duplicate operation discovery entry: ${Z}`,{kind:Q.kind,version:Q.version});_.set(Z,Q)}return{authoredValues:7,computes:new Map,discovery:_,nodes:new Map,references:new WeakSet}}class w7{#$;#_;constructor($,_){this.#$=_,this.#_=$}static create($){return new w7(pW0($),[])}namespace($){let _=V5(KC,$,"workflow namespace segment");return new w7(this.#_,[...this.#$,_])}fragment($,_,F){return _.build(this.namespace($),F)}operation($,_,F,Q={}){return this.#J($,{input:F,inputSchemaId:_.inputSchemaId,kind:_.kind,outputSchemaId:_.outputSchemaId,version:_.version},Q)}operationByKind($,_,F={}){let Q=this.#_.discovery.get(bE(_.kind,_.version));if(Q===void 0)throw new l0("unsupported-plan",`Unsupported operation: ${_.kind}@${String(_.version)}`,{kind:_.kind,version:_.version});return this.#J($,{input:_.input,inputSchemaId:Q.inputSchemaId,kind:_.kind,outputSchemaId:Q.outputSchemaId,version:_.version},F)}compute($,_,F,Q={}){if(typeof _!=="object"||_===null)throw new l0("invalid-data","Compute nodes require a definition created by defineCompute().");let Z=dW0(_);if(Z===void 0)throw new l0("invalid-data","Compute nodes require a definition created by defineCompute().");let J=V5(TN,{bounds:Z.bounds,key:Z.key,version:wE},"trusted compute identity"),X=this.#_.computes.get(J.key);if(X!==void 0&&X!==Z)throw new l0("conflict",`Duplicate trusted compute key: ${J.key}`,{key:J.key});let Y=this.#Q($),H=sc(F,this.#_),K=nc(Q.after,this.#_),j=rc(H.dependencies,K),W=H.values+15+K.length+j.length+(Q.label===void 0?0:1);pc(this.#_,W);let N=V5(VC,{controlDependencies:K,dependencies:j,executor:{compute:J,kind:"compute"},input:H.value,inputSchemaId:Z.inputSchemaId,key:Y,...Q.label===void 0?{}:{label:Q.label},outputSchemaId:Z.outputSchemaId},"authored compute node");return this.#_.computes.set(J.key,Z),this.#_.nodes.set(Y,N),this.#_.authoredValues+=W,zC(Y,Z.outputSchemaId,this.#_)}computeDefinitions(){return Object.freeze([...this.#_.computes.values()].sort(($,_)=>$.key.localeCompare(_.key)))}build($,_){let F=V5(qN,$,"workflow identity"),Q=ac(this.#_),Z=fE(_,this.#_,new Set,0,Q);return $0(V5(u6,{nodes:[...this.#_.nodes.values()].sort((J,X)=>J.key.localeCompare(X.key)),outputs:Z,version:kN,workflow:F},"authored workflow graph"))}#Q($){let _=V5(KC,$,"workflow node key segment"),F=[...this.#$,_].join("/");if(this.#_.nodes.has(F))throw new l0("conflict",`Duplicate workflow node key: ${F}`,{nodeKey:F});if(this.#_.nodes.size>=M8)throw new l0("invalid-data",`Workflow nodes cannot exceed ${String(M8)} entries.`);return F}#J($,_,F){let Q=this.#Q($),Z=this.#_.discovery.get(bE(_.kind,_.version));if(Z===void 0)throw new l0("unsupported-plan",`Unsupported operation: ${_.kind}@${String(_.version)}`,{kind:_.kind,version:_.version});if(_.inputSchemaId!==Z.inputSchemaId||_.outputSchemaId!==Z.outputSchemaId)throw new l0("invalid-data",`Operation contract schema mismatch for ${_.kind}@${String(_.version)}.`,{actualInputSchemaId:_.inputSchemaId,actualOutputSchemaId:_.outputSchemaId,expectedInputSchemaId:Z.inputSchemaId,expectedOutputSchemaId:Z.outputSchemaId,kind:_.kind,version:_.version});let J=sc(_.input,this.#_),X=nc(F.after,this.#_),Y=rc(J.dependencies,X),H=J.values+11+X.length+Y.length+(F.label===void 0?0:1);pc(this.#_,H);let K=V5(VC,{controlDependencies:X,dependencies:Y,executor:{kind:"operation",operation:{kind:Z.kind,version:Z.version}},input:J.value,inputSchemaId:Z.inputSchemaId,key:Q,...F.label===void 0?{}:{label:F.label},outputSchemaId:Z.outputSchemaId},"authored operation node");return this.#_.nodes.set(Q,K),this.#_.authoredValues+=H,zC(Q,Z.outputSchemaId,this.#_)}}var G=l0,uF=HE;function kY($){return Hc($)}var kC={usage:2,"not-found":3,conflict:4,unavailable:5,"unsafe-path":6,"invalid-data":7,subprocess:8,"unsupported-plan":9,"authorization-required":10,cancelled:11,ambiguous:12,incompatible:13,internal:1};function AY($){if($ instanceof G)return $;return new G("internal",uF($))}var D7=1e4;var vE=["blender-product","blender-character","blender-shaded-street","blender-cloth","blender-fluid","cadquery-bracket","manim-lesson"];import{createHash as MC,randomUUID as UV0}from"crypto";import{link as zV0,mkdir as Vp,rm as kV0,writeFile as AV0}from"fs/promises";import{dirname as MV0,join as TC,resolve as TV0}from"path";import MY from"sharp";class h1 extends Error{code;constructor($,_,F){super(`[${$}] ${_}`,F);this.name="SlopcameraCloudError",this.code=$}}import{createHash as mE,randomUUID as Jp}from"crypto";import{link as oW0,rm as iW0,writeFile as aW0}from"fs/promises";import{dirname as tW0,extname as eW0,resolve as Qp}from"path";var $p=Object.freeze({"openai/gpt-image-1":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:40000000,worstImage:250000,source:"https://developers.openai.com/api/docs/models/gpt-image-1"}),"openai/gpt-image-1-mini":Object.freeze({kind:"tokens",textInputPerMillion:2000000,outputPerMillion:8000000,worstImage:52000,source:"https://developers.openai.com/api/docs/models/gpt-image-1-mini"}),"openai/gpt-image-1.5":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:32000000,worstImage:200000,source:"https://developers.openai.com/api/docs/models/gpt-image-1.5"}),"recraft/recraft-v4.1-utility":Object.freeze({kind:"image",perImage:35000,source:"https://vercel.com/ai-gateway/models/recraft-v4.1-utility"})});function sW0($){return Object.hasOwn($p,$)?$p[$]:void 0}function xE($,_){return Math.ceil($*_/1e6)}function nW0($){return Buffer.byteLength($,"utf8")}function _p($){return typeof $==="number"&&Number.isSafeInteger($)&&$>=0}function rW0($,_,F){if(F.kind==="image")return[{provider:"vercel-ai-gateway",operation:`${$} image`,microUsd:F.perImage,basis:"estimated"}];return[{provider:"vercel-ai-gateway",operation:`${$} image output`,microUsd:F.worstImage,basis:"estimated"},{provider:"vercel-ai-gateway",operation:`${$} text input`,microUsd:xE(nW0(_)+64,F.textInputPerMillion),basis:"estimated"}]}function Fp($,_,F){let Q=sW0($);if(Q===void 0)return[{provider:"vercel-ai-gateway",operation:`${$} image`,microUsd:0,basis:"unknown"}];if(Q.kind==="tokens"&&F!==void 0&&_p(F.inputTokens)&&_p(F.outputTokens)&&F.outputTokens>0)return[{provider:"vercel-ai-gateway",operation:`${$} image output`,microUsd:xE(F.outputTokens,Q.outputPerMillion),basis:"reported"},{provider:"vercel-ai-gateway",operation:`${$} text input`,microUsd:xE(F.inputTokens,Q.textInputPerMillion),basis:"reported"}];return rW0($,_,Q)}var fN="https://ai-gateway.vercel.sh/v4/ai",Xp=Object.freeze(["openai/gpt-image-1.5","recraft/recraft-v4.1-utility"]),$V0=Object.freeze(["image/png","image/jpeg","image/webp"]),rZ=32768,_V0=67108864,FV0=300000,Zp=1800000,QV0=100663296;function L7($){throw new h1("INVALID_ARGUMENT",$)}function AC($){if($.length<16||$.length>16384||$.trim()!==$||/[^\x21-\x7e]/u.test($))throw new h1("AUTHENTICATION_REQUIRED","The selected Vercel AI Gateway credential is invalid.");return $}function Yp($){return $??process.env}function uE($){let _=Yp($);if(_.AI_GATEWAY_API_KEY!==void 0)return{source:"AI_GATEWAY_API_KEY",token:AC(_.AI_GATEWAY_API_KEY)};if(_.VERCEL_OIDC_TOKEN!==void 0)return{source:"VERCEL_OIDC_TOKEN",token:AC(_.VERCEL_OIDC_TOKEN)};throw new h1("AUTHENTICATION_REQUIRED","Set AI_GATEWAY_API_KEY or run Slopcamera through `vercel env run -- \u2026` with VERCEL_OIDC_TOKEN available.")}function Hp($){let _=Yp($);if(_.AI_GATEWAY_API_KEY!==void 0)return AC(_.AI_GATEWAY_API_KEY),{available:!0,source:"AI_GATEWAY_API_KEY"};if(_.VERCEL_OIDC_TOKEN!==void 0)return AC(_.VERCEL_OIDC_TOKEN),{available:!0,source:"VERCEL_OIDC_TOKEN"};return{available:!1,source:null}}function ZV0($){if(typeof $!=="string"||$.length<3||$.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test($))L7("Model must be a bounded Vercel AI Gateway provider/model id.");return $}function JV0($){if(typeof $!=="string"||$.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test($)||Buffer.byteLength($,"utf8")>rZ)L7(`Prompt must be non-empty and no more than ${rZ} UTF-8 bytes.`);return $}function XV0($){let _=$??FV0;if(!Number.isSafeInteger(_)||_<1000||_>Zp)L7(`timeoutMs must be an integer from 1000 through ${Zp}.`);return _}function lF($){return typeof $==="object"&&$!==null||typeof $==="function"}function YV0($){if(typeof $!=="function")throw new h1("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.");return $}function HV0(){globalThis.AI_SDK_LOG_WARNINGS=!1}function hE($){if($.aborted)throw new h1("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline.")}async function KV0(){let $,_;try{[$,_]=await Promise.all([import("ai-v7"),import("@ai-sdk/gateway-v4")])}catch{throw new h1("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}if(!lF($)||!lF(_))throw new h1("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.");let F=YV0($.generateImage);if(typeof _.createGateway!=="function")throw new h1("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.");let Q=_.createGateway;return{createGateway:(Z)=>{let J=Q(Z);if(!lF(J)||typeof J.imageModel!=="function")throw new h1("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.");let X=J.imageModel;return{imageModel:(Y)=>X.call(J,Y)}},generateImage:F}}function jV0($){try{if($ instanceof Request)return new URL($.url);if($ instanceof URL)return new URL($.href);return new URL($,fN)}catch{throw new h1("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}}function WV0($,_){if(!($ instanceof Request))return _;let F=new Request($),Q=F.method==="GET"||F.method==="HEAD"?void 0:F.body??void 0,Z={headers:F.headers,method:F.method,signal:F.signal,...Q===void 0?{}:{body:Q,duplex:"half"}};return new Request(_.href,Z)}function VV0($,_){let F=$.headers.get("content-length");if(F!==null){let X=Number(F);if(!Number.isSafeInteger(X)||X<0||X>_)throw $.body?.cancel().catch(()=>{return}),new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response.")}if($.body===null)return $;let Q=$.body.getReader(),Z=0,J=new ReadableStream({cancel:async(X)=>await Q.cancel(X),pull:async(X)=>{try{let Y=await Q.read();if(Y.done){Q.releaseLock(),X.close();return}if(Z+=Y.value.byteLength,Z>_){await Q.cancel().catch(()=>{return}),X.error(new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."));return}X.enqueue(Y.value)}catch{X.error(new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."))}}});return new Response(J,{headers:$.headers,status:$.status,statusText:$.statusText})}function lE($={}){let _=$.fetch??globalThis.fetch,F=$.maximumResponseBytes??QV0;if(!Number.isSafeInteger(F)||F<1||F>1073741824)L7("maximumResponseBytes is outside the supported range.");let Q=new URL(fN);return async(Z,J)=>{let X=jV0(Z);if(X.origin!==Q.origin||X.pathname!==Q.pathname&&!X.pathname.startsWith(`${Q.pathname}/`)||X.username!==""||X.password!=="")throw new h1("GENERATION_FAILED","The Vercel AI Gateway request was rejected.");let Y=WV0(Z,X),H;try{H=await _(Y,{...J,redirect:"error"})}catch(K){if(K instanceof h1)throw K;throw new h1("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}if(H.redirected||H.status>=300&&H.status<400)throw await H.body?.cancel().catch(()=>{return}),new h1("GENERATION_FAILED","The Vercel AI Gateway request was rejected.");return VV0(H,F)}}function NV0($,_){let F=new AbortController,Q=()=>F.abort($?.reason),Z,J=new Promise((H,K)=>{Z=K}),X=()=>{Z?.(new h1("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline."))};if(F.signal.addEventListener("abort",X,{once:!0}),$?.aborted===!0)Q();$?.addEventListener("abort",Q,{once:!0});let Y=setTimeout(()=>F.abort(),_);return{dispose:()=>{clearTimeout(Y),$?.removeEventListener("abort",Q),F.signal.removeEventListener("abort",X),Z=void 0},interruption:J,signal:F.signal}}function GV0($){if(typeof $!=="string"||!$V0.includes($))throw new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an unsupported image type.");return $}function OV0($,_){if($.byteLength<12||$.byteLength>_V0)return!1;if(_==="image/png")return Buffer.from($.subarray(0,8)).equals(Buffer.from([137,80,78,71,13,10,26,10]));if(_==="image/jpeg")return $[0]===255&&$[1]===216&&$.at(-2)===255&&$.at(-1)===217;return $.byteLength>=16&&Buffer.from($.subarray(0,4)).toString("ascii")==="RIFF"&&Buffer.from($).readUInt32LE(4)===$.byteLength-8&&Buffer.from($.subarray(8,12)).toString("ascii")==="WEBP"&&["VP8 ","VP8L","VP8X"].includes(Buffer.from($.subarray(12,16)).toString("ascii"))}function wV0($){let _="provider-warning",F="Provider warning";if(typeof $==="string")F=$;else if(lF($)){if(typeof $.type==="string"&&$.type.length<=64)_=$.type.replace(/[^a-z0-9._-]/giu,"-")||_;if(typeof $.message==="string")F=$.message;else if(typeof $.details==="string")F=$.details}return`${_} sha256:${mE("sha256").update(F).digest("hex")}`}function DV0($){if(!lF($)||!lF($.usage))return;let{inputTokens:_,outputTokens:F}=$.usage;return{inputTokens:typeof _==="number"?_:void 0,outputTokens:typeof F==="number"?F:void 0}}function LV0($,_){if(!lF($)||!Array.isArray($.images)||$.images.length!==1)throw new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway did not return exactly one image.");let F=$.images[0];if(!lF(F)||!(F.uint8Array instanceof Uint8Array))throw new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.");let Q=GV0(F.mediaType);if(!OV0(F.uint8Array,Q))throw new h1("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.");let Z=lF($.providerMetadata)&&lF($.providerMetadata.gateway)?$.providerMetadata.gateway:void 0,J=Z!==void 0&&typeof Z.generationId==="string"&&Z.generationId.length>0&&Z.generationId.length<=256&&!/[\u0000-\u001f\u007f]/u.test(Z.generationId)?Z.generationId:Jp(),X=`sha256:${mE("sha256").update("slopcamera.gateway-generation-id/v1\x00").update(J).digest("hex")}`,Y=Array.isArray($.warnings)?$.warnings.slice(0,100).map(wV0):[];return{bytes:F.uint8Array,response:{image:{base64:Buffer.from(F.uint8Array).toString("base64"),mediaType:Q},model:_,provider:"vercel-ai-gateway",requestId:X,warnings:Y}}}async function Kp($,_){let F=ZV0($.model),Q=JV0($.prompt),Z=uE(_.environment),J=NV0($.signal,XV0($.timeoutMs)),X=!1,Y=!1,H=!1,K=(W)=>{if(H||!X||Y)return;H=!0,_.onProviderCost?.(Fp(F,Q,W))},j=async(W,N)=>{let V=await(_.fetch??globalThis.fetch)(W,N);if(V.status>=400&&V.status<500)Y=!0;return V};try{let W=(async()=>{hE(J.signal),HV0();let N=await(_.loadRuntime??KV0)();hE(J.signal);let V=N.createGateway({apiKey:Z.token,baseURL:fN,fetch:lE({fetch:j,..._.maximumResponseBytes===void 0?{}:{maximumResponseBytes:_.maximumResponseBytes}})});hE(J.signal),X=!0;let O=await N.generateImage({abortSignal:J.signal,maxRetries:0,model:V.imageModel(F),n:1,prompt:Q});return K(DV0(O)),LV0(O,F)})();return await Promise.race([W,J.interruption])}catch(W){if(K(void 0),W instanceof h1)throw W;throw new h1("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}finally{J.dispose()}}async function C7($,_={}){return(await Kp($,_)).response}function CV0($){let _=eW0($).toLocaleLowerCase("en-US");if(_===".png")return"image/png";if(_===".jpg"||_===".jpeg")return"image/jpeg";if(_===".webp")return"image/webp";L7("Output path must end in .png, .jpg, .jpeg, or .webp.")}async function BV0($,_){let F=Qp($),Q=Qp(tW0(F),`.${Jp()}.slopcamera-generate.tmp`);try{return await aW0(Q,_,{flag:"wx"}),await oW0(Q,F),F}catch{throw new h1("OUTPUT_WRITE_FAILED","Slopcamera could not atomically write the generated image.")}finally{await iW0(Q,{force:!0}).catch(()=>{return})}}async function B7($,_={}){if(typeof $.outputPath!=="string"||$.outputPath.length<1||$.outputPath.length>4096||$.outputPath.includes("\x00"))L7("Output path must be a non-empty local path.");let F=CV0($.outputPath),Q=await Kp($,_);if(Q.response.image.mediaType!==F)throw new h1("GENERATION_INVALID_RESPONSE",`Generated ${Q.response.image.mediaType} does not match the requested ${F} output path.`);let Z=await BV0($.outputPath,Q.bytes);return{bytes:Q.bytes.byteLength,mediaType:Q.response.image.mediaType,model:Q.response.model,outputPath:Z,provider:Q.response.provider,requestId:Q.response.requestId,sha256:mE("sha256").update(Q.bytes).digest("hex"),warnings:Q.response.warnings}}var W3=Object.freeze(["image","texture","skybox","backdrop","sprite"]),H6=Object.freeze(["style","palette","material","lighting","mood","detail"]),w1=Object.freeze({candidates:16,concurrency:4,subjectBytes:2048,candidatePromptBytes:rZ,variantBytes:1024,idLength:64,cellEdgeMin:64,cellEdgeMax:1024,cellEdgeDefault:512,labelHeight:28,cellGap:8,galleryPixels:33554432,candidatePixels:33554432,candidateEdge:8192}),Np={image:{aspect:[1,1],frame:($)=>$,rules:"Clean readable composition; no text, captions, borders, or watermark."},texture:{aspect:[1,1],frame:($)=>`Seamless tileable texture of ${$}.`,rules:"Flat orthographic top-down material surface, uniform diffuse lighting, edge-to-edge surface detail that tiles in both directions; no shadows, no specular highlights, no vignette, no borders, no objects, no text."},skybox:{aspect:[2,1],frame:($)=>`Seamless equirectangular 360-degree panorama of ${$}.`,rules:"Full spherical environment in 2:1 equirectangular projection, continuous horizon exactly at the vertical center, left and right edges wrapping seamlessly; no seams, no text, no watermark, no foreground frame."},backdrop:{aspect:[16,9],frame:($)=>`Flat scenic backdrop plate of ${$}.`,rules:"Edge-to-edge environment plate for compositing behind foreground subjects, consistent perspective and lighting across the frame; no borders, no text, no watermark."},sprite:{aspect:[1,1],frame:($)=>`A single ${$} on a plain solid neutral background.`,rules:"One centered subject, fully visible with a clean silhouette, flat even lighting; no shadows, no extra objects, no text, no watermark."}},jp={style:["photorealistic","stylized painterly","flat minimal","hand-drawn"],palette:["warm earthy palette","cool desaturated palette","neutral grayscale palette","vivid saturated palette"],material:["rough weathered surface","smooth polished surface","organic natural surface","synthetic industrial surface"],lighting:["soft diffuse lighting","dramatic directional lighting","flat ambient lighting","golden-hour lighting"],mood:["serene calm mood","dramatic mood","playful mood","mysterious mood"],detail:["minimal sparse detail","balanced detail","intricate dense detail"]},RV0={material:["clear air","light haze","thin clouds","dense dramatic clouds"],lighting:["golden hour","overcast midday","clear night sky","midday sun"]};function qV0($,_){return $==="skybox"?RV0[_]??jp[_]:jp[_]}function d2($){throw new h1("INVALID_ARGUMENT",$)}function gN($){return typeof $==="object"&&$!==null&&!Array.isArray($)}function yN($,_,F){if(typeof $!=="string"||$.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test($)||Buffer.byteLength($,"utf8")>F)d2(`${_} must be non-empty text no more than ${F} UTF-8 bytes.`);return $.trim()}function EV0($){let _=yN($,"Candidate id",w1.idLength);if(!/^[a-z0-9][a-z0-9._-]*$/iu.test(_))d2("Candidate id must start with an alphanumeric and contain only alphanumerics, '.', '_', or '-'.");return _}function Gp($){let _=$.toLowerCase().split(/[^a-z0-9]+/u).filter((F)=>F.length>0).join("-").slice(0,w1.idLength);return _.length===0?"candidate":_}function PV0($){let _=$??"image";if(typeof _!=="string"||!W3.includes(_))d2(`kind must be one of: ${W3.join(", ")}.`);return _}function dE($,_,F){let Q=Np[_],Z=[Q.frame($),`Rules: ${Q.rules}`],J=F?.trim();if(J!==void 0&&J.length>0)Z.push(`Variation direction: ${J}.`);return Z.join(` - -`)}function IV0($){if(typeof $==="string"){let Q=$.trim();if(!H6.includes(Q))d2(`vary axis must be one of: ${H6.join(", ")}.`);return{axis:Q}}if(!gN($))d2("vary entries must be axis names or {axis, values} objects.");let _=$.axis;if(!H6.includes(_))d2(`vary axis must be one of: ${H6.join(", ")}.`);let F=$.values;if(F!==void 0){if(!Array.isArray(F)||F.length<1||F.length>w1.candidates)d2("vary values must be a bounded non-empty list.");return{axis:_,values:F.map((Q)=>yN(Q,"vary value",w1.variantBytes))}}return{axis:_}}function bV0($){if(!gN($))d2("candidates entries must be {id, prompt|variant} objects.");let _=EV0($.id),F=$.prompt,Q=$.variant;if(F!==void 0&&Q!==void 0)d2("A candidate supplies prompt or variant, not both.");if(F===void 0&&Q===void 0)d2("A candidate requires a prompt or a variant.");return{id:_,...F===void 0?{}:{prompt:yN(F,"candidate prompt",w1.candidatePromptBytes)},...Q===void 0?{}:{variant:yN(Q,"candidate variant",w1.variantBytes)}}}function pE($){if(!gN($))d2("Gallery input must be an object.");let _=yN($.subject,"subject",w1.subjectBytes),F=PV0($.kind),Q=Np[F].aspect,Z=($.vary??[]).map(IV0),J=$.candidates?.map(bV0);if(J!==void 0&&Z.length>0)d2("candidates and vary are mutually exclusive.");if(J!==void 0&&$.count!==void 0)d2("candidates and count are mutually exclusive.");if(Z.length>H6.length||new Set(Z.map((j)=>j.axis)).size!==Z.length)d2("vary axes must be unique.");let X=$.count;if(X!==void 0&&(!Number.isInteger(X)||X<1||X>w1.candidates))d2(`count must be an integer from 1 through ${w1.candidates}.`);let Y=$.tiled;if(Y!==void 0&&typeof Y!=="boolean")d2("tiled must be a boolean when set.");let H=Z.map((j)=>({axis:j.axis,values:j.values??qV0(F,j.axis).slice(0,w1.candidates)})),K=[];if(J!==void 0){if(J.length<1||J.length>w1.candidates)d2(`candidates must contain 1 through ${w1.candidates} entries.`);for(let j of J)K.push({id:j.id,prompt:j.prompt??dE(_,F,j.variant),label:j.id})}else if(H.length>0){let j=H.reduce((N,V)=>N*V.values.length,1);if(j<1||j>w1.candidates)d2(`vary expands to ${String(j)} candidates; the bound is ${w1.candidates}.`);let W=new Set;for(let N=0;N=0;L--){let C=H[L],B=C.values[V%C.values.length];V=Math.floor(V/C.values.length),O.unshift(`${C.axis}: ${B}`)}let D=O.join("; "),w=Gp(O.map((L)=>L.split(": ")[1]).join("-"));if(W.has(w))w=`${w}-${String(N+1)}`;W.add(w),K.push({id:w,prompt:dE(_,F,D),label:w})}}else{let j=X??4;for(let W=0;W({index:W+1,...j})),tiled:Y??F==="texture"}}function Op($){let _=[];for(let F of $.split(";")){let Q=F.trim();if(Q==="")continue;let[Z,J]=Q.split("=",2);if(!H6.includes(Z.trim()))d2(`vary axis must be one of: ${H6.join(", ")}.`);if(J===void 0){_.push({axis:Z.trim()});continue}let X=J.split(",").map((Y)=>Y.trim()).filter((Y)=>Y.length>0);if(X.length===0||X.length>w1.candidates)d2("vary values must be a non-empty bounded comma-separated list.");_.push({axis:Z.trim(),values:X})}if(_.length===0)d2("vary requires at least one axis, e.g. 'style; palette=warm,cool'.");return _}var Wp={" ":["00000","00000","00000","00000","00000","00000","00000"],"0":["01110","10001","10011","10101","11001","10001","01110"],"1":["00100","01100","00100","00100","00100","00100","01110"],"2":["01110","10001","00001","00010","00100","01000","11111"],"3":["11111","00010","00100","00010","00001","10001","01110"],"4":["00010","00110","01010","11111","00010","00010","00010"],"5":["11111","10000","11110","00001","00001","10001","01110"],"6":["00110","01000","10000","11110","10001","10001","01110"],"7":["11111","00001","00010","00100","01000","01000","01000"],"8":["01110","10001","10001","01110","10001","10001","01110"],"9":["01110","10001","10001","01111","00001","00010","01100"],a:["00000","00000","01110","00001","01111","10001","01111"],b:["10000","10000","10110","11001","10001","10001","11110"],c:["00000","00000","01110","10000","10000","10001","01110"],d:["00001","00001","01101","10011","10001","10001","01111"],e:["00000","00000","01110","10001","11111","10000","01110"],f:["00110","01001","01000","11100","01000","01000","01000"],g:["00000","01111","10001","10001","01111","00001","01110"],h:["10000","10000","10110","11001","10001","10001","10001"],i:["00100","00000","01100","00100","00100","00100","01110"],j:["00010","00000","00110","00010","00010","10010","01100"],k:["10000","10000","10010","10100","11000","10100","10010"],l:["01100","00100","00100","00100","00100","00100","01110"],m:["00000","00000","11010","10101","10101","10101","10101"],n:["00000","00000","10110","11001","10001","10001","10001"],o:["00000","00000","01110","10001","10001","10001","01110"],p:["00000","00000","11110","10001","11110","10000","10000"],q:["00000","00000","01101","10011","01111","00001","00001"],r:["00000","00000","10110","11001","10000","10000","10000"],s:["00000","00000","01111","10000","01110","00001","11110"],t:["01000","01000","11100","01000","01000","01001","00110"],u:["00000","00000","10001","10001","10001","10011","01101"],v:["00000","00000","10001","10001","10001","01010","00100"],w:["00000","00000","10001","10101","10101","10101","01010"],x:["00000","00000","10001","01010","00100","01010","10001"],y:["00000","00000","10001","10001","01111","00001","01110"],z:["00000","00000","11111","00010","00100","01000","11111"],"#":["01010","01010","11111","01010","11111","01010","01010"],"-":["00000","00000","00000","11111","00000","00000","00000"],_:["00000","00000","00000","00000","00000","00000","11111"],".":["00000","00000","00000","00000","00000","01100","01100"],":":["00000","01100","01100","00000","01100","01100","00000"],"=":["00000","00000","11111","00000","11111","00000","00000"],"/":["00001","00010","00010","00100","01000","01000","10000"],"?":["01110","10001","00001","00010","00100","00000","00100"]};function SV0($,_,F){let Z=Buffer.alloc(_*F*4);for(let K=0;K<_*F;K++){let j=K*4;Z[j]=18,Z[j+1]=21,Z[j+2]=27,Z[j+3]=255}let J=(K,j)=>{let W=Wp[K]??Wp["?"],N=Math.floor((F-14)/2);for(let V=0;V<7;V++)for(let O=0;O<5;O++){if(W[V][O]!=="1")continue;for(let D=0;D<2;D++)for(let w=0;w<2;w++){let L=j+O*2+w,C=N+V*2+D;if(L<0||L>=_||C<0||C>=F)continue;let B=(C*_+L)*4;Z[B]=235,Z[B+1]=238,Z[B+2]=244,Z[B+3]=255}}},X=12,Y=Math.floor((_-8)/X),H=$.toLowerCase().replace(/[^a-z0-9 #._:=/?-]/gu,"?").slice(0,Math.max(0,Y));for(let K=0;K{return})}}async function yV0($,_,F,Q){let Z=Q.generate??C7;try{let J=await Z({model:_,prompt:$.prompt,...F.signal===void 0?{}:{signal:F.signal},...F.timeoutMs===void 0?{}:{timeoutMs:F.timeoutMs}},Q),X=Buffer.from(J.image.base64,"base64");return{index:$.index,id:$.id,label:$.label,prompt:$.prompt,status:"generated",bytes:X,mediaType:J.image.mediaType,requestId:J.requestId,warnings:J.warnings}}catch(J){return{index:$.index,id:$.id,label:$.label,prompt:$.prompt,status:"failed",error:J instanceof Error?J.message:String(J)}}}async function gV0($,_,F){let Q=Array($.length),Z=0;return await Promise.all(Array.from({length:Math.min(_,$.length)},async()=>{while(Z<$.length){let J=Z++;Q[J]=await F($[J])}})),Q}function wp($){let _=$??Xp[1];if(typeof _!=="string"||_.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(_))d2("model must be a bounded Vercel AI Gateway provider/model id.");return _}function Dp($){let _=$??w1.cellEdgeDefault;if(typeof _!=="number"||!Number.isInteger(_)||_w1.cellEdgeMax)d2(`cellEdge must be an integer from ${String(w1.cellEdgeMin)} through ${String(w1.cellEdgeMax)}.`);return _}function Lp($){if(typeof $!=="string"||$.length<1||$.length>4096||$.includes("\x00"))d2("outputDir must be a non-empty bounded local directory path.");return TV0($)}async function U7($,_={}){if(!gN($))d2("Gallery input must be an object.");let F=$,Q=Lp(F.outputDir),Z=wp(F.model),J=Dp(F.cellEdge),X=pE(F);await Vp(Q,{recursive:!0});let Y=await gV0(X.candidates,w1.concurrency,async(H)=>await yV0(H,Z,F,_));return await RC({candidates:Y,cellEdge:J,model:Z,outputDir:Q,plan:X})}async function RC($){let _=Lp($.outputDir),F=wp($.model),Q=Dp($.cellEdge),{plan:Z}=$;if(!gN(Z)||!Array.isArray(Z.candidates)||Z.candidates.length===0)d2("plan must be a non-empty gallery plan from planSlopcameraGallery.");let J=$.candidates;await Vp(_,{recursive:!0});let X=w1.labelHeight,Y=Q,H=Math.round(Q*Z.aspect[1]/Z.aspect[0]),K=H+X,j=Math.max(1,Math.ceil(Math.sqrt(J.length))),W=Math.ceil(J.length/j),N=w1.cellGap,V=j*Y+(j-1)*N,O=W*K+(W-1)*N;if(V*O>w1.galleryPixels)d2("Gallery sheet exceeds its pixel budget; reduce count or cellEdge.");let D=[],w=[];for(let M of J){let q=(M.index-1)%j,E=Math.floor((M.index-1)/j),A=q*(Y+N),R=E*(K+N),P=M.status==="failed"?`#${String(M.index)} ${M.label} failed`:`#${String(M.index)} ${M.label}`;if(w.push({input:await MY(SV0(P,Y,X),{raw:{width:Y,height:X,channels:4}}).png().toBuffer(),left:A,top:R}),M.status==="generated"){if(M.bytes===void 0||M.mediaType===void 0)d2("A generated gallery candidate requires its bytes and media type.");let I=M.path??TC(_,`candidate-${String(M.index).padStart(2,"0")}-${Gp(M.id)}.${fV0(M.mediaType)}`);if(M.path===void 0)await cE(I,M.bytes);let g=MC("sha256").update(M.bytes).digest("hex");if(M.sha256!==void 0&&M.sha256!==g)d2("A pre-published gallery candidate digest does not match its bytes.");let b,y;if(M.cellImage!==void 0){let p=MC("sha256").update(M.cellImage.bytes).digest("hex");if(M.cellImage.sha256!==void 0&&M.cellImage.sha256!==p)d2("A gallery cell image digest does not match its bytes.");let S=MY(M.cellImage.bytes,{limitInputPixels:w1.candidatePixels,failOn:"warning"}),i=await S.metadata();if(i.width===void 0||i.height===void 0||i.width>w1.candidateEdge||i.height>w1.candidateEdge||i.width*i.height>w1.candidatePixels)d2("A gallery cell image exceeds its raster bounds.");b=S,y={sha256:p,bytes:M.cellImage.bytes.byteLength,mediaType:M.cellImage.mediaType,...M.cellImage.path===void 0?{}:{path:M.cellImage.path}}}else{let p=MY(M.bytes,{limitInputPixels:w1.candidatePixels,failOn:"warning"}),S=await p.metadata();if(S.width===void 0||S.height===void 0||S.width>w1.candidateEdge||S.height>w1.candidateEdge||S.width*S.height>w1.candidatePixels)d2("A generated candidate exceeds its raster bounds.");b=p}let r=await b.resize(Y,H,{fit:"cover"}).png().toBuffer(),h=Z.tiled&&M.cellImage===void 0?await(async()=>{let p=await MY(r).resize(Math.ceil(Y/2),Math.ceil(H/2),{fit:"fill"}).png().toBuffer(),S=[0,Math.floor(Y/2)],i=[0,Math.floor(H/2)];return MY({create:{width:Y,height:H,channels:4,background:{r:0,g:0,b:0,alpha:0}}}).composite(i.flatMap((Y0)=>S.map((_0)=>({input:p,left:_0,top:Y0})))).png().toBuffer()})():r;w.push({input:h,left:A,top:R+X}),D.push({index:M.index,id:M.id,label:M.label,prompt:M.prompt,status:"generated",path:I,sha256:g,bytes:M.bytes.byteLength,mediaType:M.mediaType,...M.requestId===void 0?{}:{requestId:M.requestId},...M.warnings===void 0?{}:{warnings:M.warnings},...M.job===void 0?{}:{job:M.job},...Z.tiled&&M.cellImage===void 0?{tiled:!0}:{},...y===void 0?{}:{cellImage:y},...M.scene===void 0?{}:{scene:M.scene},cell:{x:A,y:R,width:Y,height:K}})}else{let I=M.bytes!==void 0||M.mediaType!==void 0||M.path!==void 0||M.sha256!==void 0,g;if(I){if(M.bytes===void 0||M.mediaType===void 0||M.path===void 0)d2("A failed gallery candidate artifact requires bytes, media type, and path.");let y=MC("sha256").update(M.bytes).digest("hex");if(M.sha256!==void 0&&M.sha256!==y)d2("A failed gallery candidate artifact digest does not match its bytes.");g={bytes:M.bytes.byteLength,mediaType:M.mediaType,path:M.path,sha256:y}}let b=Buffer.alloc(Y*H*4);for(let y=0;yM.status==="failed").length,z=TC(_,"receipt.json"),k={kind:$.receipt?.kind??"slopcamera.image-gallery",schemaVersion:1,subject:Z.subject,galleryKind:$.receipt===void 0?Z.kind:"scene",model:F,provider:$.receipt===void 0?"vercel-ai-gateway":"local",...$.receipt===void 0?{}:{baseSceneSha256:$.receipt.baseSceneSha256},cell:{width:Y,height:H,labelHeight:X},axes:Z.axes,candidates:D,counts:{requested:J.length,generated:J.length-U,failed:U},gallery:{path:C,sha256:B,bytes:L.byteLength,mediaType:"image/png",width:V,height:O},receiptPath:z,clientMaxRetries:0};if(await cE(z,`${JSON.stringify(k,null,2)} -`),U===J.length)throw new h1("GENERATION_FAILED",`Every gallery candidate failed; see ${z} for the retained attempt record.`);return k}function d($){throw new G("usage",$)}function M0($,_){let F={},Q=[],Z=!1;for(let J=0;J<$.length;J+=1){let X=$[J];if(Z||!X.startsWith("-")){Q.push(X);continue}if(X==="--"){Z=!0;continue}let Y=X.indexOf("="),H=Y===-1?X:X.slice(0,Y),K=Y===-1?void 0:X.slice(Y+1),j=_[H];if(j===void 0)d(`Unknown option: ${H}`);if(j==="flag"){if(K!==void 0)d(`${H} does not take a value.`);if(F[H]!==void 0)d(`Option may be specified only once: ${H}`);F[H]=!0;continue}let W=K??$[J+1];if(W===void 0||K===void 0&&W.startsWith("--"))d(`Option requires a value: ${H}`);if(K===void 0)J+=1;if(j==="value"){if(F[H]!==void 0)d(`Option may be specified only once: ${H}`);F[H]=W}else{let N=F[H];F[H]=N===void 0?[W]:[...kp(N,H),W]}}return{options:F,positionals:Q}}function kp($,_){if(!Array.isArray($)||!$.every((F)=>typeof F==="string"))d(`Internal parser error for ${_}.`);return $}function f($,_){let F=$.options[_];if(F===void 0)return;if(typeof F!=="string")d(`Internal parser error for ${_}.`);return F}function V3($,_){let F=$.options[_];if(F===void 0)return[];return kp(F,_)}function Q0($,_){return $.options[_]===!0}function T8($,_,F){if($===void 0)return F;if(!/^[1-9][0-9]*$/u.test($))d(`${_} must be a positive integer.`);let Q=Number($);if(!Number.isSafeInteger(Q))d(`${_} exceeds the safe integer range.`);return Q}function N3($,_){return $===void 0?void 0:T8($,_,1)}function vN($,_){if($===void 0)return;if(!/^(?:0|[1-9][0-9]*)$/u.test($))d(`${_} must be a non-negative integer.`);let F=Number($);if(!Number.isSafeInteger(F))d(`${_} exceeds the safe integer range.`);return F}function EC($,_,F){let Q=T8($,_,F);if(Q%2!==0)d(`${_} must be an even positive integer.`);return Q}function w2($,_,F){if($===void 0){if(F===void 0)d(`${_} is required.`);return F}if($.trim()===""||!Number.isFinite(Number($)))d(`${_} must be a finite number.`);return Number($)}function b_($,_,F,Q,Z){let J=w2($,_,Z);if(JQ)d(`${_} must be between ${F} and ${Q}.`);return J}function K6($,_,F,Q){return $===void 0?void 0:b_($,_,F,Q)}function oZ($,_,F){if($===void 0)return F;if($==="true")return!0;if($==="false")return!1;d(`${_} must be true or false.`)}function N5($,_,F,Q){if($===void 0){if(Q===void 0)d(`${_} is required.`);return Q}let Z=F.find((J)=>J===$);if(Z===void 0)d(`${_} must be one of: ${F.join(", ")}.`);return Z}function b0($,_,F){if($.positionals.length!==_)d(`Usage: ${F}`);return $.positionals}var B0={"--json":"flag"};function vV0($){let _=M0($,B0);return b0(_,0,"slopcamera capabilities [--json]"),{json:Q0(_,"--json"),kind:"capabilities"}}function xV0($){let _=M0($,B0);return b0(_,0,"slopcamera doctor [--json]"),{json:Q0(_,"--json"),kind:"doctor"}}function PC($,_,F,Q){let Z=f($,_),J=f($,F);if(Z!==void 0&&J!==void 0)d(`${_} and ${F} are mutually exclusive.`);if(Q.required&&Z===void 0&&J===void 0)d(`Exactly one of ${_} or ${F} is required.`);return{inline:Z,path:J}}function hV0($){let _=[],F=new Set;for(let Q of $){let Z=Q.indexOf("="),J=Z===-1?"":Q.slice(0,Z),X=Z===-1?"":Q.slice(Z+1);if(J!=="first"&&J!=="last"||X.trim()==="")d("--frame must use first= or last=.");if(F.has(J))d(`--frame ${J} may be specified only once.`);F.add(J),_.push({frameType:J,path:X})}return _}function mV0($){let _=$[0];if(_==="list"){let F=M0($.slice(1),{...B0,"--limit":"value","--provider":"value","--query":"value","--refresh":"flag","--type":"value"});return b0(F,0,"slopcamera ai models list [options]"),{json:Q0(F,"--json"),kind:"ai-models-list",limit:(()=>{let Q=T8(f(F,"--limit"),"--limit",100);if(Q>500)d("--limit must be at most 500.");return Q})(),modelType:N5(f(F,"--type"),"--type",["all","image","video","speech","transcription"],"all"),provider:f(F,"--provider"),query:f(F,"--query"),refresh:Q0(F,"--refresh")}}if(_==="show"){let F=M0($.slice(1),{...B0,"--refresh":"flag"}),[Q]=b0(F,1,"slopcamera ai models show [--refresh] [--json]");return{json:Q0(F,"--json"),kind:"ai-models-show",model:Q,refresh:Q0(F,"--refresh")}}d("Usage: slopcamera ai models [options]")}function uV0($){if($[0]!=="inspect")d("Usage: slopcamera ai provider-options inspect [--json]");let _=M0($.slice(1),B0),[F]=b0(_,1,"slopcamera ai provider-options inspect [--json]");return{json:Q0(_,"--json"),kind:"ai-provider-options-inspect",path:F}}function lV0($){if($[0]==="gallery")return dV0($.slice(1));if($[0]!=="generate")d("Usage: slopcamera ai image --model [options]");let _=M0($.slice(1),{...B0,"--allow-cloud-upload":"flag","--aspect-ratio":"value","--count":"value","--hosted":"flag","--image":"repeat","--mask":"value","--max-per-call":"value","--max-output-tokens":"value","--model":"value","--prompt":"value","--prompt-file":"value","--provider-options":"value","--seed":"value","--size":"value","--stop":"repeat","--temperature":"value","--timeout":"value"});b0(_,0,"slopcamera ai image generate --model [options]");let F=PC(_,"--prompt","--prompt-file",{required:!1}),Q=V3(_,"--image");if(Q.length>16)d("--image may be specified at most 16 times.");if(F.inline===void 0&&F.path===void 0&&Q.length===0)d("Image generation requires a prompt or at least one --image reference.");let Z=f(_,"--model");if(Z===void 0)d("--model is required.");let J=T8(f(_,"--count"),"--count",1);if(J>16)d("--count must be at most 16.");let X=N3(f(_,"--max-per-call"),"--max-per-call");if(X!==void 0&&X>16)d("--max-per-call must be at most 16.");if(X!==void 0&&X --model --output-dir [options]"),Q=f(_,"--model");if(Q===void 0)d("--model is required.");let Z=f(_,"--output-dir");if(Z===void 0||Z.trim()==="")d("--output-dir is required.");let J=f(_,"--kind");if(J!==void 0&&!W3.includes(J))d(`--kind must be one of: ${W3.join(", ")}.`);let X=N3(f(_,"--count"),"--count");if(X!==void 0&&X>w1.candidates)d(`--count must be at most ${w1.candidates}.`);let Y=N3(f(_,"--cell"),"--cell");if(Y!==void 0&&(Yw1.cellEdgeMax))d(`--cell must be between ${w1.cellEdgeMin} and ${w1.cellEdgeMax}.`);let H=f(_,"--candidates"),K=f(_,"--vary");if(H!==void 0&&(K!==void 0||X!==void 0))d("--candidates is mutually exclusive with --vary and --count.");let j=Q0(_,"--tile"),W=Q0(_,"--no-tile");if(j&&W)d("--tile and --no-tile are mutually exclusive.");let N=f(_,"--preview");if(N!==void 0&&N!=="probe")d("--preview must be probe.");if(N==="probe"&&!["texture","skybox","backdrop"].includes(J??"image"))d("--preview probe requires --kind texture, skybox, or backdrop.");return{candidatesFile:H,cell:Y,count:X,galleryKind:J,json:Q0(_,"--json"),kind:"ai-image-gallery",model:Q,outputDir:Z,preview:N,subject:F,tiled:j?!0:W?!1:void 0,timeout:f(_,"--timeout")??"10m",vary:K}}function cV0($){if($[0]!=="gallery")d("Usage: slopcamera ai scene gallery --variants --output-dir [options]");let _=M0($.slice(1),{...B0,"--camera":"value","--cell":"value","--output-dir":"value","--time-us":"value","--variants":"value"}),[F]=b0(_,1,"slopcamera ai scene gallery --variants --output-dir [options]"),Q=f(_,"--variants");if(Q===void 0)d("--variants is required.");let Z=f(_,"--output-dir");if(Z===void 0||Z.trim()==="")d("--output-dir is required.");let J=f(_,"--time-us"),X;if(J!==void 0){if(!/^\d+$/u.test(J)||!Number.isSafeInteger(Number(J)))d("--time-us must be a nonnegative integer microsecond time.");X=Number(J)}let Y=N3(f(_,"--cell"),"--cell");if(Y!==void 0&&(Yw1.cellEdgeMax))d(`--cell must be between ${w1.cellEdgeMin} and ${w1.cellEdgeMax}.`);return{camera:f(_,"--camera"),cell:Y,json:Q0(_,"--json"),kind:"ai-scene-gallery",outputDir:Z,scene:F,timeUs:X,variants:Q}}function pV0($){if($[0]!=="generate")d("Usage: slopcamera ai video generate --model [options]");let _=M0($.slice(1),{...B0,"--allow-cloud-upload":"flag","--aspect-ratio":"value","--count":"value","--duration":"value","--fps":"value","--frame":"repeat","--generate-audio":"value","--image":"value","--max-per-call":"value","--model":"value","--prompt":"value","--prompt-file":"value","--provider-options":"value","--reference":"repeat","--resolution":"value","--seed":"value","--timeout":"value"});b0(_,0,"slopcamera ai video generate --model [options]");let F=PC(_,"--prompt","--prompt-file",{required:!1}),Q=f(_,"--image"),Z=hV0(V3(_,"--frame")),J=V3(_,"--reference");if(J.length>32)d("--reference may be specified at most 32 times.");if(Z.length>0&&J.length>0)d("--frame and --reference are mutually exclusive because frame images take precedence in AI SDK video requests.");if(Q!==void 0&&Z.some((K)=>K.frameType==="first"))d("--image and --frame first are mutually exclusive because the first frame takes precedence.");if(Z.some((K)=>K.frameType==="last")&&Q===void 0&&!Z.some((K)=>K.frameType==="first"))d("--frame last requires --image or --frame first.");if(F.inline===void 0&&F.path===void 0&&Q===void 0&&Z.length===0&&J.length===0)d("Video generation requires a prompt, --image, --frame, or --reference.");let X=f(_,"--model");if(X===void 0)d("--model is required.");let Y=T8(f(_,"--count"),"--count",1);if(Y>8)d("--count must be at most 8.");let H=N3(f(_,"--max-per-call"),"--max-per-call");if(H!==void 0&&H>8)d("--max-per-call must be at most 8.");if(H!==void 0&&H [options]");let _=M0($.slice(1),{...B0,"--format":"value","--instructions":"value","--instructions-file":"value","--language":"value","--model":"value","--provider-options":"value","--speed":"value","--text":"value","--text-file":"value","--timeout":"value","--voice":"value"});b0(_,0,"slopcamera ai speech generate --model [options]");let F=PC(_,"--text","--text-file",{required:!0}),Q=PC(_,"--instructions","--instructions-file",{required:!1}),Z=f(_,"--model");if(Z===void 0)d("--model is required.");return{instructions:Q.inline,instructionsFile:Q.path,json:Q0(_,"--json"),kind:"ai-speech-generate",language:f(_,"--language"),model:Z,outputFormat:f(_,"--format"),providerOptions:f(_,"--provider-options"),speed:K6(f(_,"--speed"),"--speed",0.25,4),text:F.inline,textFile:F.path,timeout:f(_,"--timeout")??"5m",voice:f(_,"--voice")}}function nV0($){let _=M0($,{...B0,"--allow-cloud-audio-upload":"flag","--format":"value","--model":"value","--provider-options":"value","--timeout":"value"}),[F]=b0(_,1,"slopcamera ai transcribe --model --allow-cloud-audio-upload [options]"),Q=f(_,"--model");if(Q===void 0)d("--model is required.");return{allowCloudAudioUpload:Q0(_,"--allow-cloud-audio-upload"),format:N5(f(_,"--format"),"--format",["all","json","srt","text","vtt"],"all"),input:F,json:Q0(_,"--json"),kind:"ai-transcribe",model:Q,providerOptions:f(_,"--provider-options"),timeout:f(_,"--timeout")??"30m"}}function rV0($){let _=$[0];if(_==="status"){let F=M0($.slice(1),B0);return b0(F,0,"slopcamera credits status [--json]"),{kind:"credits-status",json:Q0(F,"--json")}}if(_==="topup"){let F=M0($.slice(1),{...B0,"--email":"value","--pack":"value"});b0(F,0,"slopcamera credits topup [--pack ] [--email
] [--json]");let Q=f(F,"--email");if(Q!==void 0&&(Q.length>320||/[\s\u0000-\u001f\u007f]/u.test(Q)))d("--email must be a nonempty email address without whitespace or control characters.");let Z=f(F,"--pack");if(Z!==void 0&&!/^[a-z0-9][a-z0-9_-]{0,63}$/u.test(Z))d("--pack must be a pack id such as p10, p25, p50, or p100.");return{email:Q,json:Q0(F,"--json"),kind:"credits-topup",pack:Z}}if(_==="wait"){let F=M0($.slice(1),{...B0,"--timeout":"value"});return b0(F,0,"slopcamera credits wait [--timeout