create-opentray 0.0.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +163 -0
  2. package/dist/assets/create-openspec-template-iOS-Dark-1024@1x.png +0 -0
  3. package/dist/assets/create-openspec-template-iOS-Default-1024@1x.png +0 -0
  4. package/dist/bin-B9VeXatR.mjs +3830 -0
  5. package/dist/bin-B9VeXatR.mjs.map +1 -0
  6. package/dist/bin-CeFW1wxR.d.mts +15 -0
  7. package/dist/bin-CeFW1wxR.d.mts.map +1 -0
  8. package/dist/bin.d.mts +2 -0
  9. package/dist/bin.mjs +3 -0
  10. package/dist/icon-codec-BQ3cGV1n.mjs +13 -0
  11. package/dist/icon-codec-BQ3cGV1n.mjs.map +1 -0
  12. package/dist/index.d.mts +557 -0
  13. package/dist/index.d.mts.map +1 -0
  14. package/dist/index.mjs +2 -0
  15. package/dist/shell/assets/__vite-browser-external-2447137e.js +1 -0
  16. package/dist/shell/assets/browse.js +1 -0
  17. package/dist/shell/assets/ghostty-web.js +13 -0
  18. package/dist/shell/assets/index.css +1 -0
  19. package/dist/shell/assets/index.js +9 -0
  20. package/dist/shell/assets/input.js +1 -0
  21. package/dist/shell/assets/main.js +43 -0
  22. package/dist/shell/assets/terminal-pane.js +2 -0
  23. package/dist/shell/assets/terminal.js +1 -0
  24. package/dist/shell/browse.html +15 -0
  25. package/dist/shell/ghostty-vt.wasm +0 -0
  26. package/dist/shell/index.html +15 -0
  27. package/dist/shell/terminal.html +15 -0
  28. package/dist/webui/assets/__vite-browser-external-2447137e.js +1 -0
  29. package/dist/webui/assets/browse.js +1 -0
  30. package/dist/webui/assets/ghostty-web.js +13 -0
  31. package/dist/webui/assets/index.css +1 -0
  32. package/dist/webui/assets/index.js +9 -0
  33. package/dist/webui/assets/input.js +1 -0
  34. package/dist/webui/assets/main.js +43 -0
  35. package/dist/webui/assets/terminal-pane.js +2 -0
  36. package/dist/webui/assets/terminal.js +1 -0
  37. package/dist/webui/browse.html +15 -0
  38. package/dist/webui/ghostty-vt.wasm +0 -0
  39. package/dist/webui/index.html +16 -0
  40. package/dist/webui/terminal.html +15 -0
  41. package/dist/webui/vendor/ghostty-vt.wasm +0 -0
  42. package/dist/webui/vendor/ghostty-web.js +2963 -0
  43. package/package.json +45 -5
  44. package/index.js +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin-B9VeXatR.mjs","names":["module","moduleDirectory"],"sources":["../src/app-id.ts","../src/command-run.ts","../src/port-scan.ts","../src/icon-compose.ts","../src/scrape.ts","../src/tokenize.ts","../src/entry-template.ts","../src/shell-server-template.ts","../src/scaffold.ts","../src/materialize.ts","../src/launch-vector.ts","../src/open-app.ts","../src/wizard.ts","../src/server.ts","../src/bin.ts"],"sourcesContent":["// Orthogonal intents (maintained 2026-07-22; original user request: the default\n// appId is the command segment before the first Option, reversed and dot-joined):\n// 1. Derive the default appId from the pre-option tokens of the command.\n// 2. Keep the derivation pure so it is testable against the user's example.\n// 3. Provide a display-name and directory-safe projection for scaffolding.\n\n/** A token that looks like a command option, e.g. `--xx`, `-p`, or `--port=8080`. */\nconst isOptionToken = (token: string): boolean => token.startsWith(\"-\") && token.length > 1;\n\n/**\n * Default appId derivation. `npx somecommand start --xx` keeps the pre-option\n * tokens `[\"npx\", \"somecommand\", \"start\"]`, reverses them, and dot-joins:\n * `start.somecommand.npx`.\n */\nexport const deriveDefaultAppId = (tokens: readonly string[]): string => {\n const preOption: string[] = [];\n for (const token of tokens) {\n if (isOptionToken(token)) {\n break;\n }\n preOption.push(token);\n }\n const segments = preOption\n .map((token) => token.split(/[/\\\\]/).pop() ?? token)\n .filter((segment) => segment.length > 0)\n .reverse();\n if (segments.length === 0) {\n return \"app.opentray\";\n }\n return segments.join(\".\");\n};\n\n/** Human display name from the appId derivation: `Somecommand Start`. */\nexport const deriveDefaultAppName = (tokens: readonly string[]): string => {\n const appId = deriveDefaultAppId(tokens);\n return appId\n .split(\".\")\n .filter((segment) => segment.length > 0)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join(\" \");\n};\n\n/** Directory-safe project name from an appId (mirrors packaging normalizeAppId semantics). */\nexport const toProjectDirectoryName = (appId: string): string => {\n const normalized = appId\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return normalized.length > 0 ? normalized : \"opentray-app\";\n};\n\n/** True when the appId has the shape consumers expect for stable identity. */\nexport const isValidAppId = (appId: string): boolean => {\n const trimmed = appId.trim();\n return trimmed.length > 0 && /^[a-z0-9]+(\\.[a-z0-9-]+)+$/iu.test(trimmed);\n};\n","// Orthogonal intents (maintained 2026-07-22; original user requests: run the\n// start command once and stream its shell output; owner acceptance then asked\n// for a real interactive terminal with completely objective stdio transport —\n// the renderer owns all analysis):\n// 1. Spawn the tokenized command without a shell whenever possible.\n// 2. Prefer a pseudo-terminal (prebuilt @lydell/node-pty) for interactive stdin.\n// 3. Transport the binding's strings VERBATIM: no re-decoding, no analysis.\n// node-pty's contract is a UTF-8 text channel (invalid bytes become U+FFFD\n// inside the binding, split multibyte chars are joined there — same\n// semantics as the ../openspecui reference stack). The wizard adds zero\n// interpretation on top; ghostty-web owns all rendering.\n// 4. Degrade to pipe mode when the native PTY dependency is unavailable.\n// 5. Own process-tree teardown on stop/exit across POSIX and Windows.\n\nimport { spawn, execFile, type ChildProcess } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\n\nexport interface CommandRunEvent {\n readonly type: \"stdout\" | \"stderr\" | \"exit\" | \"spawn-error\" | \"pty-ready\" | \"pty-unavailable\";\n /** The PTY binding's output chunk, verbatim; rendering is the frontend's job. */\n readonly chunk?: string;\n readonly code?: number | null;\n readonly message?: string;\n}\n\nexport interface CommandRunTerminalSize {\n readonly cols: number;\n readonly rows: number;\n}\n\nexport interface CommandRunOptions {\n readonly tokens: readonly string[];\n readonly cwd?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly ringLimit?: number;\n /** Attach through a PTY when available; defaults to true. */\n readonly pty?: boolean;\n readonly terminalSize?: CommandRunTerminalSize;\n readonly onEvent: (event: CommandRunEvent) => void;\n}\n\nexport interface CommandRun {\n readonly pid: number | undefined;\n readonly pty: boolean;\n readonly exited: Promise<{ code: number | null; spawnError?: string }>;\n readonly output: readonly string[];\n /** Write terminal input bytes to the command's stdin (PTY mode only). */\n write(data: string): void;\n /** Resize the pseudo-terminal (PTY mode only). */\n resize(size: CommandRunTerminalSize): void;\n kill(): Promise<void>;\n}\n\nconst SHELL_METACHARS = /[<>&|;$`\"'%]/u;\n\n/** True when the command line needs a shell (only Windows uses cmd /c). */\nexport const needsShell = (tokens: readonly string[]): boolean =>\n tokens.some((token) => SHELL_METACHARS.test(token) && token.length > 1);\n\n/** Minimal shape of the node-pty API this module needs (@lydell/node-pty is API-compatible). */\ninterface PtyModule {\n spawn(\n file: string,\n args: readonly string[],\n options: {\n name: string;\n cols: number;\n rows: number;\n cwd: string;\n env: Record<string, string>;\n },\n ): PtyProcess;\n}\n\ninterface PtyProcess {\n readonly pid: number;\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(signal?: string): void;\n onData(listener: (data: string) => void): void;\n onExit(listener: (event: { exitCode: number; signal?: number }) => void): void;\n}\n\nlet ptyProbe: Promise<PtyModule | undefined> | undefined;\n\n/**\n * Feature-detect the optional native PTY dependency. A failed or missing\n * install must never break the wizard: callers fall back to pipe mode.\n * The probe result is cached so repeated commands do not re-require it.\n */\nexport const loadPtyModule = (\n probe: () => Promise<PtyModule | undefined> = defaultPtyProbe,\n): Promise<PtyModule | undefined> => {\n ptyProbe ??= probe().catch(() => undefined);\n return ptyProbe;\n};\n\nconst defaultPtyProbe = async (): Promise<PtyModule | undefined> => {\n // Bun loads the native module and spawns fine, but its PTY read thread never\n // delivers onData — a silent empty terminal (verified 2026-08-16). Node is\n // the supported interactive host; under Bun degrade to pipes immediately.\n if (process.versions.bun !== undefined) {\n return undefined;\n }\n const require = createRequire(import.meta.url);\n for (const request of [\"@lydell/node-pty\", \"node-pty\"]) {\n try {\n const raw: unknown = require(request);\n const module = raw as { spawn?: unknown };\n if (typeof module.spawn === \"function\") {\n return raw as PtyModule;\n }\n } catch {\n // Try the next candidate distribution.\n }\n }\n return undefined;\n};\n\n/** Test seam: reset the cached PTY probe. */\nexport const resetPtyProbeCache = (): void => {\n ptyProbe = undefined;\n};\n\nconst DEFAULT_TERMINAL_SIZE: CommandRunTerminalSize = { cols: 100, rows: 30 };\n\n/** Minimal shapes of Bun's native PTY (Bun ≥ 1.2.19). */\ninterface BunTerminalOptions {\n cols?: number;\n rows?: number;\n name?: string;\n data?: (terminal: unknown, data: Uint8Array<ArrayBuffer>) => void;\n exit?: (terminal: unknown, exitCode: number, signal: string | null) => void;\n}\n\ninterface BunTerminal {\n write(data: string | BufferSource): number;\n resize(cols: number, rows: number): void;\n close(): void;\n}\n\ninterface BunProcess {\n readonly pid: number;\n readonly exited: Promise<number>;\n kill(signal?: number | string): void;\n}\n\ninterface BunRuntime {\n Terminal: new (options: BunTerminalOptions) => BunTerminal;\n spawn(\n command: readonly string[],\n options: { terminal: BunTerminal; cwd?: string; env?: Record<string, string> },\n ): BunProcess;\n}\n\n/** The Bun global when running under Bun with the native Terminal API. */\nexport const bunTerminalRuntime = (): BunRuntime | undefined => {\n const runtime = (globalThis as { Bun?: unknown }).Bun;\n if (typeof runtime !== \"object\" || runtime === null) {\n return undefined;\n }\n const candidate = runtime as Partial<BunRuntime>;\n if (typeof candidate.Terminal !== \"function\" || typeof candidate.spawn !== \"function\") {\n return undefined;\n }\n return candidate as BunRuntime;\n};\n\n/**\n * Native Bun PTY backend: `Bun.Terminal` + `Bun.spawn({ terminal })`. Under\n * Bun this replaces @lydell/node-pty entirely — the optional native module\n * loads but never delivers output under Bun, while the built-in Terminal is\n * first-class (verified Bun 1.3.14: output, stdin echo, resize, exit codes).\n */\nexport const startBunTerminalRun = (\n options: CommandRunOptions,\n bun: BunRuntime,\n): CommandRun => {\n const [command, ...args] = options.tokens;\n if (command === undefined) {\n return emptyRun(options, \"command is empty\");\n }\n const size = options.terminalSize ?? DEFAULT_TERMINAL_SIZE;\n const ring: string[] = [];\n const ringLimit = options.ringLimit ?? 200;\n const decoder = new TextDecoder();\n const append = (chunk: string): void => {\n ring.push(chunk);\n if (ring.length > ringLimit) {\n ring.splice(0, ring.length - ringLimit);\n }\n };\n\n const terminal = new bun.Terminal({\n cols: size.cols,\n rows: size.rows,\n name: \"xterm-256color\",\n data: (_terminal, data) => {\n // Objective passthrough: decode the PTY's bytes verbatim; rendering and\n // all analysis stay in the frontend renderer.\n const text = decoder.decode(data);\n if (text.length === 0) {\n return;\n }\n append(text);\n options.onEvent({ type: \"stdout\", chunk: text });\n },\n });\n\n const proc = bun.spawn([command, ...args], {\n terminal,\n cwd: options.cwd ?? globalThis.process.cwd(),\n env: {\n ...globalThis.process.env,\n ...options.env,\n TERM: \"xterm-256color\",\n } as Record<string, string>,\n });\n options.onEvent({ type: \"pty-ready\" });\n\n const exited = proc.exited.then((code) => {\n options.onEvent({ type: \"exit\", code });\n return { code };\n });\n\n let killPromise: Promise<void> | undefined;\n return {\n pid: proc.pid,\n pty: true,\n exited,\n output: ring,\n write(data) {\n terminal.write(data);\n },\n resize({ cols, rows }) {\n terminal.resize(cols, rows);\n },\n kill() {\n killPromise ??= (async () => {\n // Closing the PTY sends SIGHUP to the session (children included);\n // the direct kill covers processes that detached the terminal.\n try {\n proc.kill();\n } catch {\n // already dead\n }\n try {\n terminal.close();\n } catch {\n // already closed\n }\n await exited.catch(() => undefined);\n })();\n return killPromise;\n },\n };\n};\n\nexport const startCommandRun = async (options: CommandRunOptions): Promise<CommandRun> => {\n if (options.pty !== false) {\n const bun = bunTerminalRuntime();\n if (bun !== undefined) {\n try {\n return startBunTerminalRun(options, bun);\n } catch (error) {\n // PTY spawn failures fall through to pipe mode so the wizard stays usable.\n options.onEvent({\n type: \"spawn-error\",\n message: `bun terminal spawn failed: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n } else if (process.versions.bun !== undefined) {\n options.onEvent({\n type: \"pty-unavailable\",\n message:\n \"Bun 版本缺少 Bun.Terminal(需要 Bun ≥ 1.2.19),预览以非交互模式运行。\",\n });\n } else {\n const ptyModule = await loadPtyModule();\n if (ptyModule !== undefined) {\n try {\n return startPtyRun(options, ptyModule);\n } catch (error) {\n options.onEvent({\n type: \"spawn-error\",\n message: `pty spawn failed: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n } else {\n options.onEvent({\n type: \"pty-unavailable\",\n message:\n \"node-pty 不可用,预览以非交互模式运行(无法向命令输入内容)。可安装 @lydell/node-pty 启用交互。\",\n });\n }\n }\n }\n return startPipeRun(options);\n};\n\nconst startPtyRun = (options: CommandRunOptions, ptyModule: PtyModule): CommandRun => {\n const [command, ...args] = options.tokens;\n if (command === undefined) {\n return emptyRun(options, \"command is empty\");\n }\n const size = options.terminalSize ?? DEFAULT_TERMINAL_SIZE;\n const ring: string[] = [];\n const ringLimit = options.ringLimit ?? 200;\n const onEvent = options.onEvent;\n const append = (chunk: string): void => {\n ring.push(chunk);\n if (ring.length > ringLimit) {\n ring.splice(0, ring.length - ringLimit);\n }\n };\n\n const ptyProcess = ptyModule.spawn(command, args, {\n name: \"xterm-256color\",\n cols: size.cols,\n rows: size.rows,\n cwd: options.cwd ?? globalThis.process.cwd(),\n env: {\n ...globalThis.process.env,\n ...options.env,\n TERM: \"xterm-256color\",\n } as Record<string, string>,\n });\n onEvent({ type: \"pty-ready\" });\n\n const exited = new Promise<{ code: number | null; spawnError?: string }>((resolve) => {\n ptyProcess.onExit(({ exitCode }) => {\n onEvent({ type: \"exit\", code: exitCode });\n resolve({ code: exitCode });\n });\n });\n ptyProcess.onData((data) => {\n if (data.length === 0) {\n return;\n }\n append(data);\n onEvent({ type: \"stdout\", chunk: data });\n });\n\n let killPromise: Promise<void> | undefined;\n return {\n pid: ptyProcess.pid,\n pty: true,\n exited,\n output: ring,\n write(data) {\n ptyProcess.write(data);\n },\n resize({ cols, rows }) {\n try {\n ptyProcess.resize(cols, rows);\n } catch {\n // A dead PTY cannot be resized; teardown owns the rest.\n }\n },\n kill: () => {\n killPromise ??= (async () => {\n try {\n ptyProcess.kill();\n } catch {\n // Best-effort teardown: a dead process is the goal state.\n }\n })();\n return killPromise;\n },\n };\n};\n\nconst startPipeRun = (options: CommandRunOptions): CommandRun => {\n const [command, ...args] = options.tokens;\n if (command === undefined) {\n return emptyRun(options, \"command is empty\");\n }\n\n const ring: string[] = [];\n const ringLimit = options.ringLimit ?? 200;\n const onEvent = options.onEvent;\n const append = (chunk: string): void => {\n ring.push(chunk);\n if (ring.length > ringLimit) {\n ring.splice(0, ring.length - ringLimit);\n }\n };\n\n const useShell = process.platform === \"win32\" && needsShell(options.tokens);\n let child: ChildProcess;\n try {\n child = spawn(command, args, {\n cwd: options.cwd,\n env: options.env ?? process.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: useShell,\n windowsHide: true,\n detached: process.platform !== \"win32\",\n });\n } catch (error) {\n return emptyRun(\n options,\n error instanceof Error ? error.message : String(error),\n { code: null, spawnError: error instanceof Error ? error.message : String(error) },\n );\n }\n\n const exited = new Promise<{ code: number | null; spawnError?: string }>((resolve) => {\n child.once(\"error\", (error: Error) => {\n onEvent({ type: \"spawn-error\", message: error.message });\n resolve({ code: null, spawnError: error.message });\n });\n child.once(\"exit\", (code) => {\n onEvent({ type: \"exit\", code });\n resolve({ code });\n });\n });\n\n const pipe = (stream: NodeJS.ReadableStream | null, type: \"stdout\" | \"stderr\"): void => {\n if (stream === null) {\n return;\n }\n // Pipe fallback: standard one-shot UTF-8 text decode (the degraded path);\n // the renderer still owns all rendering.\n stream.on(\"data\", (chunk: Buffer | string) => {\n const text = Buffer.from(chunk).toString(\"utf8\");\n append(text);\n onEvent({ type, chunk: text });\n });\n };\n pipe(child.stdout, \"stdout\");\n pipe(child.stderr, \"stderr\");\n\n let killPromise: Promise<void> | undefined;\n return {\n pid: child.pid,\n pty: false,\n exited,\n output: ring,\n write() {\n // Pipe mode has no stdin; the WebUI shows the pty-unavailable notice.\n },\n resize() {\n // Nothing to resize without a PTY.\n },\n kill: () => {\n killPromise ??= killProcessTree(child);\n return killPromise;\n },\n };\n};\n\nconst emptyRun = (\n options: CommandRunOptions,\n message: string,\n resolved?: { code: number | null; spawnError?: string },\n): CommandRun => {\n options.onEvent({ type: \"spawn-error\", message });\n return {\n pid: undefined,\n pty: false,\n exited: Promise.resolve(resolved ?? { code: null, spawnError: message }),\n output: [],\n write() {},\n resize() {},\n kill: async () => {},\n };\n};\n\n/** Kill a command run and every descendant it spawned. */\nexport const killProcessTree = async (child: ChildProcess): Promise<void> => {\n const pid = child.pid;\n if (pid === undefined) {\n return;\n }\n try {\n if (process.platform === \"win32\") {\n await runExecFile(\"taskkill\", [\"/PID\", String(pid), \"/T\", \"/F\"]);\n } else {\n // POSIX: signal the whole group (spawned detached) so children that\n // ignored the direct signal still terminate.\n try {\n process.kill(-pid, \"SIGTERM\");\n } catch {\n child.kill(\"SIGTERM\");\n }\n await waitForExit(child, 3_000).then((exited) => {\n if (!exited) {\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n child.kill(\"SIGKILL\");\n }\n }\n });\n }\n } catch {\n // Best-effort teardown: a dead process is the goal state.\n }\n if (child.exitCode === null && child.signalCode === null) {\n child.kill(\"SIGKILL\");\n }\n};\n\nconst waitForExit = (child: ChildProcess, timeoutMs: number): Promise<boolean> =>\n new Promise((resolve) => {\n if (child.exitCode !== null || child.signalCode !== null) {\n resolve(true);\n return;\n }\n const timer = setTimeout(() => resolve(false), timeoutMs);\n child.once(\"exit\", () => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n\nconst runExecFile = (command: string, args: readonly string[]): Promise<void> =>\n new Promise((resolve, reject) => {\n execFile(command, [...args], { timeout: 5_000, windowsHide: true }, (error) => {\n if (error !== null) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n","// Orthogonal intents (maintained 2026-07-22; original user request: run the\n// command once and discover the HTTP ports it listens on):\n// 1. Snapshot listening TCP ports before spawn as the diff baseline.\n// 2. Enumerate listeners per platform: lsof (macOS/Linux), netstat/PowerShell (Windows).\n// 3. Verify candidates answer HTTP before listing them as services.\n\nimport { execFile } from \"node:child_process\";\nimport net from \"node:net\";\n\nexport interface DiscoveredService {\n readonly port: number;\n readonly url: string;\n readonly firstSeenAt: number;\n title?: string;\n}\n\nexport type ListenersRunner = (platform: NodeJS.Platform) => Promise<ReadonlySet<number>>;\n\n/** Listener snapshot with process ownership: port -> owning PIDs. */\nexport type ListenerOwners = ReadonlyMap<number, ReadonlySet<number>>;\n\n/** Loopback service URL for a discovered port. */\nexport const serviceUrl = (port: number): string => `http://127.0.0.1:${port}`;\n\nconst LOOPBACK_NO_PROXY = \"localhost,127.0.0.1,::1\";\n/** Ensure loopback fetches bypass system proxies (same guard as source examples). */\nexport const ensureLoopbackNoProxy = (env: NodeJS.ProcessEnv = process.env): void => {\n const existing = env.NO_PROXY ?? env.no_proxy ?? \"\";\n const merged = [existing, LOOPBACK_NO_PROXY]\n .join(\",\")\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n env.NO_PROXY = Array.from(new Set(merged)).join(\",\");\n env.no_proxy = env.NO_PROXY;\n};\n\nexport const listListeningPorts: ListenersRunner = async (platform) => {\n if (platform === \"win32\") {\n return listWindowsListeningPorts();\n }\n return listLsofListeningPorts();\n};\n\n/** Listeners with ownership; used to attribute ports to the preview process tree. */\nexport const listListeningPortOwners = async (\n platform: NodeJS.Platform = process.platform,\n): Promise<ListenerOwners> => {\n if (platform === \"win32\") {\n const stdout = await runCapture(\"netstat\", [\"-ano\", \"-p\", \"tcp\"]).catch(() => \"\");\n return parseNetstatPortOwners(stdout);\n }\n const stdout = await runCapture(\"lsof\", [\"-nP\", \"-iTCP\", \"-sTCP:LISTEN\", \"-F\", \"pPn\"]).catch(\n () => \"\",\n );\n return parseLsofPortOwners(stdout);\n};\n\nconst listLsofListeningPorts = async (): Promise<ReadonlySet<number>> => {\n // lsof exits non-zero when NO listener matches (\"no process found\") —\n // an empty result, not an error. The sibling owner-scan above already\n // treats it that way; this call site rejected and killed whole test\n // suites on quiet machines (CI runners).\n const stdout = await runCapture(\"lsof\", [\n \"-nP\",\n \"-iTCP\",\n \"-sTCP:LISTEN\",\n \"-F\",\n \"Pn\",\n ]).catch(() => \"\");\n return parseLsofPorts(stdout);\n};\n\n/**\n * Parse `lsof -F pPn` output into port -> owning PIDs. The field stream is\n * `p<pid>`, `P<proto>`, `n<host:port>` per socket, so each address inherits\n * the most recent pid field.\n */\nexport const parseLsofPortOwners = (stdout: string): ListenerOwners => {\n const owners = new Map<number, Set<number>>();\n let currentPid: number | undefined;\n for (const line of stdout.split(\"\\n\")) {\n if (line.startsWith(\"p\")) {\n const pid = Number.parseInt(line.slice(1), 10);\n currentPid = Number.isInteger(pid) ? pid : undefined;\n continue;\n }\n if (!line.startsWith(\"n\")) {\n continue;\n }\n const hostPort = line.slice(1);\n const index = hostPort.lastIndexOf(\":\");\n if (index < 0) {\n continue;\n }\n const port = Number.parseInt(hostPort.slice(index + 1), 10);\n if (!Number.isInteger(port) || port <= 0 || currentPid === undefined) {\n continue;\n }\n const pids = owners.get(port) ?? new Set<number>();\n pids.add(currentPid);\n owners.set(port, pids);\n }\n return owners;\n};\n\n/** Parse `netstat -ano -p tcp` into port -> owning PIDs (PID is the last column). */\nexport const parseNetstatPortOwners = (stdout: string): ListenerOwners => {\n const owners = new Map<number, Set<number>>();\n for (const rawLine of stdout.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line.toLowerCase().includes(\"listening\")) {\n continue;\n }\n const columns = line.split(/\\s+/);\n const local = columns.find((column) => column.includes(\":\"));\n const pid = Number.parseInt(columns[columns.length - 1] ?? \"\", 10);\n if (local === undefined || !Number.isInteger(pid)) {\n continue;\n }\n const index = local.lastIndexOf(\":\");\n const port = Number.parseInt(local.slice(index + 1), 10);\n if (!Number.isInteger(port) || port <= 0) {\n continue;\n }\n const pids = owners.get(port) ?? new Set<number>();\n pids.add(pid);\n owners.set(port, pids);\n }\n return owners;\n};\n\nexport const parseLsofPorts = (stdout: string): ReadonlySet<number> => {\n const ports = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line.startsWith(\"n\")) {\n continue;\n }\n const hostPort = line.slice(1);\n const index = hostPort.lastIndexOf(\":\");\n if (index < 0) {\n continue;\n }\n const port = Number.parseInt(hostPort.slice(index + 1), 10);\n if (Number.isInteger(port) && port > 0) {\n ports.add(port);\n }\n }\n return ports;\n};\n\nconst listWindowsListeningPorts = async (): Promise<ReadonlySet<number>> => {\n try {\n const stdout = await runCapture(\"netstat\", [\"-ano\", \"-p\", \"tcp\"]);\n return parseNetstatPorts(stdout);\n } catch {\n const stdout = await runPowerShellTcpConnections();\n return parsePowerShellPorts(stdout);\n }\n};\n\n/** Parses `netstat -ano -p tcp` output; keeps LISTENING rows. */\nexport const parseNetstatPorts = (stdout: string): ReadonlySet<number> => {\n const ports = new Set<number>();\n for (const rawLine of stdout.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line.toLowerCase().includes(\"listening\")) {\n continue;\n }\n const columns = line.split(/\\s+/);\n // Typical row: TCP 127.0.0.1:19080 0.0.0.0:0 LISTENING 1234\n const local = columns.find((column) => column.includes(\":\"));\n if (local === undefined) {\n continue;\n }\n const index = local.lastIndexOf(\":\");\n const port = Number.parseInt(local.slice(index + 1), 10);\n if (Number.isInteger(port) && port > 0) {\n ports.add(port);\n }\n }\n return ports;\n};\n\nconst runPowerShellTcpConnections = async (): Promise<string> => {\n const script = \"[Net.NetworkInformation.NetworkInformation]::GetActiveTcpConnections() | ForEach-Object { $_.LocalEndPoint.Port }\";\n return await runCapture(\"powershell.exe\", [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-ExecutionPolicy\",\n \"Bypass\",\n \"-Command\",\n script,\n ]);\n};\n\nexport const parsePowerShellPorts = (stdout: string): ReadonlySet<number> => {\n const ports = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n const port = Number.parseInt(line.trim(), 10);\n if (Number.isInteger(port) && port > 0) {\n ports.add(port);\n }\n }\n return ports;\n};\n\n/** TCP-connect probe used by the generated app and discovery verification. */\nexport const waitForTcpPort = async (\n port: number,\n timeoutMs: number,\n intervalMs = 150,\n host = \"127.0.0.1\",\n): Promise<boolean> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await tcpProbe(host, port)) {\n return true;\n }\n await sleep(intervalMs);\n }\n return false;\n};\n\nexport const tcpProbe = (host: string, port: number, timeoutMs = 500): Promise<boolean> =>\n new Promise((resolve) => {\n const socket = new net.Socket();\n const finish = (result: boolean): void => {\n socket.destroy();\n resolve(result);\n };\n socket.setTimeout(timeoutMs);\n socket.once(\"connect\", () => finish(true));\n socket.once(\"timeout\", () => finish(false));\n socket.once(\"error\", () => finish(false));\n socket.connect(port, host);\n });\n\n/** Verify a port answers with an HTTP response (any status counts). */\nexport const verifyHttpService = async (port: number, timeoutMs = 2_000): Promise<boolean> => {\n ensureLoopbackNoProxy();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(serviceUrl(port), {\n signal: controller.signal,\n redirect: \"manual\",\n });\n // Any HTTP answer (including 4xx/5xx and redirects) proves an HTTP service.\n return response.status > 0;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n};\n\n/** Collect a PID and every descendant (BFS over `pgrep -P` on POSIX). */\nexport const collectProcessTreePids = async (\n rootPid: number,\n platform: NodeJS.Platform = process.platform,\n options: { runCapture?: typeof runCapture } = {},\n): Promise<ReadonlySet<number>> => {\n const capture = options.runCapture ?? runCapture;\n const tree = new Set<number>([rootPid]);\n if (platform === \"win32\") {\n // Windows tree enumeration needs CIM queries; direct ownership covers the\n // common single-process dev server. taskkill /T still tears down children.\n return tree;\n }\n const frontier = [rootPid];\n while (frontier.length > 0) {\n const pid = frontier.shift();\n if (pid === undefined) {\n break;\n }\n const stdout = await capture(\"pgrep\", [\"-P\", String(pid)]).catch(() => \"\");\n for (const line of stdout.split(\"\\n\")) {\n const child = Number.parseInt(line.trim(), 10);\n if (Number.isInteger(child) && child > 0 && !tree.has(child)) {\n tree.add(child);\n frontier.push(child);\n }\n }\n }\n return tree;\n};\n\nexport interface PortDiscoveryOptions {\n readonly platform?: NodeJS.Platform;\n readonly baseline: ReadonlySet<number>;\n readonly listListeners?: ListenersRunner;\n readonly verifyHttp?: (port: number) => Promise<boolean>;\n /** Resolves the PIDs whose listeners count as services (preview process tree). */\n readonly resolveOwnerPids?: () => Promise<ReadonlySet<number>>;\n readonly listOwners?: () => Promise<ListenerOwners>;\n readonly intervalMs?: number;\n}\n\nexport interface PortDiscoverySession {\n /** Known new services, first-seen order. */\n services(): readonly DiscoveredService[];\n /** One polling pass; resolves to services discovered during this pass. */\n poll(): Promise<readonly DiscoveredService[]>;\n stop(): void;\n}\n\n/**\n * Diff-based port discovery. Each poll re-enumerates listeners, keeps ports\n * absent from the baseline, and adds HTTP-verified ones in first-seen order.\n */\nexport const createPortDiscovery = (options: PortDiscoveryOptions): PortDiscoverySession => {\n const platform = options.platform ?? process.platform;\n const listListeners = options.listListeners ?? listListeningPorts;\n const verifyHttp = options.verifyHttp ?? verifyHttpService;\n const listOwners = options.listOwners ?? (() => listListeningPortOwners(platform));\n const services = new Map<number, DiscoveredService>();\n const verifying = new Set<number>();\n const rejected = new Set<number>();\n let stopped = false;\n\n const poll = async (): Promise<readonly DiscoveredService[]> => {\n if (stopped) {\n return [];\n }\n let listeners: ReadonlySet<number>;\n try {\n listeners = await listListeners(platform);\n } catch {\n return [];\n }\n\n // Ownership filter: only ports owned by the preview process tree may\n // become services, so foreign loopback listeners (browser DevTools\n // sockets, sync daemons) are never adopted.\n let owners: ListenerOwners | undefined;\n let ownerPids: ReadonlySet<number> | undefined;\n if (options.resolveOwnerPids !== undefined) {\n const [ownerMap, pids] = await Promise.all([\n listOwners().catch(() => undefined),\n options.resolveOwnerPids().catch(() => undefined),\n ]);\n owners = ownerMap;\n ownerPids = pids;\n }\n\n const added: DiscoveredService[] = [];\n const pending: Promise<void>[] = [];\n for (const port of listeners) {\n if (options.baseline.has(port) || services.has(port) || rejected.has(port)) {\n continue;\n }\n if (verifying.has(port)) {\n continue;\n }\n if (owners !== undefined && ownerPids !== undefined) {\n const portOwners = owners.get(port);\n const owned =\n portOwners !== undefined &&\n [...portOwners].some((pid) => ownerPids.has(pid));\n if (!owned) {\n // Another process owns this listener; ignore it permanently unless\n // ownership changes (re-checked next poll while unknown).\n continue;\n }\n }\n verifying.add(port);\n pending.push(\n verifyHttp(port)\n .then((ok) => {\n if (!ok) {\n rejected.add(port);\n return;\n }\n const service: DiscoveredService = {\n port,\n url: serviceUrl(port),\n firstSeenAt: Date.now(),\n };\n services.set(port, service);\n added.push(service);\n })\n .catch(() => {\n rejected.add(port);\n })\n .finally(() => {\n verifying.delete(port);\n }),\n );\n }\n await Promise.all(pending);\n return [...services.values()].sort((a, b) => a.firstSeenAt - b.firstSeenAt);\n };\n\n return {\n services: () => [...services.values()].sort((a, b) => a.firstSeenAt - b.firstSeenAt),\n poll,\n stop() {\n stopped = true;\n },\n };\n};\n\nexport const runCapture = async (\n command: string,\n args: readonly string[],\n): Promise<string> =>\n new Promise((resolve, reject) => {\n execFile(\n command,\n [...args],\n { encoding: \"utf8\", timeout: 10_000, windowsHide: true },\n (error, stdout) => {\n if (error !== null) {\n reject(error);\n return;\n }\n resolve(stdout);\n },\n );\n });\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n","/**\n * App-icon composition (owner round-12): the user's foreground icon is\n * composited onto one of three BACKGROUNDS — black, white, or transparent —\n * and the background is auto-selected for contrast with the artwork's own\n * luminance. The foreground's ORIGINAL PIXELS are always preserved (never\n * recolored); macOS receives an 824px-content variant inside the 1024 canvas\n * (platform best practice), Windows/Linux take the full 1024.\n *\n * The bundled background PNGs carry the squircle alpha mask. That mask is the\n * owner's clipping law (invert → polarize → mask): it is applied to EVERY\n * composition — including the transparent background, whose square source\n * would otherwise render un-rounded on macOS.\n */\nimport { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport sharp from \"sharp\";\n\nconst moduleDirectory = dirname(fileURLToPath(import.meta.url));\n\nexport const APP_ICON_CANVAS = 1024;\n/** macOS best-practice content size inside the 1024 canvas. */\nexport const MACOS_CONTENT_SIZE = 824;\n/** Default foreground inset ratio (user-adjustable 0.5–0.95). */\nexport const FOREGROUND_SCALE_DEFAULT = 0.8;\n\nexport type IconBackground = \"black\" | \"white\" | \"transparent\";\n\nexport const ICON_BACKGROUNDS: readonly IconBackground[] = [\n \"black\",\n \"white\",\n \"transparent\",\n];\n\nconst BACKGROUND_FILES: Record<Exclude<IconBackground, \"transparent\">, string> = {\n black: \"create-openspec-template-iOS-Dark-1024@1x.png\",\n white: \"create-openspec-template-iOS-Default-1024@1x.png\",\n};\n\nconst TRANSPARENT: sharp.Color = { r: 0, g: 0, b: 0, alpha: 0 };\n\nconst backgroundCache = new Map<Exclude<IconBackground, \"transparent\">, Buffer>();\n\nconst assetsDirectory = (): string =>\n // Source checkout: module lives in src/, assets in ../assets. Built bundle:\n // assets ship beside dist output.\n moduleDirectory.endsWith(`${sep}src`)\n ? join(moduleDirectory, \"..\", \"assets\")\n : join(moduleDirectory, \"assets\");\n\nconst loadBackground = async (\n background: Exclude<IconBackground, \"transparent\">,\n): Promise<Buffer> => {\n const cached = backgroundCache.get(background);\n if (cached !== undefined) {\n return cached;\n }\n const bytes = await readFile(join(assetsDirectory(), BACKGROUND_FILES[background]));\n backgroundCache.set(background, bytes);\n return bytes;\n};\n\n/**\n * Full-resolution RGBA pixels of a source (no geometry-altering resize).\n * `.rotate()` applies EXIF orientation so phone-photo icons compose upright.\n * SVG density is raised so large-viewBox art rasterizes instead of hitting\n * the pixel limit wholesale.\n */\nconst foregroundRaw = async (\n sourcePath: string,\n): Promise<{ data: Buffer; width: number; height: number }> => {\n // SVG raster size derives from viewBox BEFORE any resize can cap it, so a\n // huge viewBox trips sharp's input-pixel limit at decode time. Lift the\n // limit (the pipeline itself bounds the raster) and sample statistics at\n // a bounded size.\n const { data, info } = await sharp(sourcePath, {\n failOn: \"none\",\n density: 72,\n limitInputPixels: false,\n })\n .rotate()\n .resize(512, 512, { fit: \"inside\", withoutEnlargement: false })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n return { data, width: info.width, height: info.height };\n};\n\n/** Both analysis metrics from ONE decode (huge uploads must not double). */\nexport const foregroundStats = async (\n sourcePath: string,\n): Promise<{ luminance: number | undefined; coverage: number }> => {\n const { data, width, height } = await foregroundRaw(sourcePath);\n let weight = 0;\n let sum = 0;\n let opaque = 0;\n for (let i = 0; i < data.length; i += 4) {\n const a = (data[i + 3] ?? 0) / 255;\n if (a > 0) {\n const lum =\n (0.299 * (data[i] ?? 0) + 0.587 * (data[i + 1] ?? 0) + 0.114 * (data[i + 2] ?? 0)) / 255;\n weight += a;\n sum += lum * a;\n }\n if ((data[i + 3] ?? 0) > 16) opaque += 1;\n }\n const luminance = weight < width * height * 0.02 ? undefined : sum / weight;\n return { luminance, coverage: opaque / (width * height) };\n};\n\n/**\n * Mean luminance of the artwork's own pixels (0 = black … 1 = white);\n * effectively-empty images report undefined. Alpha-weighted so transparent\n * regions contribute nothing. The source is read at FULL size: a\n * fit-contain downscale would letterbox non-square art with sharp's default\n * OPAQUE BLACK padding and drag white artwork toward \"dark\" (the round-12\n * defect that made a white icon suggest the white background).\n */\nexport const foregroundLuminance = async (\n sourcePath: string,\n): Promise<number | undefined> => {\n const { data, width, height } = await foregroundRaw(sourcePath);\n let weight = 0;\n let sum = 0;\n for (let i = 0; i < data.length; i += 4) {\n const a = (data[i + 3] ?? 0) / 255;\n if (a <= 0) continue;\n const lum =\n (0.299 * (data[i] ?? 0) + 0.587 * (data[i + 1] ?? 0) + 0.114 * (data[i + 2] ?? 0)) /\n 255;\n weight += a;\n sum += lum * a;\n }\n if (weight < width * height * 0.02) {\n return undefined; // effectively empty\n }\n return sum / weight;\n};\n\n/**\n * Opaque coverage ratio (0–1): the fraction of the source canvas the artwork\n * actually paints. A fully opaque image (logo shot on a solid background)\n * reports 1 — the case where the TRANSPARENT background must be used so the\n * user's own art shows through untouched.\n */\nexport const foregroundCoverage = async (sourcePath: string): Promise<number> => {\n const { data, width, height } = await foregroundRaw(sourcePath);\n let opaque = 0;\n for (let i = 0; i < data.length; i += 4) {\n if ((data[i + 3] ?? 0) > 16) opaque += 1;\n }\n return opaque / (width * height);\n};\n\n/** Owner rule: pick the background for a foreground automatically. */\nexport const autoBackground = (options: {\n readonly luminance: number | undefined;\n readonly coverage: number;\n}): IconBackground => {\n // A fully opaque foreground already carries its own backdrop — compose on\n // transparency so the user's art passes through verbatim.\n if (options.coverage >= 0.985) {\n return \"transparent\";\n }\n // Light artwork → dark background; dark artwork → light background. The\n // artwork's own pixels stay untouched either way.\n return options.luminance !== undefined && options.luminance > 0.5\n ? \"black\"\n : \"white\";\n};\n\nlet squircleMaskPromise: Promise<Buffer> | undefined;\n\n/**\n * The squircle clip mask (1024², single channel) extracted from the bundled\n * background's alpha: 255 inside the rounded tile, 0 outside.\n */\nconst squircleMask = (): Promise<Buffer> => {\n squircleMaskPromise ??= (async () => {\n const bg = await loadBackground(\"white\");\n const { data, info } = await sharp(bg, { failOn: \"none\" })\n .ensureAlpha()\n .extractChannel(\"alpha\")\n .raw()\n .toBuffer({ resolveWithObject: true });\n if (info.width !== APP_ICON_CANVAS || info.height !== APP_ICON_CANVAS) {\n throw new Error(\"squircle mask must be 1024×1024\");\n }\n return data;\n })();\n return squircleMaskPromise;\n};\n\n/** Clip an RGBA buffer to the squircle via a dest-in alpha mask. */\nconst clipToSquircle = async (bytes: Buffer): Promise<Buffer> => {\n const mask = await squircleMask();\n // Overlay whose alpha IS the mask; dest-in keeps the destination only\n // where the mask is opaque (joinChannel does not reliably replace alpha).\n const overlay = Buffer.alloc(APP_ICON_CANVAS * APP_ICON_CANVAS * 4);\n for (let i = 0; i < APP_ICON_CANVAS * APP_ICON_CANVAS; i += 1) {\n const o = i * 4;\n overlay[o] = 255;\n overlay[o + 1] = 255;\n overlay[o + 2] = 255;\n overlay[o + 3] = mask[i] ?? 0;\n }\n return sharp(bytes)\n .composite([\n {\n input: overlay,\n blend: \"dest-in\",\n left: 0,\n top: 0,\n raw: { width: APP_ICON_CANVAS, height: APP_ICON_CANVAS, channels: 4 },\n },\n ])\n .png({ compressionLevel: 9 })\n .toBuffer();\n};\n\n/**\n * Composite the app icon. The foreground's ORIGINAL pixels are preserved on\n * every background — the background choice provides the contrast, not a\n * recolor of the artwork (the round-12 defect that painted a white icon\n * black).\n *\n * Output: `app-composited.png` at CANVAS size (Windows/Linux form — the tile\n * fills the canvas), plus `app-composited-macos.png` where the ENTIRE tile\n * (background + art) is scaled to 824 and centered on the transparent 1024\n * canvas. Dock icons since Big Sur carry those margins instead of running\n * edge-to-edge; scaling only the art (the earlier defect) left the tile\n * filling all available space.\n */\nexport const composeAppIcon = async (options: {\n readonly foregroundPath: string;\n readonly background: IconBackground;\n readonly scale?: number;\n readonly outputDir: string;\n}): Promise<{\n readonly compositePath: string;\n readonly macOSPath: string;\n readonly background: IconBackground;\n}> => {\n const scale = options.scale ?? FOREGROUND_SCALE_DEFAULT;\n const outputDir = options.outputDir;\n await mkdir(outputDir, { recursive: true });\n\n // Per-composition subdirectory: fixed filenames must never overwrite an\n // earlier composition, because the wizard serves previews by cache key.\n const key = compositionCacheKey({\n foregroundPath: options.foregroundPath,\n background: options.background,\n scale,\n });\n const compositionDir = join(outputDir, key);\n await mkdir(compositionDir, { recursive: true });\n\n const buildComposite = async (): Promise<Buffer> => {\n const fgSize = Math.round(APP_ICON_CANVAS * scale);\n const offset = Math.round((APP_ICON_CANVAS - fgSize) / 2);\n\n // The artwork, scaled with TRANSPARENT letterboxing (sharp's default\n // contain-padding is opaque black).\n const foreground = await sharp(options.foregroundPath, {\n failOn: \"none\",\n density: 72,\n limitInputPixels: false,\n })\n .rotate()\n .resize(fgSize, fgSize, { fit: \"contain\", background: TRANSPARENT })\n .png()\n .toBuffer();\n\n const base =\n options.background === \"transparent\"\n ? sharp({\n create: {\n width: APP_ICON_CANVAS,\n height: APP_ICON_CANVAS,\n channels: 4,\n background: TRANSPARENT,\n },\n })\n : sharp(await loadBackground(options.background));\n\n const composed = await base\n .composite([{ input: foreground, top: offset, left: offset }])\n .png({ compressionLevel: 9 })\n .toBuffer();\n\n // Every composition is clipped to the squircle. The bundled backgrounds\n // carry the mask in their own alpha, but OVER compositing still lets the\n // foreground's square corners escape the tile at large scales — so the\n // clip runs for ALL backgrounds, not just the transparent one.\n return clipToSquircle(composed);\n };\n\n const writeVariant = async (bytes: Buffer, suffix: string): Promise<string> => {\n const path = join(compositionDir, `app-composited${suffix}.png`);\n await writeFile(path, bytes);\n return path;\n };\n\n const full = await buildComposite();\n const macOSMargin = Math.round((APP_ICON_CANVAS - MACOS_CONTENT_SIZE) / 2);\n const macOSBytes = await sharp(full)\n .resize(MACOS_CONTENT_SIZE, MACOS_CONTENT_SIZE, { kernel: sharp.kernel.lanczos3 })\n .extend({\n top: macOSMargin,\n bottom: macOSMargin,\n left: macOSMargin,\n right: macOSMargin,\n background: TRANSPARENT,\n })\n .png({ compressionLevel: 9 })\n .toBuffer();\n\n const compositePath = await writeVariant(full, \"\");\n const macOSPath = await writeVariant(macOSBytes, \"-macos\");\n return { compositePath, macOSPath, background: options.background };\n};\n\n/** Stable cache key for a composed icon (wizard-side preview reuse). */\nexport const compositionCacheKey = (options: {\n readonly foregroundPath: string;\n readonly background: IconBackground;\n readonly scale: number;\n}): string =>\n createHash(\"sha256\")\n .update(`${options.foregroundPath}|${options.background}|${options.scale}`)\n .digest(\"hex\")\n .slice(0, 16);\n","// Orthogonal intents (maintained 2026-08-16; original user request: scrape the\n// service favicon and title as default identity, switching services rescrapes;\n// round-8 acceptance: collect EVERY icon candidate the page declares — SVG,\n// apple-touch-icon, sized PNG sets, /favicon.ico — measure true clarity,\n// dedupe near-identical images, and expose them as ranked clickable\n// candidates):\n// 1. Fetch the service root with proxy-free loopback semantics.\n// 2. Download all declared candidates (capped), never skipping SVG.\n// 3. Decode true pixel dimensions (sharp; ICO via directory/PNG-payload/DIB\n// extraction) and a perceptual hash; rank by clarity, hide near-duplicates.\n// 4. Keep scrape failures non-fatal with an empty result and a glyph fallback source.\n\nimport { createHash } from \"node:crypto\";\nimport { mkdtemp, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { ensureLoopbackNoProxy, serviceUrl } from \"./port-scan\";\n\n/** Variant tag: the original art, or a solid-color silhouette derived from it. */\nexport type IconVariant = \"original\" | \"solid-black\" | \"solid-white\";\n\n/** One scraped icon candidate, ranked and deduplicated. */\nexport interface ScrapedIcon {\n /** Index within the candidate list (stable for /api/icon-data/:port/:index). */\n readonly index: number;\n /** Absolute URL the bytes came from (variants inherit their source URL). */\n readonly url: string;\n /** Absolute temp file holding the icon bytes. */\n readonly path: string;\n /** True pixel clarity (largest dimension; SVG uses intrinsic or 512). */\n readonly width: number;\n readonly height: number;\n /** png | svg | jpeg | webp | gif | ico (ico payloads are extracted to png). */\n readonly format: string;\n /** Which art this entry carries (originals feed the app-icon picker; the\n * advanced tray picker also shows solid variants). */\n readonly variant: IconVariant;\n /** Index of the original candidate a variant was derived from. */\n readonly variantOf?: number;\n}\n\nexport interface ScrapeResult {\n readonly ok: boolean;\n readonly title: string | undefined;\n /** Absolute temp file holding the chosen (clearest) favicon bytes, when found. */\n readonly iconPath: string | undefined;\n readonly iconUrl?: string;\n /** All viable candidates ranked by clarity, near-duplicates removed. */\n readonly icons: readonly ScrapedIcon[];\n}\n\nexport interface FaviconCandidate {\n readonly href: string;\n readonly rel: string;\n readonly sizes?: string;\n}\n\n/** Extract `<title>` text from HTML. */\nexport const extractTitle = (html: string): string | undefined => {\n const match = /<title[^>]*>([\\s\\S]*?)<\\/title>/iu.exec(html);\n if (match === null || match[1] === undefined) {\n return undefined;\n }\n const decoded = match[1]\n .replace(/&amp;/gu, \"&\")\n .replace(/&lt;/gu, \"<\")\n .replace(/&gt;/gu, \">\")\n .replace(/&quot;/gu, '\"')\n .replace(/&#39;/gu, \"'\");\n const trimmed = decoded.replace(/\\s+/gu, \" \").trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\n/** Extract `<link rel=... href=...>` favicon candidates from HTML head. */\nexport const extractFaviconCandidates = (html: string): readonly FaviconCandidate[] => {\n const candidates: FaviconCandidate[] = [];\n const pattern = /<link\\b[^>]*>/giu;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(html)) !== null) {\n const tag = match[0];\n const rel = /rel\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/iu.exec(tag);\n const href = /href\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/iu.exec(tag);\n if (rel === null || href === null) {\n continue;\n }\n const relValue = (rel[2] ?? rel[3] ?? \"\").trim().toLowerCase();\n if (!relValue.includes(\"icon\") || relValue.includes(\"mask\")) {\n continue;\n }\n const hrefValue = (href[2] ?? href[3] ?? \"\").trim();\n if (hrefValue.length === 0) {\n continue;\n }\n const sizesMatch = /sizes\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/iu.exec(tag);\n const sizesValue = sizesMatch?.[2] ?? sizesMatch?.[3];\n candidates.push({\n href: hrefValue,\n rel: relValue,\n ...(sizesValue === undefined ? {} : { sizes: sizesValue }),\n });\n }\n return candidates;\n};\n\n/** Largest dimension of a `sizes` attribute value such as `32x32` or `any`. */\nexport const faviconCandidateSize = (candidate: FaviconCandidate): number => {\n if (candidate.sizes === undefined) {\n return 0;\n }\n const match = /(\\d+)\\s*x\\s*(\\d+)/iu.exec(candidate.sizes);\n if (match === null || match[1] === undefined || match[2] === undefined) {\n return 0;\n }\n return Math.max(Number.parseInt(match[1], 10), Number.parseInt(match[2], 10));\n};\n\n/** Resolve a favicon href against the service origin. */\nexport const resolveFaviconUrl = (href: string, origin: string): string | undefined => {\n try {\n return new URL(href, origin).href;\n } catch {\n return undefined;\n }\n};\n\n/** Order candidates: declared-size icons descending, then apple-touch-icon, then others. */\nexport const rankFaviconCandidates = (\n candidates: readonly FaviconCandidate[],\n): readonly FaviconCandidate[] => {\n const score = (candidate: FaviconCandidate): number => {\n const declared = faviconCandidateSize(candidate);\n if (declared > 0) {\n return declared;\n }\n if (candidate.rel.includes(\"apple-touch-icon\")) {\n return 128;\n }\n return 1;\n };\n return [...candidates].sort((a, b) => score(b) - score(a));\n};\n\nexport interface ScrapePage {\n readonly ok: boolean;\n readonly status: number;\n readonly body: string;\n readonly headers: Record<string, string>;\n}\n\nexport interface ScrapeBytes {\n readonly ok: boolean;\n readonly status: number;\n readonly bytes: Buffer;\n readonly contentType: string;\n}\n\nexport interface ScrapeFetch {\n page(url: string, timeoutMs?: number): Promise<ScrapePage>;\n bytes(url: string, timeoutMs?: number): Promise<ScrapeBytes>;\n}\n\nconst fetchWithTimeout = async (\n url: string,\n timeoutMs: number,\n accept: string,\n): Promise<Response | undefined> => {\n ensureLoopbackNoProxy();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, {\n signal: controller.signal,\n redirect: \"follow\",\n headers: { accept },\n });\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n};\n\nconst defaultFetch: ScrapeFetch = {\n async page(url, timeoutMs = 5_000) {\n const response = await fetchWithTimeout(\n url,\n timeoutMs,\n \"text/html,application/xhtml+xml\",\n );\n if (response === undefined) {\n return { ok: false, status: 0, body: \"\", headers: {} };\n }\n const body = await response.text();\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n return { ok: response.ok, status: response.status, body, headers };\n },\n async bytes(url, timeoutMs = 5_000) {\n const response = await fetchWithTimeout(url, timeoutMs, \"image/*,*/*;q=0.8\");\n if (response === undefined) {\n return { ok: false, status: 0, bytes: Buffer.alloc(0), contentType: \"\" };\n }\n const buffer = Buffer.from(await response.arrayBuffer());\n return {\n ok: response.ok,\n status: response.status,\n bytes: buffer,\n contentType: (response.headers.get(\"content-type\") ?? \"\").toLowerCase(),\n };\n },\n};\n\n/** Cap on downloaded candidates per scrape. */\nconst MAX_ICON_DOWNLOADS = 8;\n\n/**\n * Scrape title and ALL icon candidates from a service port. Never throws:\n * failures return `ok: false` with whatever partial identity was found.\n */\nexport const scrapeService = async (\n port: number,\n options: { fetch?: ScrapeFetch; tempDir?: string } = {},\n): Promise<ScrapeResult> => {\n const fetchImpl = options.fetch ?? defaultFetch;\n const origin = serviceUrl(port);\n const page = await fetchImpl.page(origin);\n if (!page.ok) {\n return {\n ok: false,\n title: undefined,\n iconPath: undefined,\n icons: [],\n };\n }\n\n const title = extractTitle(page.body);\n const candidates = rankFaviconCandidates(extractFaviconCandidates(page.body));\n const orderedUrls = [\n ...candidates.map((candidate) => resolveFaviconUrl(candidate.href, origin)),\n `${origin}/favicon.ico`,\n ]\n .filter((url): url is string => url !== undefined)\n .filter((url, index, all) => all.indexOf(url) === index)\n .slice(0, MAX_ICON_DOWNLOADS);\n\n const dir = await ensureTempIconDir(options.tempDir);\n const collected: { url: string; path: string; width: number; height: number; format: string; hash: string | undefined }[] = [];\n for (const url of orderedUrls) {\n if (collected.length >= MAX_ICON_DOWNLOADS) {\n break;\n }\n const icon = await fetchImpl.bytes(url);\n if (!icon.ok || icon.bytes.length < 64) {\n continue;\n }\n const prepared = await prepareIconBytes(icon.bytes, icon.contentType);\n if (prepared === undefined) {\n // HTML fallback routes, corrupt images, undecodable payloads: skip.\n continue;\n }\n const { bytes, format } = prepared;\n const meta = await iconDimensions(bytes, format);\n if (meta === undefined) {\n continue;\n }\n const hash = await iconPerceptualHash(bytes);\n // Near-duplicate clarity: same image at another size is redundant.\n if (hash !== undefined && collected.some((c) => c.hash !== undefined && hamming(hash, c.hash) <= 8)) {\n continue;\n }\n const path = await writeIconTemp(bytes, dir);\n collected.push({ url, path, ...meta, format, hash });\n }\n\n // Rank by true clarity: pixel area descending (SVG scales infinitely, so\n // intrinsic (or default 512) dimensions rank it with the clearest sources).\n collected.sort((a, b) => b.width * a.height === a.width * b.height ? 0 : b.width * b.height - a.width * a.height);\n const originals = collected.map((c, order) => ({ ...c, order }));\n\n // Solid-color silhouettes (tray/template candidates): alpha mask filled\n // with one color, rendered at a tray-appropriate size, deduped among\n // themselves — identical silhouettes from different sources collapse.\n const solids: { url: string; path: string; variant: IconVariant; variantOf: number; hash: string | undefined }[] = [];\n for (const original of originals) {\n for (const variant of [\"solid-black\", \"solid-white\"] as const) {\n const solid = await renderSolidSilhouette(original.path, variant === \"solid-black\" ? black : white);\n if (solid === undefined) {\n continue;\n }\n const hash = await iconPerceptualHash(solid);\n if (hash !== undefined && solids.some((s) => s.hash !== undefined && hamming(hash, s.hash) <= 6)) {\n continue;\n }\n const path = await writeIconTemp(solid, dir);\n solids.push({ url: original.url, path, variant, variantOf: original.order, hash });\n }\n }\n\n const icons: ScrapedIcon[] = [\n ...originals.map<ScrapedIcon>((c) => ({\n index: c.order,\n url: c.url,\n path: c.path,\n width: c.width,\n height: c.height,\n format: c.format,\n variant: \"original\",\n })),\n ...solids.map<ScrapedIcon>((s, i) => ({\n index: originals.length + i,\n url: s.url,\n path: s.path,\n width: SOLID_SIZE,\n height: SOLID_SIZE,\n format: \"png\",\n variant: s.variant,\n variantOf: s.variantOf,\n })),\n ];\n\n return {\n ok: true,\n title,\n iconPath: originals[0]?.path,\n ...(originals[0] === undefined ? {} : { iconUrl: originals[0].url }),\n icons,\n };\n}\n\nconst black = { r: 0, g: 0, b: 0 };\nconst white = { r: 255, g: 255, b: 255 };\nconst SOLID_SIZE = 128;\n\n/**\n * Render a solid-color silhouette from an icon's alpha mask (RGB discarded,\n * alpha kept) — the shape language macOS tray templates want. Non-decodable\n * sources (e.g. corrupt bytes) return undefined instead of failing the scrape.\n */\nconst renderSolidSilhouette = async (\n sourcePath: string,\n color: { r: number; g: number; b: number },\n): Promise<Buffer | undefined> => {\n try {\n const sharpModule = await import(\"sharp\");\n const sharp = sharpModule.default;\n const { data, info } = await sharp(sourcePath, { failOn: \"none\" })\n .resize(SOLID_SIZE, SOLID_SIZE, {\n fit: \"contain\",\n background: { r: 0, g: 0, b: 0, alpha: 0 },\n })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n if (info.channels !== 4) {\n return undefined;\n }\n const out = Buffer.alloc(data.length);\n for (let i = 0; i < data.length; i += 4) {\n out[i] = color.r;\n out[i + 1] = color.g;\n out[i + 2] = color.b;\n out[i + 3] = data[i + 3] ?? 0;\n }\n return sharp(out, { raw: { width: info.width, height: info.height, channels: 4 } })\n .png()\n .toBuffer();\n } catch {\n return undefined;\n }\n};;\n\n/** Recognizable raster image signatures (PNG/JPEG/GIF/ICO/BMP/WebP). */\nconst hasRasterImageSignature = (bytes: Buffer): boolean => {\n if (bytes.length < 16) {\n return false;\n }\n if (\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47 &&\n bytes[4] === 0x0d &&\n bytes[5] === 0x0a &&\n bytes[6] === 0x1a &&\n bytes[7] === 0x0a &&\n bytes.subarray(12, 16).toString(\"latin1\") === \"IHDR\"\n ) {\n return true; // PNG with a well-formed leading IHDR chunk\n }\n if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {\n return true; // JPEG\n }\n if (\n bytes.subarray(0, 4).toString(\"latin1\") === \"RIFF\" &&\n bytes.subarray(8, 12).toString(\"latin1\") === \"WEBP\"\n ) {\n return true; // WebP\n }\n if (bytes[0] === 0x00 && bytes[1] === 0x00 && bytes[2] === 0x01 && bytes[3] === 0x00) {\n return true; // ICO\n }\n if (bytes.subarray(0, 6).toString(\"latin1\").startsWith(\"GIF8\")) {\n return true; // GIF\n }\n if (bytes[0] === 0x42 && bytes[1] === 0x4d) {\n return true; // BMP\n }\n return false;\n};\n\n\nconst ensureTempIconDir = async (tempDir?: string): Promise<string | undefined> => {\n if (tempDir !== undefined) {\n return tempDir;\n }\n return mkdtemp(join(tmpdir(), \"create-opentray-icon-\"));\n};\n\n/**\n * Validate and normalize candidate bytes. Returns decodable image bytes plus a\n * format tag: raster signatures pass through, SVG text passes through, and ICO\n * containers are cracked open to their largest frame (PNG payload extracted\n * verbatim, BMP DIB rows converted to PNG) because sharp cannot read ICO.\n */\nconst prepareIconBytes = async (\n bytes: Buffer,\n contentType: string,\n): Promise<{ bytes: Buffer; format: string } | undefined> => {\n if (looksLikeSvg(bytes, contentType)) {\n return { bytes: await densifySvg(bytes), format: \"svg\" };\n }\n // ICO first: its magic overlaps the generic raster check, but sharp cannot\n // read ICO — crack the container to its largest frame.\n if (isIcoContainer(bytes)) {\n const extracted = await extractLargestIcoFrame(bytes);\n if (extracted !== undefined) {\n return { bytes: extracted, format: \"png\" };\n }\n return undefined;\n }\n if (hasRasterImageSignature(bytes)) {\n const format = rasterFormatOf(bytes);\n return format === undefined ? undefined : { bytes, format };\n }\n return undefined;\n};\n\n/**\n * Rewrite an SVG so it rasterizes at high resolution. sharp (librsvg) pays\n * no attention to a density attribute; it renders at the declared\n * width/height. Scraped favicons declare small intrinsic sizes (often just\n * 16–50px), so the rasterized base bitmap is tiny and every later upscale\n * (icon catalog, tray, candidates) is blurry. Rewriting the root <svg>\n * width/height to a large target — viewBox untouched, so vector geometry\n * scales cleanly — gives every downstream consumer a crisp base.\n */\nexport const SVG_RASTER_TARGET = 1024;\n\nconst densifySvg = async (bytes: Buffer): Promise<Buffer> => {\n const text = bytes.toString(\"utf8\");\n const svgOpen = text.indexOf(\"<svg\");\n if (svgOpen === -1) {\n return bytes;\n }\n const tagEnd = text.indexOf(\">\", svgOpen);\n if (tagEnd === -1) {\n return bytes;\n }\n const openTag = text.slice(svgOpen, tagEnd + 1);\n let next = openTag;\n if (/\\swidth=/u.test(next)) {\n next = next.replace(/\\swidth=\"[^\"]*\"/u, ` width=\"${SVG_RASTER_TARGET}\"`);\n } else {\n next = next.replace(\"<svg\", `<svg width=\"${SVG_RASTER_TARGET}\"`);\n }\n if (/\\sheight=/u.test(next)) {\n next = next.replace(/\\sheight=\"[^\"]*\"/u, ` height=\"${SVG_RASTER_TARGET}\"`);\n } else {\n next = next.replace(\"<svg\", `<svg height=\"${SVG_RASTER_TARGET}\"`);\n }\n if (next === openTag) {\n return bytes;\n }\n return Buffer.from(text.slice(0, svgOpen) + next + text.slice(tagEnd + 1), \"utf8\");\n};\n\nconst looksLikeSvg = (bytes: Buffer, contentType: string): boolean => {\n if (contentType.includes(\"image/svg\")) {\n return true;\n }\n const head = bytes.subarray(0, 512).toString(\"utf8\").trimStart();\n return head.startsWith(\"<?xml\") || head.startsWith(\"<svg\") || head.includes(\"<svg\");\n};\n\nconst rasterFormatOf = (bytes: Buffer): string | undefined => {\n if (bytes[0] === 0x89 && bytes[1] === 0x50) return \"png\";\n if (bytes[0] === 0xff && bytes[1] === 0xd8) return \"jpeg\";\n if (bytes[0] === 0x47 && bytes[1] === 0x49) return \"gif\";\n if (bytes.subarray(0, 4).toString(\"latin1\") === \"RIFF\") return \"webp\";\n return undefined;\n};\n\nconst isIcoContainer = (bytes: Buffer): boolean =>\n bytes.length >= 8 &&\n bytes[0] === 0x00 && bytes[1] === 0x00 &&\n bytes[2] === 0x01 && bytes[3] === 0x00;\n\n/** Crack an ICO open: pick the largest frame; PNG payloads return verbatim, DIB rows become PNG. */\nconst extractLargestIcoFrame = async (ico: Buffer): Promise<Buffer | undefined> => {\n const count = ico.readUInt16LE(4);\n if (count === 0 || count > 64) {\n return undefined;\n }\n let best: { offset: number; size: number; width: number; height: number } | undefined;\n for (let i = 0; i < count; i += 1) {\n const base = 6 + i * 16;\n if (base + 16 > ico.length) {\n break;\n }\n const rawWidth = ico[base] ?? 0;\n const rawHeight = ico[base + 1] ?? 0;\n const width = rawWidth === 0 ? 256 : rawWidth;\n const height = rawHeight === 0 ? 256 : rawHeight;\n const size = ico.readUInt32LE(base + 8);\n const offset = ico.readUInt32LE(base + 12);\n if (offset + size > ico.length) {\n continue;\n }\n if (best === undefined || width * height > best.width * best.height) {\n best = { offset, size, width, height };\n }\n }\n if (best === undefined) {\n return undefined;\n }\n const frame = ico.subarray(best.offset, best.offset + best.size);\n const b0 = frame[0];\n const b1 = frame[1];\n if (b0 === 0x89 && b1 === 0x50) {\n return frame; // PNG-compressed entry (modern 256px icons)\n }\n return dibToPng(frame, best.width, best.height);\n};\n\n/** Convert a bottom-up BGRA/BGR DIB (BITMAPINFOHEADER) frame to PNG bytes. */\nconst dibToPng = async (dib: Buffer, width: number, height: number): Promise<Buffer | undefined> => {\n if (dib.length < 40) {\n return undefined;\n }\n const declaredHeight = dib.readInt32LE(8);\n const bitCount = dib.readUInt16LE(14);\n const pixels = dib.subarray(40);\n const rowBytes = Math.ceil((width * bitCount) / 8);\n const rows = Math.abs(declaredHeight) / 2;\n if (rows === 0 || pixels.length < rowBytes * rows) {\n return undefined;\n }\n const channels = bitCount === 32 ? 4 : 3;\n const rgba = Buffer.alloc(width * rows * channels);\n for (let y = 0; y < rows; y += 1) {\n const src = pixels.subarray(y * rowBytes, (y + 1) * rowBytes);\n const flipped = rows - 1 - y;\n for (let x = 0; x < width; x += 1) {\n const srcIdx = x * channels;\n const dstIdx = (flipped * width + x) * channels;\n const b = src[srcIdx];\n const g = src[srcIdx + 1];\n const r = src[srcIdx + 2];\n if (b === undefined || g === undefined || r === undefined) {\n continue;\n }\n rgba[dstIdx] = r;\n rgba[dstIdx + 1] = g;\n rgba[dstIdx + 2] = b;\n if (channels === 4) {\n rgba[dstIdx + 3] = src[srcIdx + 3] ?? 255;\n }\n }\n }\n const { toPngBuffer } = await import(\"./icon-codec.js\");\n return toPngBuffer(rgba, width, rows, channels);\n};\n\n/** True pixel dimensions; SVG uses intrinsic attrs, else viewBox, else 512. */\nconst iconDimensions = async (\n bytes: Buffer,\n format: string,\n): Promise<{ width: number; height: number } | undefined> => {\n if (format === \"svg\") {\n return svgDimensions(bytes) ?? { width: 512, height: 512 };\n }\n try {\n const sharpModule = await import(\"sharp\");\n const sharp = sharpModule.default;\n const meta = await sharp(bytes, { failOn: \"none\" }).metadata();\n if (meta.width !== undefined && meta.height !== undefined && meta.width > 0) {\n return { width: meta.width, height: meta.height };\n }\n } catch {\n // fall through to raster header parsing\n }\n if (format === \"png\" && bytes.length >= 24) {\n const width = bytes.readUInt32BE(16);\n const height = bytes.readUInt32BE(20);\n if (width > 0) return { width, height };\n }\n return undefined;\n};\n\nconst svgDimensions = (bytes: Buffer): { width: number; height: number } | undefined => {\n const head = bytes.subarray(0, 2048).toString(\"utf8\");\n const num = (value: string | undefined): number | undefined => {\n if (value === undefined) return undefined;\n const parsed = Number.parseFloat(value);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n };\n const width = num(/<svg[^>]*\\bwidth\\s*=\\s*[\"']([\\d.]+)/iu.exec(head)?.[1]);\n const height = num(/<svg[^>]*\\bheight\\s*=\\s*[\"']([\\d.]+)/iu.exec(head)?.[1]);\n if (width !== undefined && height !== undefined) {\n return { width, height };\n }\n const viewBox = /viewBox\\s*=\\s*[\"']\\s*([\\d.]+)[\\s,]+([\\d.]+)[\\s,]+([\\d.]+)[\\s,]+([\\d.]+)/iu.exec(head);\n const vbWidthRaw = viewBox?.[3];\n const vbHeightRaw = viewBox?.[4];\n if (vbWidthRaw !== undefined && vbHeightRaw !== undefined) {\n const vbWidth = Number.parseFloat(vbWidthRaw);\n const vbHeight = Number.parseFloat(vbHeightRaw);\n if (Number.isFinite(vbWidth) && vbWidth > 0) {\n return { width: vbWidth, height: Number.isFinite(vbHeight) ? vbHeight : vbWidth };\n }\n }\n return undefined;\n};\n\n/** 64-bit average hash over an 8x8 grayscale normalization (perceptual dedupe). */\nconst iconPerceptualHash = async (bytes: Buffer): Promise<string | undefined> => {\n try {\n const sharpModule = await import(\"sharp\");\n const sharp = sharpModule.default;\n const { data } = await sharp(bytes, { failOn: \"none\" })\n .removeAlpha()\n .flatten({ background: \"#ffffff\" })\n .resize(8, 8, { fit: \"fill\" })\n .grayscale()\n .raw()\n .toBuffer({ resolveWithObject: true });\n if (data.length < 64) {\n return undefined;\n }\n let sum = 0;\n for (const value of data.subarray(0, 64)) {\n sum += value;\n }\n const mean = sum / 64;\n let hash = \"\";\n for (let i = 0; i < 64; i += 1) {\n const value = data[i];\n hash += value === undefined ? \"0\" : value >= mean ? \"1\" : \"0\";\n }\n return hash;\n } catch {\n return undefined;\n }\n};\n\nconst hamming = (a: string, b: string): number => {\n let distance = 0;\n for (let i = 0; i < Math.min(a.length, b.length); i += 1) {\n if (a[i] !== b[i]) {\n distance += 1;\n }\n }\n return distance;\n};\n\nconst writeIconTemp = async (bytes: Buffer, dir?: string): Promise<string> => {\n const targetDir = dir ?? (await mkdtemp(join(tmpdir(), \"create-opentray-icon-\")));\n // Unique per bytes: multiple candidates of one scrape live side by side.\n const name = `icon-${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16)}.bin`;\n const path = join(targetDir, name);\n await writeFile(path, bytes);\n return path;\n};\n\n/**\n * First-letter glyph fallback source: a self-contained SVG that the icon\n * generator can rasterize when no favicon was usable.\n */\nexport const createGlyphIconSvg = (appName: string, accent = \"#0A84FF\"): string => {\n const letter = (appName.trim().charAt(0) || \"A\").toUpperCase();\n const escaped = letter.replace(/&/gu, \"&amp;\").replace(/</gu, \"&lt;\").replace(/>/gu, \"&gt;\");\n return [\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"512\" height=\"512\" viewBox=\"0 0 512 512\">`,\n `<rect width=\"512\" height=\"512\" rx=\"96\" fill=\"${accent}\"/>`,\n `<text x=\"256\" y=\"256\" font-family=\"-apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\" font-size=\"280\" font-weight=\"600\" fill=\"#FFFFFF\" text-anchor=\"middle\" dominant-baseline=\"central\">${escaped}</text>`,\n `</svg>`,\n ].join(\"\");\n};\n\n/** Persist the glyph fallback SVG as a temp icon source. */\nexport const writeGlyphIconTemp = async (\n appName: string,\n tempDir: string,\n): Promise<string> => {\n const path = join(tempDir, \"glyph.svg\");\n await writeFile(path, createGlyphIconSvg(appName), \"utf8\");\n return path;\n};\n","// Orthogonal intents (maintained 2026-07-22; original user request: the wizard\n// runs the user's start command once and derives identity from it):\n// 1. Split a command line into an argv without invoking a shell.\n// 2. Preserve quoting and escaping semantics for POSIX-like input.\n// 3. Report unbalanced quotes instead of silently dropping tokens.\n\nexport interface TokenizeResult {\n readonly ok: boolean;\n readonly tokens: readonly string[];\n readonly error: string | undefined;\n}\n\n/**\n * Shell-style tokenizer for the wizard command input. Supports single quotes,\n * double quotes, and backslash escapes outside quotes. It never executes\n * anything; it only produces the argv that will be spawned.\n */\nexport const tokenizeCommandLine = (input: string): TokenizeResult => {\n const tokens: string[] = [];\n let current = \"\";\n let hasCurrent = false;\n let index = 0;\n\n while (index < input.length) {\n const char = input[index];\n if (char === undefined) {\n break;\n }\n\n if (isWhitespace(char)) {\n if (hasCurrent) {\n tokens.push(current);\n current = \"\";\n hasCurrent = false;\n }\n index += 1;\n continue;\n }\n\n if (char === '\"') {\n const quoted = readQuoted(input, index, '\"');\n if (quoted === undefined) {\n return { ok: false, tokens: [], error: \"unbalanced double quote in command\" };\n }\n current += quoted.value;\n hasCurrent = true;\n index = quoted.nextIndex;\n continue;\n }\n\n if (char === \"'\") {\n const quoted = readQuoted(input, index, \"'\");\n if (quoted === undefined) {\n return { ok: false, tokens: [], error: \"unbalanced single quote in command\" };\n }\n current += quoted.value;\n hasCurrent = true;\n index = quoted.nextIndex;\n continue;\n }\n\n if (char === \"\\\\\" && index + 1 < input.length) {\n const next = input[index + 1];\n if (next !== undefined) {\n current += next;\n hasCurrent = true;\n index += 2;\n continue;\n }\n }\n\n current += char;\n hasCurrent = true;\n index += 1;\n }\n\n if (hasCurrent) {\n tokens.push(current);\n }\n\n if (tokens.length === 0) {\n return { ok: false, tokens: [], error: \"command is empty\" };\n }\n return { ok: true, tokens, error: undefined };\n};\n\nconst readQuoted = (\n input: string,\n start: number,\n quote: '\"' | \"'\",\n): { value: string; nextIndex: number } | undefined => {\n let value = \"\";\n let index = start + 1;\n while (index < input.length) {\n const char = input[index];\n if (char === undefined) {\n break;\n }\n if (char === quote) {\n return { value, nextIndex: index + 1 };\n }\n if (quote === '\"' && char === \"\\\\\" && index + 1 < input.length) {\n const next = input[index + 1];\n if (next !== undefined && (next === '\"' || next === \"\\\\\")) {\n value += next;\n index += 2;\n continue;\n }\n }\n value += char;\n index += 1;\n }\n return undefined;\n};\n\nconst isWhitespace = (char: string): boolean =>\n char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\";\n","// Orthogonal intents (2026-08-16; owner round-9 demands: startup terminal\n// PTY streaming, shell window, tray icon wiring, multi-port monitor with\n// (detached) title marking):\n// 1. Render the generated app entry from the frozen config as one real TS\n// template literal (compile-checked here; no string surgery at build time).\n// 2. Shell mode: window opens immediately, command runs through a PTY, ports\n// are monitored and detaches mark the window title.\n// 3. Plain mode: wait-for-service window with favicon/title sync.\n// 4. Tray icon: config asset → platform icon candidates (darwin template\n// variant for solid art), text-only fallback otherwise.\nimport type { ScaffoldAppConfig } from \"./scaffold\";\n\n/** The generated app entry: supervises the command, owns tray + window. */\nexport const createEntrySource = (config: ScaffoldAppConfig): string => `#!/usr/bin/env node\n// Generated by create-opentray. Supervises the recorded command and hosts it\n// in an OpenTray tray + appMode window. Quit lives in the tray menu.\nimport { spawn, execFile, spawnSync } from \"node:child_process\";\nimport { appendFile, mkdir, readFile } from \"node:fs/promises\";\nimport http from \"node:http\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { createTray } from \"opentray\";\nimport { WebviewExt } from \"@opentray/ext-webview\";\n\nconst PROJECT_DIR = dirname(fileURLToPath(import.meta.url));\n\n// App Launch Law: persist the ABSOLUTE JS runtime. Under a Bun-hosted\n// wizard, execPath is bun — resolve node's real path once at startup\n// instead of persisting a bare PATH lookup (launchd's PATH is minimal).\nconst nodeRuntime = () => {\n if (process.versions.bun === undefined) return process.execPath;\n try {\n const found = spawnSync(\"which\", [\"node\"], { encoding: \"utf8\" }).stdout?.trim() ?? \"\";\n if (found.length > 0) return found;\n } catch { /* fall through */ }\n return \"node\";\n};\nconst READY_MARK_PREFIX = \"opentray: ready\";\n\nconst config = ${JSON.stringify(config, null, 2)};\n\nconst appLogPath = resolve(PROJECT_DIR, \"app.log\");\nawait mkdir(dirname(appLogPath), { recursive: true });\nconst logSink = appendFile.bind(undefined, appLogPath);\n\nconst shellOptions = ${JSON.stringify(config.shell ?? null)};\nconst hasShell = shellOptions !== null && (shellOptions.showTerminal || shellOptions.showAddressBar);\nconst showTerminal = shellOptions !== null && shellOptions.showTerminal === true;\nconst shellApi = hasShell ? await import(\"./app-shell-server.mjs\") : null;\n\n// Configured env overlay (advanced command options); empty by default.\nconst commandEnv = ${JSON.stringify(config.command.env ?? {})};\n\nlet command;\nlet commandExited = false;\nif (showTerminal) {\n // Startup terminal (advanced option): run the command through a PTY and\n // stream its bytes to the shell UI — the same tab experience the wizard has.\n let ptyModule;\n try {\n ptyModule = await import(\"@lydell/node-pty\");\n } catch {\n ptyModule = null;\n }\n const cwd = resolve(PROJECT_DIR, config.command.cwd);\n if (ptyModule !== null) {\n const pty = ptyModule.spawn(config.command.command, [...config.command.args], {\n name: \"xterm-256color\",\n cols: 100,\n rows: 30,\n cwd,\n env: { ...process.env, ...commandEnv, TERM: \"xterm-256color\" },\n });\n shellApi?.registerPty(pty);\n pty.onData((chunk) => {\n void logSink(chunk, \"utf8\");\n shellApi?.pushOutput(chunk);\n });\n const exited = new Promise((resolvePromise) => {\n pty.onExit(({ exitCode }) => {\n commandExited = true;\n void logSink(\\`[command exited \\${exitCode}]\\n\\`, \"utf8\");\n resolvePromise(exitCode);\n });\n });\n command = { pid: pty.pid, exited, kill: async () => { try { pty.kill(); } catch {} await exited; } };\n } else {\n await logSink(\"[create-opentray] @lydell/node-pty unavailable; startup terminal degraded to pipes\\\\n\", \"utf8\");\n }\n}\nif (command === undefined) {\n const child = spawn(config.command.command, [...config.command.args], {\n cwd: resolve(PROJECT_DIR, config.command.cwd),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...commandEnv },\n windowsHide: true,\n });\n child.stdout.setEncoding(\"utf8\");\n child.stderr.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk) => { void logSink(chunk, \"utf8\"); shellApi?.pushOutput(chunk); });\n child.stderr.on(\"data\", (chunk) => { void logSink(chunk, \"utf8\"); shellApi?.pushOutput(chunk); });\n child.once(\"exit\", (code) => {\n commandExited = true;\n void logSink(\\`[command exited \\${code ?? \"signal\"}]\\n\\`, \"utf8\");\n });\n command = child;\n}\n\nconst killCommand = async () => {\n if (commandExited) return;\n // command.kill is always a function here: the PTY wrapper (shell mode) or\n // the ChildProcess (plain mode). The PTY wrapper tears down the session\n // tree; plain spawns SIGTERM the direct child.\n await command.kill();\n};\n\nlet appIcon;\ntry {\n const manifest = JSON.parse(await readFile(resolve(PROJECT_DIR, \"app-icon\", \"app-icon.json\"), \"utf8\"));\n // The manifest stores project-relative source paths; OpenTray validates file\n // sources against the caller's cwd, so canonicalize them to absolute paths\n // before dispatch (a reused broker cwd must never reinterpret App identity).\n appIcon = manifest.appIcon.map((asset) => (\n asset.source?.type === \"file\" && !asset.source.path.startsWith(\"/\")\n ? { ...asset, source: { ...asset.source, path: resolve(PROJECT_DIR, \"app-icon\", asset.source.path) } }\n : asset\n ));\n} catch {\n appIcon = undefined;\n}\n\nconst httpAnswers = (port) => new Promise((resolvePromise) => {\n const request = http.get({ host: \"127.0.0.1\", port, path: \"/\", timeout: 1500 }, (response) => {\n response.resume();\n resolvePromise(response.statusCode !== undefined && response.statusCode > 0);\n });\n request.once(\"timeout\", () => { request.destroy(); resolvePromise(false); });\n request.once(\"error\", () => resolvePromise(false));\n});\n\nconst listProcessTreePids = async (rootPid) => {\n if (process.platform === \"win32\") return [rootPid];\n const pids = new Set([rootPid]);\n const frontier = [rootPid];\n while (frontier.length > 0) {\n const pid = frontier.pop();\n const children = await new Promise((resolvePromise) => {\n execFile(\"pgrep\", [\"-P\", String(pid)], (error, stdout) => {\n resolvePromise(error ? [] : stdout.split(\"\\\\n\").map((line) => Number(line)).filter((n) => Number.isInteger(n) && n > 0));\n });\n });\n for (const child of children) {\n if (!pids.has(child)) { pids.add(child); frontier.push(child); }\n }\n }\n return [...pids];\n};\n\nconst listOwnedListeningPorts = async (rootPid) => {\n if (process.platform === \"win32\") return [];\n const mine = new Set((await listProcessTreePids(rootPid)).map((pid) => String(pid)));\n const output = await new Promise((resolvePromise) => {\n execFile(\"lsof\", [\"-F\", \"pPn\", \"-nP\", \"-i\", \"TCP\", \"-sTCP:LISTEN\"], (error, stdout) => {\n resolvePromise(error ? \"\" : stdout);\n });\n });\n const ports = new Set();\n let currentPid = \"\";\n for (const line of output.split(\"\\\\n\")) {\n if (line.length === 0) continue;\n const tag = line[0];\n const value = line.slice(1);\n if (tag === \"p\") currentPid = value;\n else if (tag === \"n\" && currentPid !== \"\" && mine.has(currentPid)) {\n const port = Number(value.split(\":\").pop());\n if (Number.isInteger(port) && port > 0) ports.add(port);\n }\n }\n return [...ports];\n};\n\n// PORTS COME EXCLUSIVELY FROM SNIFFING (owner law): scan the command's\n// process tree for listening ports and HTTP-verify each candidate. The\n// recorded preview port is informational only and is never addressed\n// without verification; dynamic-port commands (listen(0)) therefore behave\n// identically to fixed-port ones.\nconst sniffServicePort = async (timeoutMs) => {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const owned = await listOwnedListeningPorts(command.pid).catch(() => []);\n for (const port of owned.filter((p) => p > 0)) {\n if (await httpAnswers(port)) return port;\n }\n if (Date.now() > deadline) {\n throw new Error(\"no HTTP service found among the command's listening ports within \" + timeoutMs + \"ms\");\n }\n await new Promise((resolvePromise) => setTimeout(resolvePromise, 500));\n }\n};\n\n// Shell mode: start the local shell server first so windows can open\n// immediately; the monitor below sniffs services continuously.\nconst shellPort = hasShell ? await shellApi.listenShell() : null;\n\n// Plain mode needs one verified address before opening its single window;\n// shell mode defers entirely to the continuous monitor.\nconst servicePort = hasShell\n ? null\n : await sniffServicePort(30_000).catch(async (error) => {\n await logSink(\\`[create-opentray] \\${error.message}\\\\n\\`, \"utf8\");\n await killCommand();\n throw error;\n });\n\nconst trayIcon = ${JSON.stringify(config.trayIcon ?? null)};\nconst trayIconCandidates = trayIcon === null\n ? {}\n : trayIcon.template && process.platform === \"darwin\"\n ? { \"darwin-icon-only\": { type: \"file\", path: resolve(PROJECT_DIR, trayIcon.path), isTemplate: true } }\n : { \"icon-only\": { type: \"file\", path: resolve(PROJECT_DIR, trayIcon.path) } };\n\nconst tray = await createTray({\n id: config.appId,\n tooltip: { title: config.appName, description: \\`\\${config.appName} (OpenTray)\\` },\n icon: Object.keys(trayIconCandidates).length > 0\n ? trayIconCandidates\n : { \"text-only\": \"${config.appName.replace(/['\"\\\\]/gu, \"\").slice(0, 2) || \"A\"}\" },\n menu: { items: [\n { type: \"item\", id: 1, title: \\`Show \\${config.appName}\\`, primaryEvent: true },\n { type: \"separator\" },\n { type: \"item\", id: 2, title: \"Quit\" },\n ] },\n}, {\n appId: config.appId,\n appName: config.appName,\n ...(appIcon === undefined ? {} : { appIcon }),\n appLaunch: {\n // The generated entry may embed a native PTY (@lydell/node-pty), which\n // requires a Node host — cold launches must not inherit a Bun execPath.\n command: nodeRuntime(),\n args: [resolve(PROJECT_DIR, \"main.mjs\")],\n cwd: PROJECT_DIR,\n },\n});\n\nconst baseTitle = config.appName;\nconst showAddressBar = shellOptions !== null && shellOptions.showAddressBar === true;\n\n// Dedicated windows (round 9b): one terminal window when enabled, one window\n// per listened port — an address-bar wrapper page when enabled, the direct\n// service URL otherwise. No embedded tabs panel.\n// Each window gets its OWN extension mount (tray.extend per window): one\n// mount owns exactly one native webview slot, so dedicated windows never\n// collide on content.\nconst serviceWindows = new Map();\nlet terminalWindow = null;\n\nconst ensureServiceWindow = async (port) => {\n if (serviceWindows.has(port)) return;\n const direct = \\`http://127.0.0.1:\\${port}\\`;\n const url = showAddressBar && shellPort !== null\n ? \\`http://127.0.0.1:\\${shellPort}/browse.html?url=\\${encodeURIComponent(direct)}\\`\n : direct;\n const win = tray.extend(WebviewExt).createWebviewWindow({\n url,\n width: config.window.width,\n height: config.window.height,\n title: baseTitle,\n style: { appMode: true, autoHide: false, keepOnTop: false },\n ...(showAddressBar ? {} : { titleSync: { documentToWindow: true, windowToDocument: true } }),\n ...(showAddressBar ? {} : { iconSync: { faviconToWindow: true, windowToFavicon: true } }),\n });\n serviceWindows.set(port, { win, detached: false });\n await win.show().catch(() => {});\n};\n\nif (showTerminal && shellPort !== null) {\n terminalWindow = tray.extend(WebviewExt).createWebviewWindow({\n url: \\`http://127.0.0.1:\\${shellPort}/terminal.html\\`,\n width: 900,\n height: 560,\n title: \\`\\${baseTitle} — Terminal\\`,\n style: { appMode: true, autoHide: false, keepOnTop: false },\n });\n await terminalWindow.show().catch(() => {});\n}\n\n// Plain mode: the first verified port opens immediately through the SAME\n// per-port window machinery the monitor uses (no duplicate window object);\n// additional ports get their own windows as the monitor verifies them.\nif (!hasShell && servicePort !== null) {\n await ensureServiceWindow(servicePort);\n}\n\n{\n // Continuous owned-port monitor (BOTH modes): every HTTP-verified listening\n // port gets its own window, and a port that stops listening marks THAT\n // window's title (detached). Spec: one window per listened port, never\n // gated on shell mode.\n const seenPorts = new Set();\n const verifiedPorts = new Set();\n const monitor = setInterval(async () => {\n try {\n const owned = await listOwnedListeningPorts(command.pid).catch(() => []);\n const listening = owned.filter((port) => port > 0);\n // Spec: owned-listener scan PLUS HTTP verification before a port is\n // ever addressed — the monitor never adopts a non-HTTP listener.\n for (const port of listening) {\n if (!verifiedPorts.has(port) && (await httpAnswers(port))) {\n verifiedPorts.add(port);\n }\n }\n for (const port of verifiedPorts) seenPorts.add(port);\n const services = [...seenPorts].map((port) => ({\n port,\n detached: !listening.includes(port),\n }));\n shellApi?.setServices(services);\n for (const service of services) {\n if (!service.detached) await ensureServiceWindow(service.port);\n const entry = serviceWindows.get(service.port);\n if (entry !== undefined && Boolean(entry.detached) !== Boolean(service.detached)) {\n entry.detached = Boolean(service.detached);\n const title = service.detached ? \\`\\${baseTitle} (detached)\\` : baseTitle;\n await entry.win.setTitle(title).catch(() => {});\n }\n }\n } catch {\n /* monitor tick */\n }\n }, 1500);\n const stopMonitor = () => clearInterval(monitor);\n process.once(\"exit\", stopMonitor);\n}\n\nconst quit = async () => {\n for (const { win } of serviceWindows.values()) {\n try { await win.destroy(); } catch {}\n }\n serviceWindows.clear();\n if (terminalWindow !== null) {\n try { await terminalWindow.destroy(); } catch {}\n }\n await tray.destroy();\n await killCommand();\n process.exit(0);\n};\n\ntray.onMenuClick(({ itemId }) => {\n if (itemId === 1) {\n const target = terminalWindow !== null\n ? terminalWindow\n : serviceWindows.size > 0\n ? [...serviceWindows.values()][0].win\n : null;\n if (target === null) return;\n void target.isVisible().then(async (visible) => {\n if (visible) { await target.close(); } else { await target.toVisible(); }\n }).catch(() => {});\n return;\n }\n if (itemId === 2) void quit();\n});\n\nprocess.on(\"SIGINT\", () => void quit());\nprocess.on(\"SIGTERM\", () => void quit());\n\nconsole.log(\\`\\${READY_MARK_PREFIX} \\${JSON.stringify({ appId: config.appId, ...(servicePort === null ? {} : { port: servicePort }) })}\\`);\n\n`;\n","// Orthogonal intents (2026-08-16; owner round-9 demand: the generated app may\n// embed the SAME terminal/iframe tab experience the wizard has):\n// 1. Render the generated app's shell-server entry source from a frozen config.\n// 2. Serve prebuilt shell UI statically + SSE state (command, PTY ring, ports).\n// 3. Accept terminal input routed to the PTY registered by main.mjs.\n// 4. Report owned-port lifecycles so the shell can auto-open tabs and mark\n// detached services; main.mjs owns detection and calls setServices.\n// Kept physically separate from scaffold.ts: the template's nested JS template\n// literals are hostile to inline embedding.\n\n/** The generated app's local shell host: static UI + PTY stream + port state. */\nexport const shellServerSource = (options: {\n readonly commandDisplay: string;\n}): string => `#!/usr/bin/env node\n// Generated by create-opentray. Local shell host: static UI + PTY stream + port state.\nimport { createServer } from \"node:http\";\nimport { readFile } from \"node:fs/promises\";\nimport { extname, join, normalize, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname } from \"node:path\";\n\nconst PROJECT_DIR = dirname(fileURLToPath(import.meta.url));\nconst SHELL_DIR = resolve(PROJECT_DIR, \"app-shell\");\n\n// --- state shared with the shell UI ---\nconst state = {\n command: ${JSON.stringify(options.commandDisplay)},\n interactive: false, // main.mjs flips this when a PTY is attached\n output: [], // ring of PTY chunks\n services: [], // [{ port, detached }]\n listeners: new Set(), // SSE response streams\n};\nconst RING_LIMIT = 2000;\n\nconst emit = (event) => {\n const frame = \\`data: \\${JSON.stringify(event)}\\n\\n\\`;\n for (const res of state.listeners) {\n try {\n res.write(frame);\n } catch {\n /* dropped client */\n }\n }\n};\n\nconst snapshot = () => ({\n type: \"state\",\n command: state.command,\n interactive: state.interactive,\n services: state.services,\n output: state.output.slice(-400),\n});\n\n/** main.mjs registers the live PTY handle here. */\nexport const registerPty = (pty) => {\n state.pty = pty;\n state.interactive = pty !== null && pty !== undefined;\n};\n\n/** main.mjs forwards PTY output chunks here. */\nexport const pushOutput = (chunk) => {\n state.output.push(chunk);\n if (state.output.length > RING_LIMIT) {\n state.output.splice(0, state.output.length - RING_LIMIT);\n }\n emit({ type: \"log\", chunk });\n};\n\n/** main.mjs reports the owned-port snapshot; detached flags drive title marks. */\nexport const setServices = (services) => {\n const changed =\n services.length !== state.services.length ||\n services.some(\n (s, i) =>\n state.services[i] === undefined ||\n s.port !== state.services[i].port ||\n Boolean(s.detached) !== Boolean(state.services[i].detached),\n );\n state.services = services;\n if (changed) {\n emit({ type: \"services\", services });\n }\n};\n\nconst CT = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".wasm\": \"application/wasm\",\n \".json\": \"application/json\",\n};\n\nconst server = createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n if (url.pathname === \"/api/events\") {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-store\",\n connection: \"keep-alive\",\n });\n res.write(\\`data: \\${JSON.stringify(snapshot())}\\n\\n\\`);\n state.listeners.add(res);\n req.on(\"close\", () => {\n state.listeners.delete(res);\n });\n return;\n }\n if (url.pathname === \"/api/terminal-input\" && req.method === \"POST\") {\n let body = \"\";\n for await (const chunk of req) {\n body += chunk;\n }\n try {\n const { data } = JSON.parse(body);\n if (state.pty !== null && state.pty !== undefined && typeof data === \"string\") {\n state.pty.write(data);\n }\n } catch {\n /* malformed body */\n }\n res.writeHead(200, { \"content-type\": \"application/json\" }).end('{\"ok\":true}');\n return;\n }\n // Static shell files with traversal containment; SPA fallback for routes.\n const relative = normalize(url.pathname).replace(/\\\\/(\\\\.\\\\.\\\\/?)*/, \"\");\n const target = resolve(\n SHELL_DIR,\n relative === \"\" || relative === \"/\" ? \"index.html\" : relative,\n );\n if (target !== SHELL_DIR && !target.startsWith(SHELL_DIR + sep)) {\n res.writeHead(404).end();\n return;\n }\n const bytes = await readFile(target).catch(() => undefined);\n if (bytes === undefined) {\n const index = await readFile(join(SHELL_DIR, \"index.html\")).catch(() => undefined);\n if (index === undefined) {\n res.writeHead(404).end();\n return;\n }\n res\n .writeHead(200, { \"content-type\": CT[\".html\"], \"cache-control\": \"no-store\" })\n .end(index);\n return;\n }\n res\n .writeHead(200, {\n \"content-type\": CT[extname(target).toLowerCase()] ?? \"application/octet-stream\",\n \"cache-control\": \"no-store\",\n })\n .end(bytes);\n});\n\n/** Start listening on an ephemeral loopback port; resolves the port. */\nexport const listenShell = () =>\n new Promise((resolvePort) => {\n server.listen(0, \"127.0.0.1\", () => {\n resolvePort(server.address().port);\n });\n });\n`;\n","// Orthogonal intents (maintained 2026-07-22; original user request: create a\n// locally hosted app from the frozen wizard form):\n// 1. Write a self-contained consumer project that depends only on published packages.\n// 2. Persist the frozen identity, command vector, and service port as app config.\n// 3. Generate an entry that supervises the command and owns tray+window lifetime.\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport type { LaunchVector } from \"./launch-vector\";\nimport { createEntrySource } from \"./entry-template\";\nimport { shellServerSource } from \"./shell-server-template\";\n\nexport { createEntrySource };\nimport { toProjectDirectoryName } from \"./app-id\";\n\nexport interface ScaffoldAppConfig {\n readonly schemaVersion: 1;\n readonly appId: string;\n readonly appName: string;\n readonly command: LaunchVector;\n readonly service: { readonly port: number };\n readonly window: { readonly width: number; readonly height: number };\n /** Tray icon asset (written by materialize); omitted → text-only tray. */\n readonly trayIcon?: { readonly path: string; readonly template: boolean };\n /** Generated-app shell (startup terminal / address-bar tabs). */\n readonly shell?: {\n readonly showTerminal: boolean;\n readonly showAddressBar: boolean;\n };\n}\n\nexport interface ScaffoldOptions {\n readonly config: ScaffoldAppConfig;\n readonly targetDir: string;\n /** opentray/@opentray/ext-webview version range written into package.json. */\n readonly dependencyRange: string;\n /** Whether install will be skipped; only affects README guidance text. */\n readonly skipInstall?: boolean;\n /** Directory holding the prebuilt shell UI (copied to app-shell/ when the\n * shell is enabled). */\n readonly shellAssetsDir?: string;\n}\n\nexport interface ScaffoldResult {\n readonly projectDir: string;\n readonly entryPath: string;\n readonly configPath: string;\n readonly appIconDir: string;\n readonly writtenFiles: readonly string[];\n}\n\n/** Filenames that identify a directory as a create-opentray project. */\nexport const SCAFFOLD_MARKER_FILES = [\n \"opentray.app.json\",\n \"main.mjs\",\n] as const;\n\nexport const writeScaffold = async (options: ScaffoldOptions): Promise<ScaffoldResult> => {\n const projectDir = resolve(options.targetDir);\n await mkdir(join(projectDir, \"app-icon\"), { recursive: true });\n\n const writtenFiles: string[] = [];\n const write = async (relative: string, content: string): Promise<void> => {\n const path = join(projectDir, relative);\n await mkdir(join(path, \"..\"), { recursive: true });\n await writeFile(path, content, \"utf8\");\n writtenFiles.push(relative);\n };\n\n await write(\"package.json\", createPackageJson(options));\n await write(\"opentray.app.json\", `${JSON.stringify(options.config, null, 2)}\\n`);\n await write(\"main.mjs\", createEntrySource(options.config));\n const shell = options.config.shell;\n if (shell !== undefined && (shell.showTerminal || shell.showAddressBar)) {\n await write(\"app-shell-server.mjs\", createShellServerSource(options.config));\n if (options.shellAssetsDir !== undefined) {\n const { cp } = await import(\"node:fs/promises\");\n await cp(options.shellAssetsDir, join(projectDir, \"app-shell\"), { recursive: true });\n writtenFiles.push(\"app-shell/\");\n }\n }\n await write(\"README.md\", createReadme(options));\n await write(\".gitignore\", [\"node_modules/\\n\", \"app.log\\n\", \"dist/\\n\"].join(\"\"));\n\n return {\n projectDir,\n entryPath: join(projectDir, \"main.mjs\"),\n configPath: join(projectDir, \"opentray.app.json\"),\n appIconDir: join(projectDir, \"app-icon\"),\n writtenFiles,\n };\n};\n\nconst createPackageJson = (options: ScaffoldOptions): string => {\n const shell = options.config.shell;\n const dependencies: Record<string, string> = {\n opentray: options.dependencyRange,\n \"@opentray/ext-webview\": options.dependencyRange,\n };\n if (shell !== undefined && shell.showTerminal) {\n // The startup terminal streams the command through a native PTY.\n dependencies[\"@lydell/node-pty\"] = \"^1.1.0\";\n }\n return `${JSON.stringify(\n {\n name: toProjectDirectoryName(options.config.appId),\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n description: `${options.config.appName} — OpenTray-hosted app generated by create-opentray`,\n scripts: {\n start: \"node main.mjs\",\n },\n dependencies,\n },\n null,\n 2,\n )}\\n`;\n};\n\nconst createReadme = (options: ScaffoldOptions): string => `# ${options.config.appName}\n\nGenerated by \\`create-opentray\\`. This app supervises the recorded start\ncommand, hosts it in an OpenTray tray + application window, and can be pinned\nto the taskbar (Windows) or Dock (macOS).\n\n## Run\n\n${options.skipInstall === true ? \"Install dependencies first, then:\" : \"\"}\n\\`\\`\\`bash\nnpm run start\n\\`\\`\\`\n\n- Service: ${options.config.service.port > 0 ? `http://127.0.0.1:${options.config.service.port} (preview hint; re-sniffed at runtime)` : \"sniffed at runtime from the command's owned listening ports\"}\n- Command: \\`${options.config.command.command} ${options.config.command.args.join(\" \")}\\`\n- Logs: \\`app.log\\`\n\n## Files\n\n- \\`opentray.app.json\\` — frozen identity and launch vector\n- \\`app-icon/\\` — generated platform icon catalog (ICNS/ICO/PNG)\n- \\`main.mjs\\` — app entry: supervises the command and owns the tray session\n`;\n\n/** Shell server entry source: static UI + PTY stream + port state (round 9). */\nexport const createShellServerSource = (config: ScaffoldAppConfig): string =>\n shellServerSource({\n commandDisplay: `${config.command.command} ${config.command.args.join(\" \")}`.trim(),\n });\n","// Orthogonal intents (maintained 2026-07-22; original user request: after the\n// frozen dialog confirms generation, run a pending pipeline with live logs and\n// end in a success state that can open the app):\n// 1. Guard the target directory before writing anything.\n// 2. Generate the strict AppIcon catalog through the sanctioned vite-plugin generator.\n// 3. Install, first-launch with an absolute runtime vector, and gate on the\n// generated entry's ready marker plus the stable Darwin bundle.\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { mkdir, readFile, readdir, rm, stat, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport {\n resolveDefaultDarwinAppBundlePath,\n sanitizeAppBundleName,\n} from \"@opentray/packaging\";\nimport { generateOpenTrayAppIcon } from \"@opentray/vite-plugin\";\n\nimport { tcpProbe } from \"./port-scan\";\nimport { fileURLToPath } from \"node:url\";\nimport { access } from \"node:fs/promises\";\n\nconst moduleDirectory = dirname(fileURLToPath(import.meta.url));\n\n/** Prebuilt shell UI: prefer the packaged copy (dist/shell), else the\n * workspace build next to this package. */\nconst resolveShellAssetsDir = async (): Promise<string | undefined> => {\n const candidates = [\n join(moduleDirectory, \"shell\"),\n // Source checkout: moduleDirectory is packages/create/src → ../dist/shell.\n join(moduleDirectory, \"..\", \"dist\", \"shell\"),\n // tsdown chunk layout: moduleDirectory is packages/create/dist → ./shell\n // (covered above) — plus a direct workspace fallback.\n join(moduleDirectory, \"..\", \"create-webui\", \"dist\"),\n ];\n for (const candidate of candidates) {\n if (await access(join(candidate, \"index.html\")).then(() => true, () => false)) {\n return candidate;\n }\n }\n return undefined;\n};\nconst shellAssetsDir = await resolveShellAssetsDir();\nimport { composeAppIcon } from \"./icon-compose\";\nimport type { IconBackground } from \"./icon-compose\";\nimport { writeGlyphIconTemp } from \"./scrape\";\n\nconst errorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\nimport { toProjectDirectoryName } from \"./app-id\";\nimport { writeScaffold, type ScaffoldAppConfig, type ScaffoldResult } from \"./scaffold\";\n\nexport type MaterializeLogEvent =\n | { readonly type: \"step\"; readonly step: string; readonly message: string }\n | { readonly type: \"log\"; readonly message: string };\n\nexport interface MaterializeInput {\n readonly config: ScaffoldAppConfig;\n readonly targetDir: string;\n readonly dependencyRange: string;\n readonly iconSourcePath: string | undefined;\n /** True when the tray source is a solid silhouette (darwin template). */\n readonly trayIconIsSolid?: boolean;\n /** Icon composition (owner round-12): background + foreground scale. */\n readonly iconBackground?: IconBackground;\n readonly iconScale?: number;\n /** Tray icon source; defaults to the app icon source when omitted. */\n readonly trayIconSourcePath?: string;\n /** Generated-app shell options (startup terminal / address bar). */\n readonly shell?: { showTerminal: boolean; showAddressBar: boolean };\n readonly packageManager: \"npm\" | \"pnpm\" | \"bun\";\n readonly skipInstall: boolean;\n readonly force: boolean;\n}\n\nexport interface MaterializeResult {\n readonly scaffold: ScaffoldResult;\n readonly projectDir: string;\n readonly bundlePath: string | undefined;\n /** Absent under --skip-install (no node_modules → no first launch). */\n readonly firstLaunch?: { readonly pid: number };\n}\n\nexport interface MaterializeContext {\n readonly log: (event: MaterializeLogEvent) => void;\n readonly generateIcon?: typeof generateOpenTrayAppIcon;\n readonly runInstall?: (options: RunInstallOptions) => Promise<void>;\n readonly firstLaunchEntry?: (projectDir: string) => Promise<FirstLaunchHandle> | FirstLaunchHandle;\n readonly platform?: NodeJS.Platform;\n readonly waitMs?: (ms: number) => Promise<void>;\n readonly bundleTimeoutMs?: number;\n}\n\nexport interface RunInstallOptions {\n readonly projectDir: string;\n readonly packageManager: \"npm\" | \"pnpm\" | \"bun\";\n readonly log: (message: string) => void;\n}\n\nexport interface FirstLaunchHandle {\n readonly pid: number;\n /** Resolves when the entry prints its ready marker; rejects on early exit. */\n readonly ready: Promise<void>;\n}\n\nexport const READY_MARKER_PREFIX = \"opentray: ready\";\n\n/** True when the directory exists and contains anything beyond ignorable files. */\nexport const isDirectoryOccupied = async (dir: string): Promise<boolean> => {\n let entries: readonly string[];\n try {\n entries = await readdir(dir);\n } catch {\n return false;\n }\n const ignorable = new Set([\".DS_Store\", \"Thumbs.db\"]);\n return entries.some((entry) => !ignorable.has(entry));\n};\n\n/** Detect package manager from lockfiles then npm_config_user_agent. */\nexport const detectPackageManager = (\n files: readonly string[],\n userAgent: string | undefined,\n): \"npm\" | \"pnpm\" | \"bun\" => {\n if (files.includes(\"pnpm-lock.yaml\")) return \"pnpm\";\n if (files.includes(\"bun.lockb\") || files.includes(\"bun.lock\")) return \"bun\";\n if (files.includes(\"package-lock.json\")) return \"npm\";\n const agent = (userAgent ?? \"\").toLowerCase();\n if (agent.includes(\"pnpm\")) return \"pnpm\";\n if (agent.includes(\"bun\")) return \"bun\";\n return \"npm\";\n};\n\n/** Expected stable Darwin bundle path for the generated project's identity. */\nexport const expectedDarwinBundlePath = (config: {\n appName: string;\n appId: string;\n}): string =>\n resolveDefaultDarwinAppBundlePath({\n homeDir: homedir(),\n packageName: toProjectDirectoryName(config.appId),\n appName: sanitizeAppBundleName(config.appName),\n });\n\nexport const materialize = async (\n input: MaterializeInput,\n context: MaterializeContext,\n): Promise<MaterializeResult> => {\n const step = (name: string, message: string): void =>\n context.log({ type: \"step\", step: name, message });\n const waitMs =\n context.waitMs ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n\n const targetDir = resolve(input.targetDir);\n step(\"scaffold\", `checking target directory ${targetDir}`);\n if (await isDirectoryOccupied(targetDir)) {\n if (input.force !== true) {\n throw new Error(\n `target directory is not empty: ${targetDir} (pass --force or choose another directory)`,\n );\n }\n // Force is a TRUE overwrite: the generated project is fully wizard-owned\n // and regenerable, so clear the stale tree instead of layering over it.\n step(\"scaffold\", \"force: clearing existing target directory\");\n await rm(targetDir, { recursive: true, force: true });\n }\n\n // Tray icon FIRST (owner law: the tray defaults to the app icon): the PNG\n // is written into the target's app-icon dir (created here) so the value is\n // part of the config the entry template renders with — main.mjs previously\n // baked a stale null because the config was amended only AFTER scaffolding.\n const appIconDir = join(targetDir, \"app-icon\");\n await mkdir(appIconDir, { recursive: true });\n const traySource = input.trayIconSourcePath ?? input.iconSourcePath;\n let trayIconConfig: { path: string; template: boolean } | undefined;\n if (traySource !== undefined) {\n const trayPath = join(appIconDir, \"tray-icon.png\");\n try {\n const sharpModule = await import(\"sharp\");\n await sharpModule.default(traySource, { failOn: \"none\" })\n .resize(128, 128, { fit: \"contain\", background: { r: 0, g: 0, b: 0, alpha: 0 } })\n .png()\n .toFile(trayPath);\n // Solid-silhouette sources are single-color art: darwin templates let\n // macOS tint them for light/dark menu bars.\n const template = input.trayIconIsSolid === true;\n trayIconConfig = { path: \"app-icon/tray-icon.png\", template };\n context.log({\n type: \"log\",\n message: `tray icon: app-icon/tray-icon.png${template ? \" (template)\" : \"\"}`,\n });\n } catch (error) {\n context.log({\n type: \"log\",\n message: `tray icon unavailable (${errorMessage(error)}); falling back to text tray`,\n });\n }\n }\n\n // Owner round-12: compose the app icon (foreground over the chosen\n // background) BEFORE the catalog so ICNS encodes from the best-practice\n // 824-in-1024 variant while Windows/Linux use the full 1024 composite.\n let composedIcon:\n | { compositePath: string; macOSPath: string; background: IconBackground }\n | undefined;\n if (input.iconSourcePath !== undefined) {\n try {\n composedIcon = await composeAppIcon({\n foregroundPath: input.iconSourcePath,\n background: input.iconBackground ?? \"transparent\",\n scale: input.iconScale ?? 0.8,\n outputDir: join(targetDir, \"app-icon\"),\n });\n context.log({\n type: \"log\",\n message: `composed app icon (${composedIcon.background} background, macOS 824 / windows 1024)`,\n });\n } catch (error) {\n context.log({\n type: \"log\",\n message: `icon composition unavailable (${errorMessage(error)}); using source directly`,\n });\n }\n }\n\n step(\"scaffold\", \"writing project files\");\n const shell = input.shell;\n const scaffold = await writeScaffold({\n config: {\n ...input.config,\n ...(shell === undefined ? {} : { shell }),\n ...(trayIconConfig === undefined ? {} : { trayIcon: trayIconConfig }),\n },\n targetDir,\n dependencyRange: input.dependencyRange,\n skipInstall: input.skipInstall,\n ...(shellAssetsDir === undefined ? {} : { shellAssetsDir }),\n });\n context.log({ type: \"log\", message: `wrote ${scaffold.writtenFiles.join(\", \")}` });\n\n step(\"icon\", \"generating platform icon catalog\");\n const iconSource =\n input.iconSourcePath ??\n (await writeGlyphIconTemp(input.config.appName, scaffold.appIconDir));\n const generate = context.generateIcon ?? generateOpenTrayAppIcon;\n const generateIntoScaffold = async (sourcePath: string) =>\n generate({\n sourcePath,\n // Composed art carries its own background + squircle mask: pass\n // through instead of glyph re-tiling, and give macOS its 824 variant.\n ...(composedIcon === undefined\n ? {}\n : {\n composed: true,\n macosSourcePath: composedIcon.macOSPath,\n }),\n icnsOutputPath: join(scaffold.appIconDir, \"app-icon.icns\"),\n icoOutputPath: join(scaffold.appIconDir, \"app-icon.ico\"),\n linuxOutputDirectory: join(scaffold.appIconDir, \"linux\"),\n manifestOutputPath: join(scaffold.appIconDir, \"app-icon.json\"),\n outputPath: join(scaffold.appIconDir, \"app-icon.png\"),\n cachePath: join(scaffold.appIconDir, \".cache.json\"),\n });\n\n const catalogSource =\n composedIcon !== undefined && composedIcon.compositePath !== undefined\n ? composedIcon.compositePath\n : input.iconSourcePath;\n let iconMetadata;\n try {\n iconMetadata = await generateIntoScaffold(\n catalogSource !== undefined ? catalogSource : iconSource,\n );\n } catch (error) {\n // BOTH routes degrade to the glyph: an undecodable raw source AND an\n // encoder-side failure of a composed source must never fail the whole\n // materialization (the review flagged the asymmetric rethrow).\n context.log({\n type: \"log\",\n message: `icon source unusable (${errorMessage(error)}); falling back to glyph icon`,\n });\n iconMetadata = await generateIntoScaffold(\n await writeGlyphIconTemp(input.config.appName, scaffold.appIconDir),\n );\n }\n context.log({\n type: \"log\",\n message: `icon assets: icns + ico + ${iconMetadata.linuxPngOutputPaths.length} linux pngs`,\n });\n\n if (!input.skipInstall) {\n step(\"install\", `installing dependencies with ${input.packageManager}`);\n const runInstall = context.runInstall ?? runPackageManagerInstall;\n await runInstall({\n projectDir: scaffold.projectDir,\n packageManager: input.packageManager,\n log: (message) => context.log({ type: \"log\", message }),\n });\n } else {\n step(\"install\", \"skipping dependency install (--skip-install)\");\n }\n\n if (input.skipInstall) {\n // No node_modules → the entry's `import \"opentray\"` cannot resolve, so a\n // first launch would only fail the whole creation. The project is\n // complete; launching is the user's step after installing.\n step(\"launch\", \"skipping first launch (--skip-install)\");\n context.log({\n type: \"log\",\n message: \"install dependencies and run `node main.mjs` to launch\",\n });\n return { scaffold, projectDir: scaffold.projectDir, bundlePath: undefined };\n }\n\n step(\"launch\", \"first launch of the generated app\");\n const launch = context.firstLaunchEntry ?? firstLaunchEntry;\n const launched = await launch(scaffold.projectDir);\n context.log({ type: \"log\", message: `app entry spawned (pid ${launched.pid})` });\n\n step(\"launch\", \"waiting for app ready marker\");\n await launched.ready;\n context.log({ type: \"log\", message: `${READY_MARKER_PREFIX} received` });\n\n const platform = context.platform ?? process.platform;\n let bundlePath: string | undefined;\n if (platform === \"darwin\") {\n step(\"bundle\", \"verifying stable Darwin app bundle\");\n const expected = expectedDarwinBundlePath(input.config);\n bundlePath = await waitForDirectory(\n expected,\n context.bundleTimeoutMs ?? 60_000,\n waitMs,\n );\n context.log({ type: \"log\", message: `stable bundle: ${bundlePath}` });\n }\n\n return { scaffold, projectDir: scaffold.projectDir, bundlePath, firstLaunch: launched };\n};\n\nexport const runPackageManagerInstall = async (options: RunInstallOptions): Promise<void> => {\n const commands: Record<\"npm\" | \"pnpm\" | \"bun\", { cmd: string; args: readonly string[] }> = {\n npm: { cmd: \"npm\", args: [\"install\", \"--no-fund\", \"--no-audit\"] },\n pnpm: { cmd: \"pnpm\", args: [\"install\"] },\n bun: { cmd: \"bun\", args: [\"install\"] },\n };\n const { cmd, args } = commands[options.packageManager];\n await new Promise<void>((resolvePromise, rejectPromise) => {\n const child = spawn(cmd, [...args], {\n cwd: options.projectDir,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n windowsHide: true,\n });\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n for (const line of chunk.split(\"\\n\").filter(Boolean)) {\n options.log(line);\n }\n });\n child.stderr?.on(\"data\", (chunk: string) => {\n for (const line of chunk.split(\"\\n\").filter(Boolean)) {\n options.log(line);\n }\n });\n child.once(\"error\", rejectPromise);\n child.once(\"exit\", (code) => {\n if (code === 0) {\n resolvePromise();\n return;\n }\n rejectPromise(new Error(`${cmd} ${args.join(\" \")} exited with ${code ?? \"signal\"}`));\n });\n });\n};\n\n/**\n * Spawn the generated entry detached with piped stdout, resolving when the\n * entry prints its ready marker. The child is unref'd so the wizard can exit\n * without taking the generated app down.\n */\n/**\n * The generated app may embed a native PTY (@lydell/node-pty), which requires\n * a Node host (Bun loads it but never delivers output). Always launch the\n * generated entry with Node: prefer the Node currently executing the wizard,\n * else resolve `node` from PATH.\n */\nconst nodeExecutable = (): string =>\n process.versions.bun === undefined && process.execPath.includes(\"node\")\n ? process.execPath\n : \"node\";\n\nexport const firstLaunchEntry = async (projectDir: string): Promise<FirstLaunchHandle> => {\n const child: ChildProcess = spawn(nodeExecutable(), [join(projectDir, \"main.mjs\")], {\n cwd: projectDir,\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n detached: true,\n windowsHide: true,\n });\n const pid = child.pid;\n if (pid === undefined) {\n throw new Error(`failed to spawn generated app entry in ${projectDir}`);\n }\n child.unref();\n\n let buffer = \"\";\n const ready = new Promise<void>((resolvePromise, rejectPromise) => {\n const finish = (error: Error | undefined): void => {\n child.stdout?.removeListener(\"data\", onData);\n if (error !== undefined) {\n rejectPromise(error);\n return;\n }\n resolvePromise();\n };\n const onData = (chunk: string): void => {\n buffer += chunk;\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) {\n return;\n }\n const line = buffer.slice(0, newline);\n buffer = buffer.slice(newline + 1);\n if (line.startsWith(READY_MARKER_PREFIX)) {\n finish(undefined);\n }\n };\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", onData);\n child.once(\"error\", (error) => finish(error));\n child.once(\"exit\", (code) => {\n finish(new Error(`generated app entry exited early with ${code ?? \"signal\"}`));\n });\n });\n\n return { pid, ready };\n};\n\nconst waitForDirectory = async (\n path: string,\n timeoutMs: number,\n waitMs: (ms: number) => Promise<void>,\n): Promise<string> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n const info = await stat(path);\n if (info.isDirectory()) {\n return path;\n }\n } catch {\n // Not materialized yet.\n }\n await waitMs(500);\n }\n throw new Error(`stable Darwin app bundle did not appear within ${timeoutMs}ms: ${path}`);\n};\n\n/** TCP readiness helper reused by tests for the supervised service port. */\nexport const waitForServicePort = async (\n port: number,\n timeoutMs: number,\n probe: (port: number) => Promise<boolean> = (value) => tcpProbe(\"127.0.0.1\", value),\n): Promise<boolean> => {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await probe(port)) {\n return true;\n }\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n return false;\n};\n","// Orthogonal intents (maintained 2026-07-22; original user request: the\n// generated app must relaunch from Finder/Dock without the terminal's PATH;\n// governed by the App Launch Law):\n// 1. Resolve the user command into a PATH-independent absolute vector.\n// 2. Unwrap `#!/usr/bin/env node` style scripts onto their interpreter.\n// 3. Keep environment maps out of the persisted descriptor.\n\nimport { constants } from \"node:fs\";\nimport { access, readFile } from \"node:fs/promises\";\nimport { delimiter, isAbsolute, resolve } from \"node:path\";\n\nexport interface LaunchVector {\n readonly command: string;\n readonly args: readonly string[];\n readonly cwd: string;\n /** Optional explicit env overlay merged over the runtime environment. */\n readonly env?: Readonly<Record<string, string>>;\n}\n\nexport interface ResolveLaunchVectorOptions {\n readonly tokens: readonly string[];\n readonly cwd: string;\n readonly platform?: NodeJS.Platform;\n readonly pathEnv?: string;\n readonly accessFile?: (path: string) => Promise<void>;\n readonly firstLine?: (path: string) => Promise<string | undefined>;\n}\n\nconst isExecutable = async (path: string): Promise<boolean> => {\n try {\n await access(path, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n};\n\nconst readFirstLine = async (path: string): Promise<string | undefined> => {\n try {\n const content = await readFile(path, \"utf8\");\n return content.split(\"\\n\", 1)[0];\n } catch {\n return undefined;\n }\n};\n\n/** Resolve a bare command name against PATH; returns undefined when absent. */\nexport const resolveOnPath = async (\n command: string,\n options: Pick<ResolveLaunchVectorOptions, \"platform\" | \"pathEnv\" | \"accessFile\">,\n): Promise<string | undefined> => {\n if (isAbsolute(command) || command.includes(\"/\") || command.includes(\"\\\\\")) {\n return undefined;\n }\n const platform = options.platform ?? process.platform;\n const pathEnv =\n options.pathEnv ??\n (platform === \"win32\"\n ? `${process.env.PATH ?? \"\"}${delimiter}${process.cwd()}`\n : process.env.PATH ?? \"\");\n const extensions =\n platform === \"win32\" ? [\"\", \".cmd\", \".exe\", \".bat\"] : [\"\"];\n for (const dir of pathEnv.split(delimiter).filter(Boolean)) {\n for (const ext of extensions) {\n const candidate = resolve(dir, `${command}${ext}`);\n const exists = options.accessFile\n ? await options.accessFile(candidate).then(\n () => true,\n () => false,\n )\n : await isExecutable(candidate);\n if (exists) {\n return candidate;\n }\n }\n }\n return undefined;\n};\n\n/** Read a `#!/usr/bin/env <interpreter>` shebang; undefined for other files. */\nexport const parseShebangInterpreter = (\n firstLine: string | undefined,\n): { interpreter: string; args: readonly string[] } | undefined => {\n if (firstLine === undefined || !firstLine.startsWith(\"#!\")) {\n return undefined;\n }\n const tokens = firstLine.slice(2).trim().split(/\\s+/u).filter(Boolean);\n const [interpreter, ...interpreterArgs] = tokens;\n if (interpreter === undefined) {\n return undefined;\n }\n return { interpreter, args: interpreterArgs };\n};\n\n/**\n * Resolve the user's command tokens to a PATH-independent vector:\n * - absolute-ize bare executables through PATH lookup;\n * - resolve relative script paths against cwd;\n * - when the executable is an `env <interpreter>` shebang script, run the\n * interpreter directly with the script as its first argument.\n * The persisted descriptor never includes an environment map.\n */\nexport const resolveLaunchVector = async (\n options: ResolveLaunchVectorOptions,\n): Promise<LaunchVector> => {\n const [rawCommand, ...restArgs] = options.tokens;\n if (rawCommand === undefined || rawCommand.trim().length === 0) {\n throw new Error(\"launch vector requires a command\");\n }\n const accessFile = options.accessFile ?? (async (path: string) => access(path));\n const firstLine = options.firstLine ?? readFirstLine;\n\n let command = rawCommand;\n if (!isAbsolute(command)) {\n const onPath = await resolveOnPath(command, options);\n if (onPath !== undefined) {\n command = onPath;\n } else if (command.includes(\"/\") || command.includes(\"\\\\\")) {\n command = resolve(options.cwd, command);\n }\n }\n\n // Windows .cmd shims are not directly spawnable without a shell; route them\n // through cmd.exe explicitly so the vector stays shell-string-free. The\n // original user-typed path is preserved verbatim for cmd to resolve.\n const platform = options.platform ?? process.platform;\n if (platform === \"win32\" && /\\.cmd$/iu.test(rawCommand)) {\n return {\n command: resolveSystemPath(\"cmd.exe\"),\n args: [\"/d\", \"/s\", \"/c\", rawCommand, ...restArgs],\n cwd: options.cwd,\n };\n }\n\n const shebang = parseShebangInterpreter(await firstLine(command).catch(() => undefined));\n if (shebang !== undefined && !command.endsWith(\".exe\")) {\n let interpreter = shebang.interpreter;\n if (interpreter === \"/usr/bin/env\" || interpreter === \"env\") {\n const [envTarget, ...envArgs] = shebang.args;\n if (envTarget !== undefined) {\n const resolved = (await resolveOnPath(envTarget, options)) ?? envTarget;\n return {\n command: resolved,\n args: [...envArgs, command, ...restArgs],\n cwd: options.cwd,\n };\n }\n interpreter = \"/usr/bin/env\";\n }\n return {\n command: interpreter,\n args: [...shebang.args, command, ...restArgs],\n cwd: options.cwd,\n };\n }\n\n return { command, args: restArgs, cwd: options.cwd };\n};\n\nconst resolveSystemPath = (name: string): string => {\n if (isAbsolute(name)) {\n return name;\n }\n const systemRoot = process.env.SystemRoot ?? \"C:\\\\Windows\";\n return `${systemRoot}\\\\System32\\\\${name}`;\n};\n","// Orthogonal intents (maintained 2026-07-22; original user request: the Success\n// dialog offers an open-app action and a taskbar/Dock pinning hint):\n// 1. Open the materialized app per platform: macOS via the stable .app bundle,\n// Windows/Linux via a detached absolute-runtime launch of the generated entry.\n// 2. Keep the hint platform-truthful: no Windows shortcut persistence claims.\n\nimport { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\n\nexport interface OpenAppInput {\n readonly projectDir: string;\n readonly bundlePath: string | undefined;\n readonly platform?: NodeJS.Platform | undefined;\n}\n\nexport interface OpenAppResult {\n readonly ok: boolean;\n readonly detail: string;\n}\n\nexport const openMaterializedApp = async (input: OpenAppInput): Promise<OpenAppResult> => {\n const platform = input.platform ?? process.platform;\n if (platform === \"darwin\") {\n if (input.bundlePath === undefined) {\n return { ok: false, detail: \"stable Darwin app bundle path is unknown\" };\n }\n const child = spawn(\"open\", [input.bundlePath], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n const status = await new Promise<number | null>((resolve) => {\n child.once(\"error\", () => resolve(null));\n child.once(\"exit\", (code) => resolve(code));\n });\n if (status === 0) {\n return { ok: true, detail: `opened ${input.bundlePath}` };\n }\n return {\n ok: false,\n detail: `open ${input.bundlePath} failed with ${status ?? \"spawn error\"}`,\n };\n }\n const child = spawn(process.execPath, [join(input.projectDir, \"main.mjs\")], {\n cwd: input.projectDir,\n stdio: \"ignore\",\n detached: true,\n windowsHide: true,\n });\n // A detached launcher must not surface async spawn errors on the wizard.\n child.once(\"error\", () => {});\n child.unref();\n if (child.pid === undefined) {\n return { ok: false, detail: `failed to spawn ${input.projectDir}/main.mjs` };\n }\n return { ok: true, detail: `launched app entry (pid ${child.pid})` };\n};\n\n/** Platform-truthful pinning hint; makes no Windows persistence claim. */\nexport const pinningHint = (platform: NodeJS.Platform = process.platform): string => {\n if (platform === \"darwin\") {\n return \"右键点击 Dock 中的应用图标,选择“选项 → 在程序坞中保留”,即可固定到 Dock。\";\n }\n if (platform === \"win32\") {\n return \"右键点击任务栏中的应用图标,选择“固定到任务栏”即可固定。(OpenTray 尚未生成开始菜单快捷方式)\";\n }\n return \"可将应用窗口固定到任务栏/收藏夹;Linux 桌面快捷方式生成尚未提供。\";\n};\n","// Orthogonal intents (maintained 2026-07-22; original user request: the WebUI\n// wizard collects OpenTray's required parameters while scraping only assists):\n// 1. Own the wizard state machine: idle → running → discovered → frozen →\n// materializing → success/failed.\n// 2. Keep scrapes from overwriting user-edited fields, and freeze the form at\n// confirmation so no later poll can mutate it.\n// 3. Coordinate preview run, discovery polling, scrape polling, and teardown.\n\nimport { createHash } from \"node:crypto\";\nimport { mkdtemp, stat, writeFile } from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nimport { deriveDefaultAppId, deriveDefaultAppName, toProjectDirectoryName } from \"./app-id\";\nimport {\n startCommandRun,\n type CommandRun,\n type CommandRunEvent,\n} from \"./command-run\";\nimport {\n collectProcessTreePids,\n createPortDiscovery,\n listListeningPorts,\n type DiscoveredService,\n} from \"./port-scan\";\nimport {\n autoBackground,\n compositionCacheKey,\n composeAppIcon,\n foregroundStats,\n} from \"./icon-compose\";\nimport { scrapeService, writeGlyphIconTemp, type ScrapedIcon } from \"./scrape\";\nimport { tokenizeCommandLine } from \"./tokenize\";\nimport {\n detectPackageManager,\n materialize,\n type MaterializeContext,\n type MaterializeResult,\n} from \"./materialize\";\nimport type { LaunchVector } from \"./launch-vector\";\nimport { resolveLaunchVector } from \"./launch-vector\";\nimport { pinningHint } from \"./open-app\";\n\nexport interface WizardEnvEntry {\n readonly key: string;\n readonly value: string;\n}\n\n/** App-icon composition (owner round-12): foreground over black/white/\n * transparent background; the wizard derives the auto suggestion. */\nexport type WizardIconBackground = \"black\" | \"white\" | \"transparent\";\n\nexport const DEFAULT_ICON_SCALE = 0.8;\n\nexport interface WizardIconComposition {\n readonly key: string;\n readonly compositePath: string;\n readonly macOSPath: string;\n readonly background: WizardIconBackground;\n}\n\nexport interface WizardIconAnalysis {\n readonly luminance: number | undefined;\n readonly coverage: number;\n readonly suggested: WizardIconBackground;\n}\n\n/** Command execution options (advanced): working directory, custom env, and\n * the input mode — array mode takes argv elements verbatim (no string\n * splitting), string mode tokenizes one command line. */\nexport interface WizardCommandOptions {\n /** Empty = the wizard's working directory. */\n readonly cwd: string;\n readonly env: readonly WizardEnvEntry[];\n readonly argsMode: \"string\" | \"array\";\n}\n\nexport const DEFAULT_COMMAND_OPTIONS: WizardCommandOptions = {\n cwd: \"\",\n env: [],\n argsMode: \"string\",\n};\n\nexport type WizardState =\n | \"idle\"\n | \"running\"\n | \"discovered\"\n | \"failed\"\n | \"frozen\"\n | \"materializing\"\n | \"success\";\n\nexport interface WizardFormValues {\n readonly appId: string;\n readonly appName: string;\n readonly iconPath: string;\n /** Icon composition (owner round-12). */\n readonly iconBackground: WizardIconBackground;\n readonly iconScale: number;\n /** Empty = follow the app icon choice (default). */\n readonly trayIconPath: string;\n readonly pm: \"npm\" | \"pnpm\" | \"bun\";\n /** Wipe an existing non-empty target directory before materializing. */\n readonly force: boolean;\n /** Advanced: render the command PTY in the generated app (default false). */\n readonly showStartupTerminal: boolean;\n /** Advanced: address-bar service tabs in the generated app (default false). */\n readonly showAddressBar: boolean;\n}\n\n/** Placeholder suggestions shown in the form; empty fields resolve to these. */\nexport interface WizardFormDefaults {\n readonly appId: string;\n readonly appName: string;\n /** Resolved project directory the app will be generated into. */\n readonly targetDir: string;\n /**\n * Effective default icon source (the clearest scraped candidate). The form\n * value stays empty until the user picks/upload; composition must follow\n * this default too, or the preview would never appear without a click.\n */\n readonly iconPath: string;\n}\n\nexport type WizardEvent =\n | { readonly type: \"state\"; readonly state: WizardState; readonly reason?: string }\n | { readonly type: \"log\"; readonly stream: \"stdout\" | \"stderr\"; readonly chunk: string }\n | { readonly type: \"term-mode\"; readonly interactive: boolean; readonly message?: string }\n | { readonly type: \"run-status\"; readonly running: boolean; readonly code?: number | null }\n | { readonly type: \"command-display\"; readonly command: string }\n | {\n readonly type: \"command-options\";\n readonly options: WizardCommandOptions;\n readonly defaultCwd: string;\n }\n | {\n readonly type: \"services\";\n readonly services: readonly DiscoveredService[];\n readonly selectedPort: number | undefined;\n }\n | { readonly type: \"scrape\"; readonly port: number; readonly title?: string; readonly hasIcon: boolean }\n | { readonly type: \"icons\"; readonly port: number; readonly icons: readonly ScrapedIcon[] }\n | {\n readonly type: \"form\";\n readonly values: WizardFormValues;\n readonly defaults: WizardFormDefaults;\n readonly targetDirExists: boolean;\n }\n | { readonly type: \"materialize-log\"; readonly message: string }\n | { readonly type: \"materialize-step\"; readonly step: string; readonly message: string }\n | {\n readonly type: \"success\";\n readonly projectDir: string;\n readonly bundlePath?: string;\n readonly pinHint: string;\n };\n\nexport interface WizardOptions {\n readonly cwd: string;\n /**\n * USER_HOME anchor: the command-execution default cwd AND the root of the\n * default generated-project location (~/.opentray/create/<name>).\n */\n readonly homeDir?: string;\n /** Explicit project directory (CLI positional); default: the create root. */\n readonly targetDir?: string | undefined;\n readonly skipInstall: boolean;\n /** Seed the 强制覆盖 toggle (CLI --force); the form owns the live value. */\n readonly force?: boolean | undefined;\n readonly packageManager?: \"npm\" | \"pnpm\" | \"bun\";\n readonly dependencyRange: string;\n readonly emit: (event: WizardEvent) => void;\n readonly spawnRun?: typeof startCommandRun;\n readonly listListeners?: () => Promise<ReadonlySet<number>>;\n readonly verifyHttp?: (port: number) => Promise<boolean>;\n /** Test/embedding seam for listener ownership. */\n readonly listPortOwners?: () => Promise<import(\"./port-scan\").ListenerOwners>;\n readonly scrape?: typeof scrapeService;\n readonly resolveVector?: typeof resolveLaunchVector;\n /** Test/embedding seam for the materialize pipeline. */\n readonly materializeContext?: Partial<MaterializeContext>;\n readonly platform?: NodeJS.Platform;\n readonly pollIntervalMs?: number;\n readonly scrapeIntervalMs?: number;\n}\n\nexport interface WizardSession {\n readonly state: WizardState;\n readonly services: readonly DiscoveredService[];\n readonly selectedPort: number | undefined;\n /** True while the preview process is alive (Run button shows Interrupt). */\n readonly runAlive: boolean;\n /** Latest scraped icon candidates for the selected service, ranked by clarity. */\n readonly iconCandidates: readonly ScrapedIcon[];\n /** Persist an uploaded image into the session temp dir; returns its path. */\n saveIconUpload(bytes: Buffer): Promise<string>;\n /** Candidate lookup scoped to the port it was scraped from. */\n iconCandidate(port: number, index: number): ScrapedIcon | undefined;\n /** Select a scraped candidate as the icon source (marks the field touched). */\n selectIconCandidate(port: number, index: number): boolean;\n /** Select a candidate (original or solid variant) as the TRAY icon. */\n selectTrayIconCandidate(port: number, index: number): boolean;\n /** Test/extension seam: replace the scraped candidate set for a port. */\n replaceIconCandidates(port: number, icons: readonly ScrapedIcon[]): void;\n /** Compose the app icon preview/asset for the current foreground. */\n composeIcon(options: {\n foregroundPath: string;\n background?: WizardIconBackground;\n scale?: number;\n }): Promise<WizardIconComposition>;\n /** Register a composition for token-scoped byte serving (server seam). */\n trackIconComposition(composition: WizardIconComposition): void;\n /** Look up a registered composition by cache key. */\n iconComposition(key: string): WizardIconComposition | undefined;\n /** Wizard-owned icon source roots (containment for icon routes). */\n iconSourceRoots(): readonly string[];\n /** Auto-suggestion for the foreground (background + reason). */\n analyzeIconForeground(foregroundPath: string): Promise<WizardIconAnalysis>;\n readonly form: WizardFormValues;\n readonly result: MaterializeResult | undefined;\n /** String form is tokenized; array form is taken as argv verbatim (array\n * input mode — no string splitting is ever applied to it). */\n submitCommand(command: string | readonly string[]): Promise<void>;\n /** Derive placeholder defaults from command text without spawning anything. */\n prime(command: string | readonly string[]): void;\n readonly commandOptions: WizardCommandOptions;\n updateCommandOptions(patch: Partial<WizardCommandOptions>): void;\n selectService(port: number): void;\n updateForm(patch: Partial<WizardFormValues>): void;\n terminalInput(data: string): void;\n terminalResize(size: { cols: number; rows: number }): void;\n confirm(): void;\n create(): Promise<void>;\n stop(): Promise<void>;\n}\n\ninterface FieldTouched {\n force: boolean;\n appId: boolean;\n appName: boolean;\n iconPath: boolean;\n iconBackground: boolean;\n iconScale: boolean;\n trayIconPath: boolean;\n pm: boolean;\n showStartupTerminal: boolean;\n showAddressBar: boolean;\n}\n\nexport const createWizardSession = (options: WizardOptions): WizardSession => {\n let state: WizardState = \"idle\";\n let services: DiscoveredService[] = [];\n let selectedPort: number | undefined;\n let run: CommandRun | undefined;\n let discovery = createPortDiscovery({\n baseline: new Set<number>(),\n ...(options.listListeners === undefined\n ? {}\n : { listListeners: () => options.listListeners!() }),\n ...(options.verifyHttp === undefined ? {} : { verifyHttp: options.verifyHttp }),\n });\n let discoveryTimer: ReturnType<typeof setInterval> | undefined;\n let scrapeTimer: ReturnType<typeof setInterval> | undefined;\n let scraping = false;\n let tempIconDir: string | undefined;\n let currentIconPath: string | undefined;\n let currentTrayIconPath: string | undefined;\n let trayIconIsSolid = false;\n let iconCandidates: readonly ScrapedIcon[] = [];\n let iconPort: number | undefined;\n let currentTokens: readonly string[] = [];\n let currentCommand = \"\";\n let scrapedTitle: string | undefined;\n let resolvedVector: LaunchVector | undefined;\n let frozenForm: WizardFormValues | undefined;\n let resolvedServicePort: number | undefined;\n let resolvedTargetDir: string | undefined;\n let frozenIconPath: string | undefined;\n let submitting = false;\n let runAlive = false;\n let commandOptions: WizardCommandOptions = { ...DEFAULT_COMMAND_OPTIONS };\n let composeIconDir: string | undefined;\n const iconCompositions = new Map<string, WizardIconComposition>();\n let iconBackground: WizardIconBackground | undefined;\n let iconScale: number | undefined;\n const touched: FieldTouched = { appId: false, appName: false, iconPath: false, iconBackground: false, iconScale: false, trayIconPath: false, pm: false, force: false, showStartupTerminal: false, showAddressBar: false };\n let form: WizardFormValues = {\n appId: \"\",\n appName: \"\",\n iconPath: \"\",\n iconBackground: \"transparent\",\n iconScale: DEFAULT_ICON_SCALE,\n trayIconPath: \"\",\n force: options.force === true,\n showStartupTerminal: false,\n showAddressBar: false,\n pm:\n options.packageManager ??\n detectPackageManager([], process.env.npm_config_user_agent),\n };\n let result: MaterializeResult | undefined;\n let stopped = false;\n\n const emit = options.emit;\n const setState = (next: WizardState, reason?: string): void => {\n state = next;\n emit({ type: \"state\", state: next, ...(reason === undefined ? {} : { reason }) });\n };\n\n const stopTimers = (): void => {\n if (discoveryTimer !== undefined) {\n clearInterval(discoveryTimer);\n discoveryTimer = undefined;\n }\n if (scrapeTimer !== undefined) {\n clearInterval(scrapeTimer);\n scrapeTimer = undefined;\n }\n };\n\n const publishServices = (): void => {\n emit({\n type: \"services\",\n services: [...services],\n selectedPort,\n });\n };\n\n /**\n * Placeholder defaults: scrapes and derivations update the *suggestions*,\n * never the user's values. Empty values mean \"use the default\".\n */\n const currentDefaults = (): WizardFormDefaults => {\n const effectiveAppId = form.appId.trim().length > 0 ? form.appId : deriveDefaultAppId(currentTokens);\n return {\n appId: deriveDefaultAppId(currentTokens),\n appName: scrapedTitle ?? deriveDefaultAppName(currentTokens),\n iconPath: currentIconPath ?? \"\",\n // Projects land under the OpenTray home by default (stable, idempotent\n // per app, never pollutes the invocation directory); the CLI positional\n // provides an explicit override.\n targetDir:\n options.targetDir ??\n join(homeDir, \".opentray\", \"create\", toProjectDirectoryName(effectiveAppId)),\n };\n };\n\n const publishForm = (): void => {\n emit({ type: \"form\", values: form, defaults: currentDefaults(), targetDirExists });\n };\n\n /** Track whether the resolved target directory is already occupied so the\n * UI can warn and offer the force toggle. */\n let targetDirExists = false;\n let targetDirProbe = 0;\n const refreshTargetDirExists = (): void => {\n const probe = ++targetDirProbe;\n const target = currentDefaults().targetDir;\n void (async () => {\n let occupied = false;\n try {\n const info = await stat(target);\n occupied = info.isDirectory();\n } catch {\n occupied = false;\n }\n if (probe !== targetDirProbe) return; // a newer probe owns the state\n if (occupied !== targetDirExists) {\n targetDirExists = occupied;\n publishForm();\n }\n })();\n };\n\n const scrapeOnce = async (): Promise<void> => {\n if (scraping || selectedPort === undefined || state === \"frozen\" || state === \"materializing\") {\n return;\n }\n scraping = true;\n try {\n const port = selectedPort;\n const scraped = await (options.scrape ?? scrapeService)(\n port,\n tempIconDir === undefined ? {} : { tempDir: tempIconDir },\n );\n if (selectedPort !== port) {\n return; // selection moved during scrape\n }\n const stateNow = state as WizardState;\n if (stateNow === \"frozen\" || stateNow === \"materializing\" || stateNow === \"success\") {\n return; // a confirm froze the form mid-scrape: never mutate identity\n }\n // Candidates always refresh (possibly to an empty list); the clearest\n // one remains the empty-field default.\n iconCandidates = scraped.icons;\n iconPort = port;\n currentIconPath = scraped.icons[0]?.path;\n if (scraped.title !== undefined) {\n scrapedTitle = scraped.title;\n }\n emit({ type: \"icons\", port, icons: scraped.icons });\n emit({\n type: \"scrape\",\n port,\n ...(scraped.title === undefined ? {} : { title: scraped.title }),\n hasIcon: scraped.icons.length > 0,\n });\n publishForm();\n } finally {\n scraping = false;\n }\n };\n\n const startScrapePolling = (): void => {\n if (scrapeTimer !== undefined) {\n clearInterval(scrapeTimer);\n }\n void scrapeOnce();\n scrapeTimer = setInterval(() => {\n void scrapeOnce();\n }, options.scrapeIntervalMs ?? 1_500);\n };\n\n const selectDefaultService = (): void => {\n if (selectedPort === undefined && services.length > 0) {\n selectedPort = services[0]?.port;\n }\n if (selectedPort !== undefined && state === \"running\") {\n setState(\"discovered\");\n startScrapePolling();\n }\n };\n\n const startDiscoveryPolling = (): void => {\n if (discoveryTimer !== undefined) {\n clearInterval(discoveryTimer);\n }\n void (async () => {\n const found = await discovery.poll();\n if (found.length > 0) {\n const known = new Set(services.map((service) => service.port));\n services = [...services, ...found.filter((s) => !known.has(s.port))];\n selectDefaultService();\n publishServices();\n }\n })();\n discoveryTimer = setInterval(() => {\n void (async () => {\n const found = await discovery.poll();\n if (found.length > 0) {\n const known = new Set(services.map((service) => service.port));\n services = [...services, ...found.filter((s) => !known.has(s.port))];\n selectDefaultService();\n publishServices();\n }\n })();\n }, options.pollIntervalMs ?? 1_000);\n };\n\n /** Default command cwd is the USER_HOME directory (owner round-10 law):\n * empty input means home, and relative paths resolve against home. */\n const homeDir = options.homeDir ?? homedir();\n const effectiveCwd = (): string => {\n const custom = commandOptions.cwd.trim();\n return custom.length > 0 ? resolve(homeDir, custom) : homeDir;\n };\n\n /** Build the env overlay from configured entries (empty keys skipped). */\n const commandEnv = (): Record<string, string> => {\n const env: Record<string, string> = {};\n for (const entry of commandOptions.env) {\n if (entry.key.trim().length > 0) {\n env[entry.key.trim()] = entry.value;\n }\n }\n return env;\n };\n\n const publishCommandOptions = (): void => {\n emit({ type: \"command-options\", options: commandOptions, defaultCwd: homeDir });\n };\n\n /** Stable dir for composed preview assets, created on demand. */\n const ensureIconComposeDir = async (): Promise<string> => {\n if (composeIconDir === undefined) {\n composeIconDir = await mkdtemp(join(tmpdir(), \"create-opentray-compose-\"));\n }\n return composeIconDir;\n };\n\n const session: WizardSession = {\n get state() {\n return state;\n },\n get services() {\n return services;\n },\n get selectedPort() {\n return selectedPort;\n },\n get runAlive() {\n return runAlive;\n },\n get iconCandidates() {\n return iconCandidates;\n },\n iconCandidate(port, index) {\n if (iconPort !== port) {\n return undefined;\n }\n return iconCandidates.find((icon) => icon.index === index);\n },\n replaceIconCandidates(port, icons) {\n iconPort = port;\n iconCandidates = icons;\n },\n\n selectTrayIconCandidate(port, index) {\n if (state === \"frozen\" || state === \"materializing\" || state === \"success\") {\n return false;\n }\n const candidate = session.iconCandidate(port, index);\n if (candidate === undefined) {\n return false;\n }\n touched.trayIconPath = true;\n currentTrayIconPath = candidate.path;\n trayIconIsSolid = candidate.variant !== \"original\";\n form = { ...form, trayIconPath: candidate.path };\n publishForm();\n return true;\n },\n\n selectIconCandidate(port, index) {\n if (state === \"frozen\" || state === \"materializing\" || state === \"success\") {\n return false;\n }\n const candidate = session.iconCandidate(port, index);\n if (candidate === undefined) {\n return false;\n }\n touched.iconPath = true;\n currentIconPath = candidate.path;\n if (!touched.trayIconPath) {\n // Default coupling: the tray follows the app icon until overridden.\n currentTrayIconPath = candidate.path;\n trayIconIsSolid = candidate.variant !== \"original\";\n form = { ...form, iconPath: candidate.path, trayIconPath: candidate.path };\n } else {\n form = { ...form, iconPath: candidate.path };\n }\n publishForm();\n return true;\n },\n get form() {\n return form;\n },\n get result() {\n return result;\n },\n\n async submitCommand(command) {\n if (state !== \"idle\" && state !== \"failed\" && state !== \"running\" && state !== \"discovered\") {\n throw new Error(`cannot submit a command while ${state}`);\n }\n // Concurrent posts double-spawn: the awaits below would overwrite the\n // first run reference, orphaning it (unkillable, holding the port).\n if (submitting) {\n throw new Error(\"a command submission is already in flight\");\n }\n submitting = true;\n // Array mode: the caller supplied argv elements directly — they are\n // used verbatim and NEVER re-split. String mode: tokenize one line.\n const tokens = typeof command === \"string\" ? undefined : command;\n let tokenized: ReturnType<typeof tokenizeCommandLine> | undefined;\n if (tokens === undefined) {\n tokenized = tokenizeCommandLine(command as string);\n if (!tokenized.ok) {\n submitting = false;\n setState(\"failed\", tokenized.error);\n return;\n }\n } else if (tokens.length === 0 || tokens[0]!.trim().length === 0) {\n submitting = false;\n setState(\"failed\", \"数组模式至少需要程序元素(第一个参数)\");\n return;\n }\n await session.stop();\n runAlive = false;\n stopped = false;\n services = [];\n selectedPort = undefined;\n currentTokens = tokens ?? tokenized!.tokens;\n refreshTargetDirExists();\n currentIconPath = undefined\n currentTrayIconPath = undefined;\n trayIconIsSolid = false;\n iconCandidates = [];\n touched.appId = false;\n touched.appName = false;\n touched.pm = false;\n // Cross-run consistency: the client tray UI resets on every new run;\n // a stale server-side tray pick would silently win at materialize.\n touched.trayIconPath = false;\n form = {\n appId: \"\",\n appName: \"\",\n iconPath: \"\",\n iconBackground: iconBackground ?? \"transparent\",\n iconScale: iconScale ?? DEFAULT_ICON_SCALE,\n ...(touched.force ? { force: form.force } : { force: options.force === true }),\n ...(touched.trayIconPath ? { trayIconPath: form.trayIconPath } : { trayIconPath: \"\" }),\n ...(touched.showStartupTerminal ? { showStartupTerminal: form.showStartupTerminal } : { showStartupTerminal: false }),\n ...(touched.showAddressBar ? { showAddressBar: form.showAddressBar } : { showAddressBar: false }),\n pm:\n options.packageManager ??\n detectPackageManager([], process.env.npm_config_user_agent),\n };\n currentCommand = typeof command === \"string\" ? command : command.join(\" \");\n scrapedTitle = undefined;\n emit({ type: \"command-display\", command: currentCommand });\n tempIconDir = await mkdtemp(join(tmpdir(), \"create-opentray-\"));\n\n const listListeners =\n options.listListeners ?? (() => listListeningPorts(process.platform));\n const baseline = await listListeners().catch(() => new Set<number>());\n\n // Emit the running state before spawning so the terminal panel appears\n // the instant Run fires, ahead of any process output or probe.\n setState(\"running\");\n publishForm();\n\n const spawnRun = options.spawnRun ?? startCommandRun;\n const envOverlay = commandEnv();\n run = await spawnRun({\n tokens: currentTokens,\n cwd: effectiveCwd(),\n ...(Object.keys(envOverlay).length === 0 ? {} : { env: envOverlay }),\n onEvent: (event: CommandRunEvent) => {\n if (event.type === \"stdout\" || event.type === \"stderr\") {\n emit({\n type: \"log\",\n stream: event.type,\n chunk: event.chunk ?? \"\",\n });\n return;\n }\n if (event.type === \"pty-ready\") {\n emit({ type: \"term-mode\", interactive: true });\n return;\n }\n if (event.type === \"pty-unavailable\") {\n emit({\n type: \"term-mode\",\n interactive: false,\n ...(event.message === undefined ? {} : { message: event.message }),\n });\n return;\n }\n if (event.type === \"spawn-error\") {\n stopTimers();\n setState(\"failed\", event.message ?? \"spawn failed\");\n return;\n }\n if (event.type === \"exit\") {\n // The process died (own exit, interrupt, or external kill): stop\n // polling and tell the UI the run control can go back to Run.\n runAlive = false;\n stopTimers();\n emit({\n type: \"run-status\",\n running: false,\n ...(event.code === undefined ? {} : { code: event.code }),\n });\n if (state === \"running\" && services.length === 0) {\n setState(\n \"failed\",\n `command exited with ${event.code ?? \"signal\"} before any service appeared`,\n );\n }\n }\n },\n });\n submitting = false;\n void run.exited.then(async ({ code, spawnError }) => {\n if (state === \"running\" && services.length === 0 && spawnError !== undefined) {\n stopTimers();\n setState(\"failed\", spawnError);\n }\n void code;\n });\n\n // Discovery starts after spawn so port ownership can be resolved from\n // the live preview PID tree; foreign listeners are never adopted.\n discovery = createPortDiscovery({\n baseline,\n ...(options.listListeners === undefined\n ? {}\n : { listListeners: () => options.listListeners!() }),\n ...(options.verifyHttp === undefined ? {} : { verifyHttp: options.verifyHttp }),\n ...(options.listPortOwners === undefined\n ? {}\n : { listOwners: options.listPortOwners }),\n ...(run.pid === undefined\n ? {}\n : {\n resolveOwnerPids: () =>\n collectProcessTreePids(run?.pid ?? 0, options.platform ?? process.platform),\n }),\n });\n\n runAlive = true;\n emit({ type: \"run-status\", running: true });\n startDiscoveryPolling();\n },\n\n prime(command) {\n if (state === \"frozen\" || state === \"materializing\" || state === \"success\") {\n return;\n }\n if (typeof command !== \"string\") {\n // Array mode: argv verbatim; nothing is ever re-split.\n if (command.length === 0 || command[0]!.trim().length === 0) {\n return;\n }\n currentTokens = command;\n currentCommand = command.join(\" \");\n emit({ type: \"command-display\", command: currentCommand });\n refreshTargetDirExists();\n publishForm();\n return;\n }\n const tokenized = tokenizeCommandLine(command);\n if (!tokenized.ok) {\n return; // keep previous placeholders for empty/invalid drafts\n }\n currentTokens = tokenized.tokens;\n currentCommand = command;\n emit({ type: \"command-display\", command });\n refreshTargetDirExists();\n publishForm();\n },\n\n get commandOptions() {\n return commandOptions;\n },\n\n updateCommandOptions(patch) {\n if (state === \"frozen\" || state === \"materializing\" || state === \"success\") {\n return;\n }\n commandOptions = { ...commandOptions, ...patch };\n publishCommandOptions();\n },\n\n async analyzeIconForeground(foregroundPath) {\n // One decode serves both metrics (huge uploads must not double their\n // full-resolution buffers). A decode failure falls back consistently\n // to the WHITE suggestion — never transparent (which would silently\n // replace undecodable art with the glyph path).\n const stats = await foregroundStats(foregroundPath).catch(() => ({\n luminance: undefined,\n coverage: 0,\n }));\n return {\n ...stats,\n suggested: autoBackground(stats),\n };\n },\n\n async composeIcon(options) {\n if (state === \"frozen\" || state === \"materializing\" || state === \"success\") {\n throw new Error(\"cannot compose while frozen\");\n }\n const background = options.background ?? iconBackground ?? \"transparent\";\n const scale = options.scale ?? iconScale ?? DEFAULT_ICON_SCALE;\n const composed = await composeAppIcon({\n foregroundPath: options.foregroundPath,\n background,\n scale,\n outputDir: await ensureIconComposeDir(),\n });\n const composition: WizardIconComposition = {\n key: compositionCacheKey({\n foregroundPath: options.foregroundPath,\n background,\n scale,\n }),\n ...composed,\n };\n iconCompositions.set(composition.key, composition);\n return composition;\n },\n\n trackIconComposition(composition) {\n iconCompositions.set(composition.key, composition);\n },\n\n iconSourceRoots() {\n return [\n ...(tempIconDir !== undefined ? [tempIconDir] : []),\n ...(composeIconDir !== undefined ? [composeIconDir] : []),\n ];\n },\n\n iconComposition(key) {\n return iconCompositions.get(key);\n },\n\n async saveIconUpload(bytes) {\n const dir = tempIconDir ?? (tempIconDir = await mkdtemp(join(tmpdir(), \"create-opentray-\")));\n const name = `upload-${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16)}.bin`;\n const path = join(dir, name);\n await writeFile(path, bytes);\n return path;\n },\n\n selectService(port) {\n if (state !== \"discovered\" && state !== \"running\") {\n return;\n }\n if (!services.some((service) => service.port === port)) {\n return;\n }\n selectedPort = port;\n currentIconPath = undefined;\n publishServices();\n if (state === \"discovered\") {\n startScrapePolling();\n }\n },\n\n updateForm(patch) {\n if (state !== \"idle\" && state !== \"running\" && state !== \"discovered\" && state !== \"failed\") {\n return; // frozen/materializing: no form mutation, composition included\n }\n if (patch.iconBackground !== undefined) {\n iconBackground = patch.iconBackground;\n }\n if (patch.iconScale !== undefined) {\n iconScale = patch.iconScale;\n }\n for (const key of Object.keys(patch) as (keyof WizardFormValues)[]) {\n if (patch[key] !== undefined && patch[key] !== form[key]) {\n touched[key] = true;\n if (key === \"trayIconPath\") {\n // Typed/unknown tray sources carry no solid provenance; a stale\n // flag from an earlier solid pick would mis-tint custom art.\n trayIconIsSolid = false;\n }\n }\n }\n form = { ...form, ...patch };\n publishForm();\n },\n\n /** Forward base64-encoded terminal keystroke bytes to the preview command. */\n terminalInput(data) {\n if (state !== \"running\" && state !== \"discovered\") {\n return;\n }\n run?.write(data);\n },\n\n /** Forward terminal dimensions to the pseudo-terminal. */\n terminalResize(size) {\n if (state !== \"running\" && state !== \"discovered\") {\n return;\n }\n run?.resize(size);\n },\n\n confirm() {\n if (state !== \"idle\" && state !== \"running\" && state !== \"discovered\" && state !== \"failed\") {\n throw new Error(`cannot confirm while ${state}`);\n }\n // Ports come exclusively from runtime sniffing: the preview's discovered\n // port is recorded as an informational hint (0 when never sniffed); the\n // generated app resolves the real address by scanning its own command\n // tree. A manual port input must not exist.\n resolvedServicePort = selectedPort ?? 0;\n // Freeze the SAME default the UI displayed (explicit override wins).\n resolvedTargetDir = currentDefaults().targetDir;\n // Empty fields resolve to their placeholder defaults.\n const defaults = currentDefaults();\n let resolvedForm: WizardFormValues = {\n ...form,\n appId: form.appId.trim().length > 0 ? form.appId : defaults.appId,\n appName: form.appName.trim().length > 0 ? form.appName : defaults.appName,\n };\n // A user-entered icon path wins over the scraped favicon.\n if (resolvedForm.iconPath.trim().length > 0) {\n currentIconPath = resolvedForm.iconPath.trim();\n }\n // The icon source materialize uses is FROZEN at confirm: the user's\n // explicit value when set, else the scraped default captured here.\n // Never the live currentIconPath, which an in-flight scrape could\n // still swap (review round: post-freeze overwrite race).\n frozenIconPath = currentIconPath;\n\n // The tray icon defaults to the resolved app icon choice.\n const resolvedTrayIconPath =\n resolvedForm.trayIconPath.trim().length > 0\n ? resolvedForm.trayIconPath.trim()\n : resolvedForm.iconPath.trim().length > 0\n ? resolvedForm.iconPath.trim()\n : (currentIconPath ?? \"\");\n resolvedForm = { ...resolvedForm, trayIconPath: resolvedTrayIconPath };\n currentTrayIconPath = resolvedTrayIconPath;\n form = resolvedForm;\n stopTimers();\n frozenForm = { ...form };\n setState(\"frozen\");\n publishForm();\n },\n\n async create() {\n if (state !== \"frozen\") {\n throw new Error(`cannot create while ${state}`);\n }\n const frozen = frozenForm ?? form;\n if (currentTokens.length === 0) {\n throw new Error(\"no command recorded\");\n }\n setState(\"materializing\");\n\n // Free the service port: the generated app will spawn the command itself.\n if (run !== undefined) {\n await run.kill();\n run = undefined;\n }\n\n try {\n resolvedVector = await (options.resolveVector ?? resolveLaunchVector)({\n tokens: currentTokens,\n cwd: effectiveCwd(),\n });\n // Persist the configured env overlay onto the frozen vector; the\n // generated app merges it over its own environment when spawning.\n const envOverlay = commandEnv();\n if (Object.keys(envOverlay).length > 0) {\n resolvedVector = { ...resolvedVector, env: envOverlay };\n }\n } catch (error) {\n setState(\"failed\", error instanceof Error ? error.message : String(error));\n return;\n }\n\n try {\n result = await materialize(\n {\n config: {\n schemaVersion: 1,\n appId: frozen.appId,\n appName: frozen.appName,\n command: resolvedVector,\n service: { port: resolvedServicePort ?? 0 },\n window: { width: 1_200, height: 800 },\n },\n targetDir: resolvedTargetDir ?? currentDefaults().targetDir,\n dependencyRange: options.dependencyRange,\n iconSourcePath: frozenIconPath ?? currentIconPath,\n ...(frozen.iconBackground === undefined\n ? {}\n : { iconBackground: frozen.iconBackground }),\n ...(frozen.iconScale === undefined\n ? {}\n : { iconScale: frozen.iconScale }),\n ...(currentTrayIconPath === undefined\n ? {}\n : { trayIconSourcePath: currentTrayIconPath }),\n ...(trayIconIsSolid ? { trayIconIsSolid: true } : {}),\n shell: {\n showTerminal: frozen.showStartupTerminal,\n showAddressBar: frozen.showAddressBar,\n },\n packageManager: frozen.pm,\n skipInstall: options.skipInstall,\n force: frozen.force,\n },\n {\n log: (event) => {\n if (event.type === \"step\") {\n emit({ type: \"materialize-step\", step: event.step, message: event.message });\n return;\n }\n emit({ type: \"materialize-log\", message: event.message });\n },\n ...(options.platform === undefined ? {} : { platform: options.platform }),\n ...(options.materializeContext ?? {}),\n },\n );\n setState(\"success\");\n emit({\n type: \"success\",\n projectDir: result.projectDir,\n ...(result.bundlePath === undefined ? {} : { bundlePath: result.bundlePath }),\n pinHint: pinningHint(),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const occupied = message.includes(\"target directory is not empty\");\n setState(\n \"failed\",\n occupied\n ? `${message};可在「高级选项」中开启 强制覆盖 后重试`\n : message,\n );\n }\n },\n\n async stop() {\n stopped = true;\n stopTimers();\n discovery.stop();\n if (run !== undefined) {\n await run.kill();\n run = undefined;\n }\n },\n };\n\n publishCommandOptions();\n\n return session;\n};\n\n/** Exposed for tests: the touched-field bookkeeping semantics. */\nexport const createFieldTouchedTracker = (): { touched: FieldTouched } => ({\n touched: { force: false, appId: false, appName: false, iconPath: false, iconBackground: false, iconScale: false, trayIconPath: false, pm: false, showStartupTerminal: false, showAddressBar: false },\n});\n","// Orthogonal intents (maintained 2026-07-22; original user request: the wizard\n// is a local WebUI; mutating endpoints must be session-guarded):\n// 1. Serve the static wizard page and its API on 127.0.0.1 only.\n// 2. Stream wizard events over SSE with per-client replay of the event log.\n// 3. Reject unauthenticated or non-loopback-Host mutations with 401/403.\n\nimport {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, extname, join, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type {\n WizardCommandOptions,\n WizardEnvEntry,\n WizardEvent,\n WizardFormValues,\n WizardSession,\n} from \"./wizard\";\nimport { openMaterializedApp } from \"./open-app\";\n\nexport interface WizardServerHandle {\n readonly url: string;\n readonly port: number;\n readonly token: string;\n readonly session: WizardSession;\n close(): Promise<void>;\n}\n\nconst LOOPBACK_HOSTS = new Set([\"127.0.0.1\", \"localhost\", \"[::1]\", \"::1\"]);\n\nexport const createWizardServer = async (\n createSession: (emit: (event: WizardEvent) => void) => WizardSession,\n options: { readonly port?: number } = {},\n): Promise<WizardServerHandle> => {\n const token = randomBytes(16).toString(\"hex\");\n const clients = new Set<ServerResponse>();\n const eventLog: WizardEvent[] = [];\n\n const emit = (event: WizardEvent): void => {\n eventLog.push(event);\n const frame = `data: ${JSON.stringify(event)}\\n\\n`;\n for (const client of clients) {\n client.write(frame);\n }\n };\n\n const session = createSession(emit);\n\n const indexHtml = await readWebUiIndex();\n\n const server: Server = createServer((request, response) => {\n void handle(request, response).catch((error: unknown) => {\n respond(\n response,\n 500,\n \"application/json\",\n `${JSON.stringify({ error: error instanceof Error ? error.message : String(error) })}\\n`,\n );\n });\n });\n\n const handle = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n const url = new URL(request.url ?? \"/\", \"http://127.0.0.1\");\n if (url.pathname === \"/\" || url.pathname === \"/index.html\") {\n if (!isAuthorized(request, url, token)) {\n respond(response, 401, \"text/plain\", \"invalid wizard token\\n\");\n return;\n }\n if (indexHtml === undefined) {\n respond(response, 500, \"text/plain\", \"wizard page is missing from this installation\\n\");\n return;\n }\n respond(response, 200, \"text/html; charset=utf-8\", indexHtml);\n return;\n }\n\n if (url.pathname === \"/api/events\") {\n if (!isAuthorized(request, url, token)) {\n respond(response, 401, \"text/plain\", \"unauthorized\\n\");\n return;\n }\n response.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n response.write(\": connected\\n\\n\");\n for (const event of eventLog) {\n response.write(`data: ${JSON.stringify(event)}\\n\\n`);\n }\n clients.add(response);\n request.once(\"close\", () => {\n clients.delete(response);\n });\n return;\n }\n\n if (url.pathname.startsWith(\"/assets/\")) {\n await handleAssetFile(url.pathname, response);\n return;\n }\n\n // ghostty-web resolves its WASM document-relative: /ghostty-vt.wasm.\n if (url.pathname === \"/ghostty-vt.wasm\") {\n await handleAssetFile(\"/ghostty-vt.wasm\", response);\n return;\n }\n\n if (url.pathname.startsWith(\"/vendor/\")) {\n await handleVendorAsset(url.pathname, response);\n return;\n }\n\n // Composed app-icon preview bytes (wizard compose cache), token-scoped.\n const composedMatch = /^\\/api\\/icon-composed\\/([a-f0-9]+)$/.exec(url.pathname);\n if (composedMatch !== null) {\n if (!isAuthorized(request, url, token)) {\n respond(response, 401, \"text/plain\", \"unauthorized\\n\");\n return;\n }\n const key = composedMatch[1] as string;\n const composed = session.iconComposition(key);\n if (composed === undefined) {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n try {\n const bytes = await readFile(composed.compositePath);\n response.writeHead(200, {\n \"content-type\": \"image/png\",\n \"cache-control\": \"no-store\",\n \"content-length\": bytes.byteLength,\n });\n response.end(bytes);\n return;\n } catch {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n }\n\n // Icon bytes for candidate thumbnails: <img> tags cannot send headers, so\n // auth accepts the same ?token= query the SSE stream uses.\n const iconDataMatch = /^\\/api\\/icon-data\\/(\\d+)\\/(\\d+)$/.exec(url.pathname);\n if (iconDataMatch !== null) {\n if (!isAuthorized(request, url, token)) {\n respond(response, 401, \"text/plain\", \"unauthorized\\n\");\n return;\n }\n const port = Number.parseInt(iconDataMatch[1] as string, 10);\n const index = Number.parseInt(iconDataMatch[2] as string, 10);\n const candidate = session.iconCandidate(port, index);\n if (candidate === undefined) {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n const bytes = await readFile(candidate.path).catch(() => undefined);\n if (bytes === undefined) {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n response.writeHead(200, {\n \"content-type\": ICON_CONTENT_TYPES[candidate.format] ?? \"application/octet-stream\",\n \"content-length\": bytes.length,\n \"cache-control\": \"no-store\",\n });\n response.end(bytes);\n return;\n }\n\n if (url.pathname.startsWith(\"/api/\")) {\n if (!isAuthorized(request, url, token)) {\n respond(response, 401, \"application/json\", '{\"error\":\"unauthorized\"}\\n');\n return;\n }\n if (!isLoopbackHost(request)) {\n respond(response, 403, \"application/json\", '{\"error\":\"forbidden host\"}\\n');\n return;\n }\n await handleApi(url.pathname, request, response, session);\n return;\n }\n\n respond(response, 404, \"text/plain\", \"not found\\n\");\n };\n\n await listen(server, options.port);\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n server.close();\n throw new Error(\"wizard server did not bind a loopback port\");\n }\n\n return {\n url: `http://127.0.0.1:${address.port}/?token=${token}`,\n port: address.port,\n token,\n session,\n close: () =>\n new Promise<void>((resolve) => {\n for (const client of clients) {\n client.end();\n }\n clients.clear();\n server.close(() => resolve());\n }),\n };\n};\n\n/** Whitelisted static terminal-renderer assets; exact names only, no traversal. */\nconst VENDOR_ASSETS: Readonly<\n Record<string, { contentType: string; candidates: () => readonly string[] }>\n> = {\n \"/vendor/ghostty-web.js\": {\n contentType: \"text/javascript; charset=utf-8\",\n candidates: () => [\n join(moduleDir(), \"webui\", \"vendor\", \"ghostty-web.js\"),\n ghosttyPackageFile(\"dist/ghostty-web.js\"),\n ],\n },\n \"/vendor/ghostty-vt.wasm\": {\n contentType: \"application/wasm\",\n candidates: () => [\n join(moduleDir(), \"webui\", \"vendor\", \"ghostty-vt.wasm\"),\n ghosttyPackageFile(\"ghostty-vt.wasm\"),\n ghosttyPackageFile(\"dist/ghostty-vt.wasm\"),\n ],\n },\n};\n\nconst moduleDir = (): string => dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve a file inside the installed ghostty-web package. Deep paths are\n * blocked by the package `exports` map, so resolve the exported main module\n * and walk from the package root instead.\n */\nconst ghosttyPackageFile = (relative: string): string => {\n try {\n const require = createRequire(import.meta.url);\n const main = require.resolve(\"ghostty-web\");\n const packageRoot = dirname(dirname(main));\n return join(packageRoot, relative);\n } catch {\n return \"\";\n }\n};\n\nconst ICON_CONTENT_TYPES: Readonly<Record<string, string>> = {\n png: \"image/png\",\n svg: \"image/svg+xml\",\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n ico: \"image/x-icon\",\n};\n\nconst ASSET_CONTENT_TYPES: Readonly<Record<string, string>> = {\n \".js\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".wasm\": \"application/wasm\",\n \".html\": \"text/html; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".json\": \"application/json\",\n};\n\n/**\n * Serve one file from the built webui directory (dist/webui). Only simple\n * relative names are accepted: resolve first, then require the resolved path\n * to stay inside the webui root, so traversal cannot escape.\n */\nconst handleAssetFile = async (\n pathname: string,\n response: ServerResponse,\n): Promise<void> => {\n const relative = pathname.replace(/^\\/+/, \"\");\n if (relative.length === 0 || relative.includes(\"\\u0000\")) {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n // Built package layout first; source checkout falls back to create-webui's\n // vite output. Resolve-then-contain so traversal cannot escape any root.\n const roots = [\n join(moduleDir(), \"webui\"),\n join(moduleDir(), \"..\", \"..\", \"create-webui\", \"dist\"),\n ];\n for (const root of roots) {\n const resolved = resolve(root, relative);\n if (!(resolved === root || resolved.startsWith(`${root}${sep}`))) {\n continue;\n }\n const bytes = await readFile(resolved).catch(() => undefined);\n if (bytes === undefined) {\n continue;\n }\n const extension = extname(resolved).toLowerCase();\n response.writeHead(200, {\n \"content-type\": ASSET_CONTENT_TYPES[extension] ?? \"application/octet-stream\",\n \"content-length\": bytes.length,\n \"cache-control\": \"no-store\",\n });\n response.end(bytes);\n return;\n }\n respond(response, 404, \"text/plain\", \"not found\\n\");\n};\n\nconst handleVendorAsset = async (pathname: string, response: ServerResponse): Promise<void> => {\n const asset = VENDOR_ASSETS[pathname];\n if (asset === undefined) {\n respond(response, 404, \"text/plain\", \"not found\\n\");\n return;\n }\n for (const candidate of asset.candidates()) {\n if (candidate.length === 0) {\n continue;\n }\n const bytes = await readFile(candidate).catch(() => undefined);\n if (bytes !== undefined) {\n response.writeHead(200, {\n \"content-type\": asset.contentType,\n \"content-length\": bytes.length,\n \"cache-control\": \"no-store\",\n });\n response.end(bytes);\n return;\n }\n }\n respond(response, 404, \"text/plain\", \"terminal renderer asset is missing\\n\");\n};\n\n/** Containment: icon routes must only read sources the wizard itself\n * produced (its temp dirs / saved uploads), never arbitrary paths. */\nconst isWizardOwnedIconPath = (session: WizardSession, target: string): boolean => {\n for (const root of session.iconSourceRoots()) {\n if (root.length > 0 && target.startsWith(root + sep)) {\n return true;\n }\n }\n return false;\n};\n\nconst handleApi = async (\n pathname: string,\n request: IncomingMessage,\n response: ServerResponse,\n session: WizardSession,\n): Promise<void> => {\n if (request.method !== \"POST\") {\n respond(response, 405, \"application/json\", '{\"error\":\"method not allowed\"}\\n');\n return;\n }\n if (pathname === \"/api/icon-upload\") {\n // Raw image bytes in the request body — must not pass the JSON reader.\n const chunks: Buffer[] = [];\n for await (const chunk of request) {\n const piece = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n chunks.push(piece);\n }\n const bytes = Buffer.concat(chunks);\n if (bytes.length < 64) {\n respond(response, 400, \"application/json\", '{\"error\":\"image bytes are required\"}\\n');\n return;\n }\n const path = await session.saveIconUpload(bytes);\n respond(response, 200, \"application/json\", JSON.stringify({ path }) + \"\\n\");\n return;\n }\n const body = await readJsonBody(request);\n switch (pathname) {\n case \"/api/command\": {\n // Array form: argv elements used verbatim (array input mode).\n if (Array.isArray(body.argv)) {\n const argv = body.argv.filter((element: unknown): element is string => typeof element === \"string\");\n if (argv.length === 0 || (argv[0] ?? \"\").trim().length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"argv requires the program element\"}\\n');\n return;\n }\n await session.submitCommand(argv);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n const command = typeof body.command === \"string\" ? body.command : \"\";\n if (command.trim().length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"command is required\"}\\n');\n return;\n }\n await session.submitCommand(command);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/command-options\": {\n const patch: Partial<{ -readonly [K in keyof WizardCommandOptions]: WizardCommandOptions[K] }> = {};\n if (typeof body.cwd === \"string\") {\n patch.cwd = body.cwd;\n }\n if (body.argsMode === \"string\" || body.argsMode === \"array\") {\n patch.argsMode = body.argsMode;\n }\n if (Array.isArray(body.env)) {\n const entries: WizardEnvEntry[] = [];\n for (const entry of body.env) {\n if (\n typeof entry === \"object\" &&\n entry !== null &&\n typeof (entry as { key?: unknown }).key === \"string\" &&\n typeof (entry as { value?: unknown }).value === \"string\"\n ) {\n entries.push({ key: (entry as { key: string }).key, value: (entry as { value: string }).value });\n }\n }\n patch.env = entries;\n }\n session.updateCommandOptions(patch);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/prime\": {\n const command = typeof body.command === \"string\" ? body.command : \"\";\n if (command.trim().length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"command is required\"}\\n');\n return;\n }\n session.prime(command);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/select-service\": {\n const port = Number(body.port);\n if (!Number.isInteger(port) || port <= 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"port is required\"}\\n');\n return;\n }\n session.selectService(port);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/form\": {\n const patch: Partial<{ -readonly [K in keyof WizardFormValues]: WizardFormValues[K] }> = {};\n for (const key of [\"appId\", \"appName\", \"iconPath\", \"trayIconPath\"] as const) {\n const value = body[key];\n if (typeof value === \"string\") {\n patch[key] = value;\n }\n }\n if (body.pm === \"npm\" || body.pm === \"pnpm\" || body.pm === \"bun\") {\n patch.pm = body.pm;\n }\n if (\n body.iconBackground === \"black\" ||\n body.iconBackground === \"white\" ||\n body.iconBackground === \"transparent\"\n ) {\n patch.iconBackground = body.iconBackground;\n }\n if (typeof body.iconScale === \"number\" && body.iconScale >= 0.5 && body.iconScale <= 0.95) {\n patch.iconScale = body.iconScale;\n }\n for (const key of [\"showStartupTerminal\", \"showAddressBar\", \"force\"] as const) {\n const value = body[key];\n if (typeof value === \"boolean\") {\n patch[key] = value;\n }\n }\n session.updateForm(patch);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/confirm\": {\n try {\n session.confirm();\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n } catch (error) {\n respond(\n response,\n 409,\n \"application/json\",\n `${JSON.stringify({ error: error instanceof Error ? error.message : String(error) })}\\n`,\n );\n }\n return;\n }\n case \"/api/create\": {\n try {\n await session.create();\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n } catch (error) {\n respond(\n response,\n 409,\n \"application/json\",\n `${JSON.stringify({ error: error instanceof Error ? error.message : String(error) })}\\n`,\n );\n }\n return;\n }\n case \"/api/terminal-input\": {\n const data = typeof body.data === \"string\" ? body.data : undefined;\n if (data === undefined) {\n respond(response, 400, \"application/json\", '{\"error\":\"data is required\"}\\n');\n return;\n }\n session.terminalInput(data);\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/icon-analyze\": {\n const path = typeof body.path === \"string\" ? body.path.trim() : \"\";\n if (path.length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"path is required\"}\\n');\n return;\n }\n if (!isWizardOwnedIconPath(session, path)) {\n respond(response, 403, \"application/json\", '{\"error\":\"path is not a wizard icon source\"}\\n');\n return;\n }\n try {\n const analysis = await session.analyzeIconForeground(path);\n respond(response, 200, \"application/json\", JSON.stringify(analysis) + \"\\n\");\n } catch (error) {\n respond(response, 500, \"application/json\", JSON.stringify({ error: String(error) }) + \"\\n\");\n }\n return;\n }\n case \"/api/icon-compose\": {\n const foregroundPath = typeof body.foregroundPath === \"string\" ? body.foregroundPath.trim() : \"\";\n if (foregroundPath.length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"foregroundPath is required\"}\\n');\n return;\n }\n if (!isWizardOwnedIconPath(session, foregroundPath)) {\n respond(response, 403, \"application/json\", '{\"error\":\"foregroundPath is not a wizard icon source\"}\\n');\n return;\n }\n const background =\n body.background === \"black\" || body.background === \"white\" || body.background === \"transparent\"\n ? body.background\n : undefined;\n const scale =\n typeof body.scale === \"number\" && body.scale >= 0.5 && body.scale <= 0.95\n ? body.scale\n : undefined;\n try {\n const composed = await session.composeIcon({\n foregroundPath,\n ...(background === undefined ? {} : { background }),\n ...(scale === undefined ? {} : { scale }),\n });\n respond(response, 200, \"application/json\", JSON.stringify(composed) + \"\\n\");\n } catch (error) {\n respond(response, 500, \"application/json\", JSON.stringify({ error: String(error) }) + \"\\n\");\n }\n return;\n }\n case \"/api/tray-icon-select\": {\n const port = typeof body.port === \"number\" ? body.port : Number.NaN;\n const index = typeof body.index === \"number\" ? body.index : Number.NaN;\n if (!Number.isInteger(port) || !Number.isInteger(index)) {\n respond(response, 400, \"application/json\", '{\"error\":\"port and index are required\"}\\n');\n return;\n }\n const ok = session.selectTrayIconCandidate(port, index);\n respond(response, 200, \"application/json\", JSON.stringify({ ok }) + \"\\n\");\n return;\n }\n case \"/api/icon-select\": {\n const port = typeof body.port === \"number\" ? body.port : Number.NaN;\n const index = typeof body.index === \"number\" ? body.index : Number.NaN;\n if (!Number.isInteger(port) || !Number.isInteger(index)) {\n respond(response, 400, \"application/json\", '{\"error\":\"port and index are required\"}\\n');\n return;\n }\n const ok = session.selectIconCandidate(port, index);\n respond(response, 200, \"application/json\", JSON.stringify({ ok }) + \"\\n\");\n return;\n }\n case \"/api/icon-source\": {\n const path = typeof body.path === \"string\" ? body.path.trim() : \"\";\n if (path.length === 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"path is required\"}\\n');\n return;\n }\n session.updateForm({ iconPath: path });\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/terminal-resize\": {\n const cols = Number(body.cols);\n const rows = Number(body.rows);\n if (!Number.isInteger(cols) || !Number.isInteger(rows) || cols <= 0 || rows <= 0) {\n respond(response, 400, \"application/json\", '{\"error\":\"cols and rows are required\"}\\n');\n return;\n }\n session.terminalResize({ cols, rows });\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/stop\": {\n await session.stop();\n respond(response, 200, \"application/json\", '{\"ok\":true}\\n');\n return;\n }\n case \"/api/open-app\": {\n const result = session.result;\n if (result === undefined) {\n respond(response, 409, \"application/json\", '{\"error\":\"no materialized app\"}\\n');\n return;\n }\n const opened = await openMaterializedApp({\n projectDir: result.projectDir,\n bundlePath: result.bundlePath,\n });\n respond(\n response,\n opened.ok ? 200 : 500,\n \"application/json\",\n `${JSON.stringify({ ok: opened.ok, detail: opened.detail })}\\n`,\n );\n return;\n }\n default:\n respond(response, 404, \"application/json\", '{\"error\":\"unknown endpoint\"}\\n');\n }\n};\n\nconst listen = (server: Server, port?: number): Promise<void> =>\n new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port ?? 0, \"127.0.0.1\", () => {\n server.off(\"error\", reject);\n resolve();\n });\n });\n\nexport const isAuthorized = (\n request: IncomingMessage,\n url: URL,\n token: string,\n): boolean => {\n const header = request.headers.authorization;\n if (header === `Bearer ${token}`) {\n return true;\n }\n return url.searchParams.get(\"token\") === token;\n};\n\nexport const isLoopbackHost = (request: IncomingMessage): boolean => {\n const host = request.headers.host ?? \"\";\n const hostname = host.replace(/:\\d+$/u, \"\").toLowerCase();\n return LOOPBACK_HOSTS.has(hostname);\n};\n\nconst readJsonBody = async (request: IncomingMessage): Promise<Record<string, unknown>> => {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));\n size += buffer.length;\n if (size > 1 << 20) {\n throw new Error(\"request body too large\");\n }\n chunks.push(buffer);\n }\n if (chunks.length === 0) {\n return {};\n }\n const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n if (typeof parsed !== \"object\" || parsed === null) {\n return {};\n }\n return parsed as Record<string, unknown>;\n};\n\nconst respond = (\n response: ServerResponse,\n status: number,\n contentType: string,\n body: string,\n): void => {\n response.writeHead(status, {\n \"content-type\": contentType,\n \"content-length\": Buffer.byteLength(body),\n \"cache-control\": \"no-store\",\n });\n response.end(body);\n};\n\n/** Built package: dist/webui/index.html; source checkout falls back to the create-webui vite output. */\nconst webUiIndexPaths = (): readonly string[] => [\n join(moduleDir(), \"webui\", \"index.html\"),\n join(moduleDir(), \"..\", \"..\", \"create-webui\", \"dist\", \"index.html\"),\n];\n\nconst readWebUiIndex = async (): Promise<string | undefined> => {\n for (const candidate of webUiIndexPaths()) {\n const html = await readFile(candidate, \"utf8\").catch(() => undefined);\n if (html !== undefined) {\n return html;\n }\n }\n return undefined;\n};\n\n/** Token fingerprint helper used in tests and diagnostics. */\nexport const fingerprintToken = (token: string): string =>\n createHash(\"sha256\").update(token).digest(\"hex\").slice(0, 12);\n","#!/usr/bin/env node\n// Orthogonal intents (maintained 2026-07-22; original user request: `npx\n// create-opentray` opens a WebUI wizard that packages a start command into an\n// OpenTray-hosted app):\n// 1. Parse wizard flags and resolve the working directory.\n// 2. Serve the wizard on loopback and open the default browser unless disabled.\n// 3. Tear down the preview process tree on exit signals.\n\nimport { spawn } from \"node:child_process\";\nimport { realpathSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { createWizardServer } from \"./server\";\nimport { createWizardSession } from \"./wizard\";\nimport { ensureLoopbackNoProxy } from \"./port-scan\";\n\nexport interface WizardCliOptions {\n readonly open: boolean;\n readonly port: number | undefined;\n readonly pm: \"npm\" | \"pnpm\" | \"bun\" | undefined;\n readonly skipInstall: boolean;\n readonly force: boolean;\n readonly targetDir: string | undefined;\n}\n\n\nexport const parseWizardCli = (argv: readonly string[]): WizardCliOptions => {\n const options: {\n open: boolean;\n port?: number;\n pm?: \"npm\" | \"pnpm\" | \"bun\";\n skipInstall: boolean;\n force: boolean;\n targetDir?: string;\n } = { open: true, skipInstall: false, force: false };\n const positional: string[] = [];\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n if (arg === undefined) {\n continue;\n }\n if (arg === \"--no-open\") {\n options.open = false;\n } else if (arg === \"--skip-install\") {\n options.skipInstall = true;\n } else if (arg === \"--force\") {\n options.force = true;\n } else if (arg === \"--port\") {\n const value = argv[index + 1];\n const port = Number.parseInt(value ?? \"\", 10);\n if (Number.isInteger(port) && port > 0 && port < 65_536) {\n options.port = port;\n index += 1;\n }\n } else if (arg === \"--pm\") {\n const value = argv[index + 1];\n if (value === \"npm\" || value === \"pnpm\" || value === \"bun\") {\n options.pm = value;\n index += 1;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n options.open = false;\n positional.length = 0;\n break;\n } else if (!arg.startsWith(\"--\")) {\n positional.push(arg);\n }\n }\n const [target] = positional;\n return {\n open: options.open,\n port: options.port,\n pm: options.pm,\n skipInstall: options.skipInstall,\n force: options.force,\n targetDir: target,\n };\n};\n\nconst WIZARD_HELP = [\n \"create-opentray — turn a start command into an OpenTray-hosted desktop app\",\n \"\",\n \"Usage: create-opentray [targetDir] [options]\",\n \"\",\n \"Options:\",\n \" --no-open do not open the default browser\",\n \" --port <n> bind the wizard server to a specific loopback port\",\n \" --pm <name> package manager for the generated app (npm | pnpm | bun)\",\n \" --skip-install scaffold without installing dependencies\",\n \" --force allow materializing into a non-empty directory\",\n \" -h, --help show this help\",\n].join(\"\\n\");\n\nconst readDependencyRange = async (): Promise<string> => {\n const packageJsonUrl = new URL(\"../package.json\", import.meta.url);\n try {\n const parsed = JSON.parse(await readFile(packageJsonUrl, \"utf8\")) as {\n version?: string;\n };\n if (typeof parsed.version === \"string\" && /^\\d/u.test(parsed.version)) {\n // Generated apps stay on the same release line as this initializer.\n return `^${parsed.version}`;\n }\n } catch {\n // Fall through to the workspace dev default below.\n }\n return \"latest\";\n};\n\nexport const openBrowser = async (url: string): Promise<void> => {\n const platform = process.platform;\n const command =\n platform === \"darwin\" ? \"open\" : platform === \"win32\" ? \"cmd\" : \"xdg-open\";\n const args =\n platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : platform === \"darwin\" ? [url] : [url];\n const child = spawn(command, args, { stdio: \"ignore\", detached: true, windowsHide: true });\n child.unref();\n};\n\nexport const main = async (argv: readonly string[]): Promise<number> => {\n const options = parseWizardCli(argv);\n if (argv.includes(\"--help\") || argv.includes(\"-h\")) {\n console.log(WIZARD_HELP);\n return 0;\n }\n ensureLoopbackNoProxy();\n\n // pnpm/npm run scripts execute with the package directory as cwd; INIT_CWD\n // preserves the directory the user actually invoked the command from.\n // The positional argument selects the project directory explicitly; without\n // it, projects default to ~/.opentray/create/<name> (stable per app).\n const invocationDir = process.env.INIT_CWD || process.cwd();\n const cwd = invocationDir;\n const dependencyRange = await readDependencyRange();\n\n const server = await createWizardServer(\n (emit) =>\n createWizardSession({\n cwd,\n skipInstall: options.skipInstall,\n force: options.force,\n ...(options.targetDir === undefined\n ? {}\n : { targetDir: resolve(invocationDir, options.targetDir) }),\n ...(options.pm === undefined ? {} : { packageManager: options.pm }),\n dependencyRange,\n emit,\n }),\n options.port === undefined ? {} : { port: options.port },\n );\n\n console.log(`create-opentray wizard: ${server.url}`);\n console.log(`working directory: ${cwd}`);\n\n if (options.open) {\n await openBrowser(server.url);\n }\n\n const shutdown = (): void => {\n void (async () => {\n await server.session.stop();\n await server.close();\n process.exit(0);\n })();\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n\n // Keep the process alive until the wizard is closed or both the server\n // socket and the preview run are gone.\n await new Promise<void>(() => {});\n return 0;\n};\n\nconst isMainModule = (): boolean => {\n // The bin entry is the module Node loaded as the main script (argv[1]).\n // tsdown bundles this file into the bin chunk, and argv[1] points at the\n // bin.mjs re-export, so also accept that adjacency: when this module is\n // imported through the bin entry rather than the library entry.\n // Path comparisons use realpath: npx/npm invoke the bin through a\n // node_modules/.bin SYMLINK, and resolve() alone keeps the link path —\n // argv[1] would never equal the real module file and main() silently\n // never ran (npx create-opentray exited 0 with no output).\n const entryPath = process.argv[1];\n if (entryPath === undefined) return false;\n const sameFile = (a: string, b: string): boolean => {\n try {\n return realpathSync(resolve(a)) === realpathSync(resolve(b));\n } catch {\n return resolve(a) === resolve(b);\n }\n };\n const modulePath = fileURLToPath(import.meta.url);\n if (sameFile(entryPath, modulePath)) return true;\n return sameFile(entryPath, modulePath.replace(/bin-[^/]*\\.mjs$/u, \"bin.mjs\"));\n};\n\nif (isMainModule()) {\n void main(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"],"mappings":"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"}