diff --git a/AGENTS.md b/AGENTS.md index 4f1015f..b0bb2e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,36 +1,26 @@ # lmc -TypeScript CLI tool for streaming LLM chat with inline markdown rendering. +Bun CLI tool for streaming LLM chat with inline markdown rendering. ## Commands ``` -pnpm run dev # dev run (rolldown bundle + node) -pnpm run check # biome lint + format + organize imports + typecheck -pnpm run build # check + bundle index.ts and tts.ts (minified ESM) -pnpm run build:bin # check + bundle with esbuild (CJS) + build SEA binaries -pnpm run clean # remove dist/ +bun run dev # dev run +bun run prod # run bundled output +bun run check # biome lint + format + organize imports ``` ## Architecture -- `src/index.ts` — LMC CLI entry point, orchestration -- `src/tts.ts` — TTSC CLI entry point (text-to-speech, Kokoro pipeline via `uv run`) -- `src/config.ts` — `initConfig()`, `loadConfig()`: manages `~/.lmc_config.json` -- `src/conversation.ts` — `Conversation` class: load/save to `~/.lmc_convo.json` -- `src/errors.ts` — `handleTopLevel()`: TTY-aware error wrapping -- `src/prompts.ts` — `commitDiff()`, `reviewFile()`, `reviewDiff()`, `file()`: prompt builders +- `index.ts` — CLI entry point, orchestration - `src/render/index.ts` — `Renderer` class: streams LLM response, buffers markdown, renders to stdout/stderr -- `src/render/markdown.ts` — `renderMarkdown()`: markdown via `marked` + Shiki + `@cliffy/table` -- `src/render/codeToANSI.ts` — Shiki token output to ANSI escape sequences +- `src/render/markdown.ts` — `renderMarkdown()`: syntax-highlighted markdown via `@comark/ansi` + Shiki ## Gotchas -- **Node.js** — Use modern Node APIs (`node:fs/promises`, `node:stream/consumers`, etc.). +- **Bun only** — not Node. Use `Bun.file()`, `Bun.argv`, `Bun.sleep()`. - **No tests** — none exist. -- **Biome config**: double quotes, space indent, auto-import-organize on. `pnpm run check` also runs `tsc --noEmit`. -- **Build tools**: rolldown for ESM bundles, esbuild for CJS binaries, `node --build-sea` for SEA single-executable apps. -- **TTS**: requires Python with `uv` — Kokoro pipeline lives in `tts/`. +- **Biome config**: double quotes, space indent, auto-import-organize on. Run `bun run check` before committing. ## Style diff --git a/README.md b/README.md index d8ddfa9..7c56c4c 100644 --- a/README.md +++ b/README.md @@ -1,130 +1,15 @@ # lmc -A CLI tool for LLM chats with terminal native streaming. - -## Features - -- **Streaming markdown rendering** — incremental rendering with syntax highlighting -- **Conversation persistence** — chat history saved to JSON -- **Model configuration** — persist model preferences to JSON -- **Reasoning display** — thinking tokens rendered _in style_ on stderr -- **Usage stats** — input/output tokens and tokens/sec rate after each response -- **TTY-aware** — automatically disables markdown rendering when output is piped -- **Text-to-speech** — `ttsc` sub-command for streaming TTS via Kokoro model - -## Requirements - -- [pnpm](https://pnpm.io) v11+ -- [Node.js](https://nodejs.org) v22+ -- [uv](https://docs.astral.sh/uv/) — only for `ttsc` (text-to-speech) - -## Installation +To install dependencies: ```bash -# Bundle and install locally -pnpm build -pnpm add --global . - -# Build a single-file exe -pnpm build:bin -cp dist/lmc dist/ttsc /somewhere/bin/ - -# For ttsc only -cp -r tts ~/.ttsc +bun install ``` -_Package to come later_ - -## Usage - -### Chat (lmc) +To run: ```bash -lmc [options] +bun run index.ts ``` -| Flag | Short | Type | Description | -| ------------ | ----- | ------- | ----------------------------------------------------- | -| `--new` | `-n` | boolean | Start a new conversation (clear history) | -| `--model` | `-m` | string | Set model name for this session | -| `--config` | `-c` | boolean | Initialize a new config file (`~/.lmc_config.json`) | -| `--plain` | — | boolean | Disable markdown rendering for both stdout and stderr | -| `--plainOut` | — | boolean | Disable markdown rendering for stdout only | -| `--plainErr` | — | boolean | Disable markdown rendering for stderr only | - -**Examples:** - -```bash -lmc --config # Initialize/edit config -lmc --new "Introduce yourself." # New conversation -lmc "What is TypeScript?" # Continue most recent chat -lmc -m llama3 "Explain quantum computing" # Chat with specific model on-the-fly -lmc --plain "Plain text output" # No markdown rendering -``` - -### Text-to-Speech (ttsc) - -```bash -echo "text" | ttsc [options] -``` - -| Flag | Short | Type | Description | -| -------- | ----- | ------- | -------------------------------------------------------- | -| `--wait` | `-w` | boolean | Wait for all processing before exiting (don't auto-play) | -| `--url` | `-u` | boolean | Fetch text from a URL instead of stdin | -| `--edit` | `-e` | boolean | Edit the input with your $EDITOR before processing | - -**Examples:** - -```bash -echo "Hello, world!" | ttsc -echo "Read this aloud" | ttsc --wait -echo "https://example.com" | ttsc --url -``` - -Requires: [uv](https://docs.astral.sh/uv/) - -Wants: - -- `mpv` for playback -- `sox` for wav merging with `--wait` - -## Development - -```bash -pnpm run dev # dev run -pnpm run check # biome lint + format + organize imports -``` - -## Architecture - -``` -index.ts — LMC entry point, orchestration -tts.ts — TTSC entry point, orchestration -src/render/index.ts — Renderer class: streams LLM response, buffers markdown -src/render/markdown.ts — renderMarkdown(): syntax-highlighted markdown via Marked + Shiki -src/conversation/ — Conversation persistence -src/config/ — App config persistence -tts/ — TTS python stub (Kokoro pipeline) -out/ — Bundled output (index.js, tts.js) -``` - -## Configuration - -- **Conversation history:** `~/.lmc_convo.json` -- **Model config:** `~/.lmc_config.json` -- **TTS files:** `~/.ttsc/` - -## Dependencies - -| Package | Purpose | -| ---------------------- | ------------------------------- | -| `ai` (Vercel AI SDK) | Streaming text/usage | -| `@clack/prompts` | Interactive prompts and spinner | -| `@cliffy/table` | ASCII table rendering | -| `@mozilla/readability` | HTML-to-text extraction (ttsc) | -| `chalk` | Terminal string styling | -| `happy-dom` | DOM parsing for URLs (ttsc) | -| `shiki` | Syntax highlighting | -| `terminal-link` | Clickable terminal hyperlinks | -| `marked` | Markdown parsing | +This project was created using `bun init` in bun v1.3.11. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..6b1f630 --- /dev/null +++ b/bun.lock @@ -0,0 +1,196 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "aicl", + "dependencies": { + "@ai-sdk/openai-compatible": "^2.0.41", + "ai": "^6.0.161", + "ansi-styles": "^6.2.3", + "chalk": "^5.6.2", + "cli-table3": "^0.6.5", + "shiki": "^4.0.2", + "terminal-link": "^5.0.0", + }, + "devDependencies": { + "@biomejs/biome": "2.4.12", + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.98", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Ol+nP8PIlj8FjN8qKlxhE89N0woqAaGi9CUBGp1boe3RafpphJ7WMuq/RErSvxtwTqje03TP+zIdzP113krxRg=="], + + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + + "@biomejs/biome": ["@biomejs/biome@2.4.12", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.12", "@biomejs/cli-darwin-x64": "2.4.12", "@biomejs/cli-linux-arm64": "2.4.12", "@biomejs/cli-linux-arm64-musl": "2.4.12", "@biomejs/cli-linux-x64": "2.4.12", "@biomejs/cli-linux-x64-musl": "2.4.12", "@biomejs/cli-win32-arm64": "2.4.12", "@biomejs/cli-win32-x64": "2.4.12" }, "bin": { "biome": "bin/biome" } }, "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.12", "", { "os": "win32", "cpu": "x64" }, "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA=="], + + "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="], + + "@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="], + + "@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="], + + "@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + + "ai": ["ai@6.0.161", "", { "dependencies": { "@ai-sdk/gateway": "3.0.98", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ufhmijmx2YyWTPAicGgtpLOB/xD7mG8zKs1pT1Trj+JL/3r1rS8fkMi/cHZoChSAQSGB4pgmcWVxDrVTUvK2IQ=="], + + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + + "supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="], + + "terminal-link": ["terminal-link@5.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^4.1.0" } }, "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..e30be42 --- /dev/null +++ b/index.ts @@ -0,0 +1,93 @@ +import { parseArgs } from "node:util"; +import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; +import { streamText } from "ai"; + +import { loadConfig, saveConfig } from "./src/config"; +import { + type Conversation, + loadConversation, + saveConversation, +} from "./src/conversation"; +import Renderer from "./src/render"; + +const { values: args, positionals } = parseArgs({ + args: Bun.argv, + options: { + model: { type: "string" }, + m: { type: "string" }, + new: { type: "boolean" }, + n: { type: "boolean" }, + save: { type: "boolean" }, + s: { type: "boolean" }, + plain: { type: "boolean" }, + plainOut: { type: "boolean" }, + plainErr: { type: "boolean" }, + }, + strict: true, + allowPositionals: true, +}); + +async function main() { + const newConvo = args.new ?? args.n; + const plainOut = args.plainOut ?? args.plain ?? !process.stdout.isTTY; + const plainErr = args.plainErr ?? args.plain ?? !process.stderr.isTTY; + const modelFlag = args.model ?? args.m; + const saveConfigFlag = args.save ?? args.s; + + const config = await loadConfig(); + + config.model = modelFlag ?? config.model; + + if (!config.model) { + console.error( + "Please run 'lmc --model --save' to set a default model.", + ); + process.exit(1); + } + + if (saveConfigFlag) { + await saveConfig(config); + return; + } + + const renderer = new Renderer(plainOut, plainErr); + + const provider = createOpenAICompatible({ + name: "llama.cpp", + apiKey: "local", + baseURL: "http://127.0.0.1:8080/v1", + includeUsage: true, // Include usage information in streaming responses + }); + + let convo: Conversation = { + model: config.model, + messages: [], + }; + + if (!newConvo) { + convo = await loadConversation(config.model); + + if (args.model || args.m) { + convo.model = modelFlag ?? config.model; + } + } + + const prompt = positionals.slice(2).join(" ") || "Introduce yourself."; + convo.messages.push({ role: "user", content: prompt }); + + const { fullStream, text, totalUsage } = streamText({ + model: provider(convo.model), + messages: convo.messages, + }); + + const start = Date.now(); + await renderer.renderStream(fullStream); + const stop = Date.now(); + + convo.messages.push({ role: "assistant", content: await text }); + await saveConversation(convo); + + await renderer.renderStats(totalUsage, start, stop); +} + +await main(); diff --git a/package.json b/package.json index b27a415..85b43af 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,38 @@ { "name": "lmc", + "module": "index.ts", "type": "module", "private": true, "bin": { - "lmc": "dist/index.js", - "ttsc": "dist/tts.js" + "lmc": "out/index.js", + "ttsc": "out/tts.js" }, "scripts": { - "dev": "pnpm run bundle src/index.ts >/dev/null && node dist/index.js", - "check": "biome check --write && pnpm run tc", - "clean": "rm -rf dist", - "build": "pnpm run check && pnpm run bundle --minify src/index.ts && pnpm run bundle --minify src/tts.ts", - "build:bin": "pnpm run check && pnpm run bundle:bin --minify src/index.ts && pnpm run bundle:bin --minify src/tts.ts && pnpm run bin", - "bundle": "rolldown --dir=dist --platform=node --transform.target=node22 --format=esm", - "bundle:bin": "esbuild --bundle --outdir=dist --platform=node --target=node26 --format=cjs", - "bin": "node --build-sea sea-config.json && node --build-sea sea-config-tts.json", + "dev": "bun run index.ts", + "prod": "bun run out/index.js", + "check": "bunx --bun @biomejs/biome check --write && bun run tc", + "build:js": "bun run check && bun run --parallel build:bundle build:tts && bun run --parallel shebang shebang:tts", + "build:bundle": "bun build --target=bun --production --sourcemap --outdir=out index.ts", + "build:tts": "bun build --target=bun --production --sourcemap --outdir=out tts.ts", + "build:bin": "bun build --compile --outfile=out/lmc index.ts", + "shebang": "sed -i '1i#!\\/usr\\/bin\\/env bun' out/index.js", + "shebang:tts": "sed -i '1i#!\\/usr\\/bin\\/env bun' out/tts.js", "tc": "tsc --noEmit --incremental" }, "devDependencies": { - "@ai-sdk/openai-compatible": "^2.0.50", "@biomejs/biome": "2.4.12", - "@clack/prompts": "^1.5.1", - "@cliffy/table": "jsr:^1.2.1", - "@mozilla/readability": "^0.6.0", - "@types/node": "^25.9.3", - "ai": "^6.0.206", + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@ai-sdk/openai-compatible": "^2.0.41", + "ai": "^6.0.161", "ansi-styles": "^6.2.3", "chalk": "^5.6.2", - "esbuild": "^0.28.1", - "execa": "^9.6.1", - "happy-dom": "^20.10.3", - "marked": "^18.0.5", - "rolldown": "^1.1.1", - "shiki": "^4.2.0", - "string-width": "^8.2.1", - "terminal-link": "^5.0.0", - "typescript": "^5.9.3", - "wrap-ansi": "^10.0.0" + "cli-table3": "^0.6.5", + "shiki": "^4.0.2", + "terminal-link": "^5.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index d46eb49..0000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,1504 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - '@ai-sdk/openai-compatible': - specifier: ^2.0.50 - version: 2.0.50(zod@4.4.3) - '@biomejs/biome': - specifier: 2.4.12 - version: 2.4.12 - '@clack/prompts': - specifier: ^1.5.1 - version: 1.5.1 - '@cliffy/table': - specifier: jsr:^1.2.1 - version: '@jsr/cliffy__table@1.2.1' - '@mozilla/readability': - specifier: ^0.6.0 - version: 0.6.0 - '@types/node': - specifier: ^25.9.3 - version: 25.9.3 - ai: - specifier: ^6.0.206 - version: 6.0.206(zod@4.4.3) - ansi-styles: - specifier: ^6.2.3 - version: 6.2.3 - chalk: - specifier: ^5.6.2 - version: 5.6.2 - esbuild: - specifier: ^0.28.1 - version: 0.28.1 - execa: - specifier: ^9.6.1 - version: 9.6.1 - happy-dom: - specifier: ^20.10.3 - version: 20.10.3 - marked: - specifier: ^18.0.5 - version: 18.0.5 - rolldown: - specifier: ^1.1.1 - version: 1.1.1 - shiki: - specifier: ^4.2.0 - version: 4.2.0 - string-width: - specifier: ^8.2.1 - version: 8.2.1 - terminal-link: - specifier: ^5.0.0 - version: 5.0.0 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - wrap-ansi: - specifier: ^10.0.0 - version: 10.0.0 - -packages: - - '@ai-sdk/gateway@3.0.132': - resolution: {integrity: sha512-sFZFGk6aVSNKmgDrb14efaAbvM71AB3UUvaTsU+bvhWP9jrkRrwix/jFX8IoOAJk0/X7Xf7p3m0J2O2G6ljZbA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/openai-compatible@2.0.50': - resolution: {integrity: sha512-HyuxddF2Yv5G8qxK/0uksAINjQ4h6TpwOqHuqzsCM0u78/JWAW2OXcIplQeB44PIAORgPjbMzrw9DhnPYHMskA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider-utils@4.0.29': - resolution: {integrity: sha512-uhukHaCBvqkwBHkT8C2PrnqKTCoLn3pdHXqtcR9I8ErH+flbzgW4o7VHSNIup9LRu+WBvZIZDQLsx6rwl2tiOA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider@3.0.10': - resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==} - engines: {node: '>=18'} - - '@biomejs/biome@2.4.12': - resolution: {integrity: sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.4.12': - resolution: {integrity: sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.4.12': - resolution: {integrity: sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.4.12': - resolution: {integrity: sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-arm64@2.4.12': - resolution: {integrity: sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-linux-x64-musl@2.4.12': - resolution: {integrity: sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-x64@2.4.12': - resolution: {integrity: sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-win32-arm64@2.4.12': - resolution: {integrity: sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.4.12': - resolution: {integrity: sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@clack/core@1.4.1': - resolution: {integrity: sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==} - engines: {node: '>= 20.12.0'} - - '@clack/prompts@1.5.1': - resolution: {integrity: sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==} - engines: {node: '>= 20.12.0'} - - '@emnapi/core@1.11.0': - resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} - - '@emnapi/runtime@1.11.0': - resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} - - '@emnapi/wasi-threads@1.2.2': - resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} - - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@jsr/cliffy__table@1.2.1': - resolution: {integrity: sha512-C/edvcMwtnbFx0zyoeGRcC+ZRvGYqxEhRi19HpfbfiYS4ZErKjEwMJmavO/DN7Wxbk34UpJP+UQKL7nKe+dYkA==, tarball: https://npm.jsr.io/~/11/@jsr/cliffy__table/1.2.1.tgz} - - '@jsr/std__fmt@1.0.10': - resolution: {integrity: sha512-Q2N8XDNkh/c1IKTi+sO4Vm64M5m4W0o47qTURmMX0vgHm9AJ6jL7jEQ2kw0M1T2rBZiwAY78eUbYzFyOYWaCqQ==, tarball: https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.10.tgz} - - '@mozilla/readability@0.6.0': - resolution: {integrity: sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==} - engines: {node: '>=14.0.0'} - - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - - '@opentelemetry/api@1.9.1': - resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} - engines: {node: '>=8.0.0'} - - '@oxc-project/types@0.135.0': - resolution: {integrity: sha512-wR+xRdFkUBMvcAjBJ2q2kcZM6d+DKu2NgoOyxZgYwZdLhmiv6+rnO8PZ/P68kMiZtIKm+pW7zyEJ4kSOs0vo+Q==} - - '@rolldown/binding-android-arm64@1.1.1': - resolution: {integrity: sha512-BLf9Wak/gfwVb7NQTQW4wBgL3oAfPy7ArEkhwV543OVw/uY6B47z5xYsqPSZ9PDOorvURPinws6ThaFuNgGLgA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.1.1': - resolution: {integrity: sha512-rRZRPy/Ynb+Mxu0O6tfPldHeDgAn0sRij+IOUy6sFdUlv3hArGW/DloE3GfAxtqpOJuRNgF74Nr5gM4xBeU2jQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.1.1': - resolution: {integrity: sha512-/MtefPxhKPyWWFM8L45OWiEqRf+eSU2Qv9ZAyTaoZOoGcoPKxbbhjTJO2/U2IThv0uDZ4NWHc3/oTsR6IEOtww==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.1.1': - resolution: {integrity: sha512-202K+cpIi1kx/Zn7AtxBi4LTXSY67Aszb2K9rNsuW7FeBeh0nqoNmYLOSZidV0p88VPBzMmTZcHAdPNo3kRYzQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.1.1': - resolution: {integrity: sha512-wl9NfeXNUwrXtUc063tddmZFUI6qiNs1CNOwni0OL4vC7MqVSYugra3ZgtDmtVy8e0DluJTENmzIv2BwqLzT4Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.1.1': - resolution: {integrity: sha512-at2EO4o7D/PJLC4Xik16bU4CcjQE2tSv1LfqMA0TRYQYQihRm3gZeDB8xaX28A9SFedibcAk5DeMCKt4REKG0A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-musl@1.1.1': - resolution: {integrity: sha512-5PUjZx366h9tkJTPJF5eibxOlK3sGoeRiBJLLjjEB5/kLDuhr6qB3LkhqLz1smXNgsX+pBhnbcJBrPE30HznAA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-ppc64-gnu@1.1.1': - resolution: {integrity: sha512-1WK84XPeio3tjP1sM/TMXiC0G1i1iq1qGZ71KfNQjEFLU1kwD+Cv5T8nGySg/JUFwLbaScu6ve9DmeXlmqpkFA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-s390x-gnu@1.1.1': - resolution: {integrity: sha512-1nS1X5z1uMJ369RU25hTpKCFvUwXZp12dIzlzk4S+UxCTcSVGsAE6tzkOSufv/7jnmAtK0ZlrsJxh2fGmsnVSw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-gnu@1.1.1': - resolution: {integrity: sha512-NwX/wspnq4vYyMFsqbYvzums3ki/Tk8FZbMzMAovPDp3OfLeYKby/D+9osokadXuYEV3OvpeHlwnr/bG8QMixA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-musl@1.1.1': - resolution: {integrity: sha512-+n46LhDrJFQM+229y4oXtVpj1G50U/+XuHMlpnisFTEXhrg9f/YIjp/HymX+PVJjBEr7XHRs3CFLelV464pqwA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-openharmony-arm64@1.1.1': - resolution: {integrity: sha512-qGwEu47zOWYo7LdRHhCWTNhzwGtxXpdY6CERs8QEOqC0PXGGics/e3vHnyEUKt8xK6YkbZXFUCeklrpB6js8ag==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-wasm32-wasi@1.1.1': - resolution: {integrity: sha512-qczfgEH8u0wHGGOXtA7UMAybNKuQjjEXairyQaw4WzjiMztfbgatG1h4OKays/smhtwbWltpKCRGtVhU6h40Sg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.1.1': - resolution: {integrity: sha512-4psXSh63mSbwJF+mB8/9yfUUEzBiHYcUjxa32EO9ZwKy0Ypwjcg4F10D8SvVXgd+isy2UUUjF9HJJnDu1T/4Gg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.1.1': - resolution: {integrity: sha512-MUvC/HLXVjzkQkWiExdVTEEWf0py+GfWm8WKSZsekG3ih6a21iy0BHPF07X3JIf3ifoklZXTIaHTLPBgH1C3dw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@shikijs/core@4.2.0': - resolution: {integrity: sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==} - engines: {node: '>=20'} - - '@shikijs/engine-javascript@4.2.0': - resolution: {integrity: sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==} - engines: {node: '>=20'} - - '@shikijs/engine-oniguruma@4.2.0': - resolution: {integrity: sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==} - engines: {node: '>=20'} - - '@shikijs/langs@4.2.0': - resolution: {integrity: sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==} - engines: {node: '>=20'} - - '@shikijs/primitive@4.2.0': - resolution: {integrity: sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==} - engines: {node: '>=20'} - - '@shikijs/themes@4.2.0': - resolution: {integrity: sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==} - engines: {node: '>=20'} - - '@shikijs/types@4.2.0': - resolution: {integrity: sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==} - engines: {node: '>=20'} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@types/whatwg-mimetype@3.0.2': - resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - - '@ungap/structured-clone@1.3.1': - resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - - '@vercel/oidc@3.2.0': - resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} - engines: {node: '>= 20'} - - ai@6.0.206: - resolution: {integrity: sha512-hVdB5PozMMxmkPBfbCxkFOn0eVeCMi/imkfPvk65cxL4O8oIrvjUxDUX8dGkdkWG1No+R7e7D9ti2DHO61Z4YQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - buffer-image-size@0.6.4: - resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} - engines: {node: '>=4.0'} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - - eventsource-parser@3.1.0: - resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} - engines: {node: '>=18.0.0'} - - execa@9.6.1: - resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} - engines: {node: ^18.19.0 || >=20.5.0} - - fast-string-truncated-width@3.0.3: - resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} - - fast-string-width@3.0.2: - resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - - fast-wrap-ansi@0.2.2: - resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - - happy-dom@20.10.3: - resolution: {integrity: sha512-Hjdiy8RziuCcn5z04QI/rlsNuQoG8P0xxjgvsSMpi89cvIXIOcucQtiHS1yHSShxoBcSCeYqAskINmTiy/mlfw==} - engines: {node: '>=20.0.0'} - - has-flag@5.0.1: - resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} - engines: {node: '>=12'} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} - engines: {node: '>=18.18.0'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - marked@18.0.5: - resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} - engines: {node: '>= 20'} - hasBin: true - - mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - - oniguruma-parser@0.12.2: - resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} - - oniguruma-to-es@4.3.6: - resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - pretty-ms@9.3.0: - resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} - engines: {node: '>=18'} - - property-information@7.2.0: - resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - - rolldown@1.1.1: - resolution: {integrity: sha512-IN750c0p+s3jqJIsFLRZrQazmbAB1kkQDTtQjSt/gbS2ywLhlv4R5Shazer0FZKmuo/BsO3/w2UoYnUjuOZqHg==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki@4.2.0: - resolution: {integrity: sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==} - engines: {node: '>=20'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - string-width@8.2.1: - resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} - engines: {node: '>=20'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - - supports-hyperlinks@4.4.0: - resolution: {integrity: sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==} - engines: {node: '>=20'} - - terminal-link@5.0.0: - resolution: {integrity: sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==} - engines: {node: '>=20'} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wrap-ansi@10.0.0: - resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} - engines: {node: '>=20'} - - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} - engines: {node: '>=18'} - - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@ai-sdk/gateway@3.0.132(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) - '@vercel/oidc': 3.2.0 - zod: 4.4.3 - - '@ai-sdk/openai-compatible@2.0.50(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) - zod: 4.4.3 - - '@ai-sdk/provider-utils@4.0.29(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.1.0 - zod: 4.4.3 - - '@ai-sdk/provider@3.0.10': - dependencies: - json-schema: 0.4.0 - - '@biomejs/biome@2.4.12': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.12 - '@biomejs/cli-darwin-x64': 2.4.12 - '@biomejs/cli-linux-arm64': 2.4.12 - '@biomejs/cli-linux-arm64-musl': 2.4.12 - '@biomejs/cli-linux-x64': 2.4.12 - '@biomejs/cli-linux-x64-musl': 2.4.12 - '@biomejs/cli-win32-arm64': 2.4.12 - '@biomejs/cli-win32-x64': 2.4.12 - - '@biomejs/cli-darwin-arm64@2.4.12': - optional: true - - '@biomejs/cli-darwin-x64@2.4.12': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.4.12': - optional: true - - '@biomejs/cli-linux-arm64@2.4.12': - optional: true - - '@biomejs/cli-linux-x64-musl@2.4.12': - optional: true - - '@biomejs/cli-linux-x64@2.4.12': - optional: true - - '@biomejs/cli-win32-arm64@2.4.12': - optional: true - - '@biomejs/cli-win32-x64@2.4.12': - optional: true - - '@clack/core@1.4.1': - dependencies: - fast-wrap-ansi: 0.2.2 - sisteransi: 1.0.5 - - '@clack/prompts@1.5.1': - dependencies: - '@clack/core': 1.4.1 - fast-string-width: 3.0.2 - fast-wrap-ansi: 0.2.2 - sisteransi: 1.0.5 - - '@emnapi/core@1.11.0': - dependencies: - '@emnapi/wasi-threads': 1.2.2 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.11.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.2': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - - '@jsr/cliffy__table@1.2.1': - dependencies: - '@jsr/std__fmt': 1.0.10 - - '@jsr/std__fmt@1.0.10': {} - - '@mozilla/readability@0.6.0': {} - - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': - dependencies: - '@emnapi/core': 1.11.0 - '@emnapi/runtime': 1.11.0 - '@tybys/wasm-util': 0.10.2 - optional: true - - '@opentelemetry/api@1.9.1': {} - - '@oxc-project/types@0.135.0': {} - - '@rolldown/binding-android-arm64@1.1.1': - optional: true - - '@rolldown/binding-darwin-arm64@1.1.1': - optional: true - - '@rolldown/binding-darwin-x64@1.1.1': - optional: true - - '@rolldown/binding-freebsd-x64@1.1.1': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.1.1': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.1.1': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.1.1': - optional: true - - '@rolldown/binding-linux-ppc64-gnu@1.1.1': - optional: true - - '@rolldown/binding-linux-s390x-gnu@1.1.1': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.1.1': - optional: true - - '@rolldown/binding-linux-x64-musl@1.1.1': - optional: true - - '@rolldown/binding-openharmony-arm64@1.1.1': - optional: true - - '@rolldown/binding-wasm32-wasi@1.1.1': - dependencies: - '@emnapi/core': 1.11.0 - '@emnapi/runtime': 1.11.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.1.1': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.1.1': - optional: true - - '@rolldown/pluginutils@1.0.1': {} - - '@sec-ant/readable-stream@0.4.1': {} - - '@shikijs/core@4.2.0': - dependencies: - '@shikijs/primitive': 4.2.0 - '@shikijs/types': 4.2.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@4.2.0': - dependencies: - '@shikijs/types': 4.2.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.6 - - '@shikijs/engine-oniguruma@4.2.0': - dependencies: - '@shikijs/types': 4.2.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@4.2.0': - dependencies: - '@shikijs/types': 4.2.0 - - '@shikijs/primitive@4.2.0': - dependencies: - '@shikijs/types': 4.2.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/themes@4.2.0': - dependencies: - '@shikijs/types': 4.2.0 - - '@shikijs/types@4.2.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@sindresorhus/merge-streams@4.0.0': {} - - '@standard-schema/spec@1.1.0': {} - - '@tybys/wasm-util@0.10.2': - dependencies: - tslib: 2.8.1 - optional: true - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/node@25.9.3': - dependencies: - undici-types: 7.24.6 - - '@types/unist@3.0.3': {} - - '@types/whatwg-mimetype@3.0.2': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 25.9.3 - - '@ungap/structured-clone@1.3.1': {} - - '@vercel/oidc@3.2.0': {} - - ai@6.0.206(zod@4.4.3): - dependencies: - '@ai-sdk/gateway': 3.0.132(zod@4.4.3) - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) - '@opentelemetry/api': 1.9.1 - zod: 4.4.3 - - ansi-escapes@7.3.0: - dependencies: - environment: 1.1.0 - - ansi-regex@6.2.2: {} - - ansi-styles@6.2.3: {} - - buffer-image-size@0.6.4: - dependencies: - '@types/node': 25.9.3 - - ccount@2.0.1: {} - - chalk@5.6.2: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - comma-separated-tokens@2.0.3: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - dequal@2.0.3: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - entities@7.0.1: {} - - environment@1.1.0: {} - - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - - eventsource-parser@3.1.0: {} - - execa@9.6.1: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.3.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.2 - - fast-string-truncated-width@3.0.3: {} - - fast-string-width@3.0.2: - dependencies: - fast-string-truncated-width: 3.0.3 - - fast-wrap-ansi@0.2.2: - dependencies: - fast-string-width: 3.0.2 - - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 - - get-east-asian-width@1.6.0: {} - - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - - happy-dom@20.10.3: - dependencies: - '@types/node': 25.9.3 - '@types/whatwg-mimetype': 3.0.2 - '@types/ws': 8.18.1 - buffer-image-size: 0.6.4 - entities: 7.0.1 - whatwg-mimetype: 3.0.0 - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - has-flag@5.0.1: {} - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.2.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - html-void-elements@3.0.0: {} - - human-signals@8.0.1: {} - - is-plain-obj@4.1.0: {} - - is-stream@4.0.1: {} - - is-unicode-supported@2.1.0: {} - - isexe@2.0.0: {} - - json-schema@0.4.0: {} - - marked@18.0.5: {} - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.1 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - oniguruma-parser@0.12.2: {} - - oniguruma-to-es@4.3.6: - dependencies: - oniguruma-parser: 0.12.2 - regex: 6.1.0 - regex-recursion: 6.0.2 - - parse-ms@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - pretty-ms@9.3.0: - dependencies: - parse-ms: 4.0.0 - - property-information@7.2.0: {} - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - rolldown@1.1.1: - dependencies: - '@oxc-project/types': 0.135.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.1 - '@rolldown/binding-darwin-arm64': 1.1.1 - '@rolldown/binding-darwin-x64': 1.1.1 - '@rolldown/binding-freebsd-x64': 1.1.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.1 - '@rolldown/binding-linux-arm64-gnu': 1.1.1 - '@rolldown/binding-linux-arm64-musl': 1.1.1 - '@rolldown/binding-linux-ppc64-gnu': 1.1.1 - '@rolldown/binding-linux-s390x-gnu': 1.1.1 - '@rolldown/binding-linux-x64-gnu': 1.1.1 - '@rolldown/binding-linux-x64-musl': 1.1.1 - '@rolldown/binding-openharmony-arm64': 1.1.1 - '@rolldown/binding-wasm32-wasi': 1.1.1 - '@rolldown/binding-win32-arm64-msvc': 1.1.1 - '@rolldown/binding-win32-x64-msvc': 1.1.1 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki@4.2.0: - dependencies: - '@shikijs/core': 4.2.0 - '@shikijs/engine-javascript': 4.2.0 - '@shikijs/engine-oniguruma': 4.2.0 - '@shikijs/langs': 4.2.0 - '@shikijs/themes': 4.2.0 - '@shikijs/types': 4.2.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - signal-exit@4.1.0: {} - - sisteransi@1.0.5: {} - - space-separated-tokens@2.0.2: {} - - string-width@8.2.1: - dependencies: - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.2.2 - - strip-final-newline@4.0.0: {} - - supports-color@10.2.2: {} - - supports-hyperlinks@4.4.0: - dependencies: - has-flag: 5.0.1 - supports-color: 10.2.2 - - terminal-link@5.0.0: - dependencies: - ansi-escapes: 7.3.0 - supports-hyperlinks: 4.4.0 - - trim-lines@3.0.1: {} - - tslib@2.8.1: - optional: true - - typescript@5.9.3: {} - - undici-types@7.24.6: {} - - unicorn-magic@0.3.0: {} - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - whatwg-mimetype@3.0.0: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wrap-ansi@10.0.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 8.2.1 - strip-ansi: 7.2.0 - - ws@8.21.0: {} - - yoctocolors@2.1.2: {} - - zod@4.4.3: {} - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 8ad854b..0000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,3 +0,0 @@ -minimumReleaseAge: 4320 -allowBuilds: - esbuild: true diff --git a/sea-config-tts.json b/sea-config-tts.json deleted file mode 100644 index 1f147f7..0000000 --- a/sea-config-tts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "main": "dist/tts.js", - "output": "dist/ttsc", - "useCodeCache": true, - "disableExperimentalSEAWarning": true -} diff --git a/sea-config.json b/sea-config.json deleted file mode 100644 index 4beec69..0000000 --- a/sea-config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "main": "dist/index.js", - "output": "dist/lmc", - "useCodeCache": true, - "disableExperimentalSEAWarning": true -} diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index f885994..0000000 --- a/src/config.ts +++ /dev/null @@ -1,79 +0,0 @@ -import fs from "node:fs/promises"; -import os from "node:os"; -import { autocomplete, intro, isCancel, outro, text } from "@clack/prompts"; -import chalk from "chalk"; - -const home = os.homedir(); -const configPath = `${home}/.lmc_config.json`; - -export interface AppConfig { - baseURL: string; - model: string; -} - -export async function initConfig(old: AppConfig | null): Promise { - console.log(); - intro(chalk.blue("Welcome to LMC. Let's make a new config!")); - - const baseURL = await text({ - message: "What's your base URL:", - defaultValue: old?.baseURL ?? "http://127.0.0.1:8080/v1", - placeholder: old?.baseURL ?? "http://127.0.0.1:8080/v1", - }); - if (isCancel(baseURL)) process.exit(1); - - const models = await getModels(baseURL); - const initialValue = models.includes(old?.model ?? "") - ? old?.model - : models[0]; - const model = await autocomplete({ - message: "Choose a default model:", - initialValue, - placeholder: initialValue ?? "Start typing...", - options: models.map((model) => ({ value: model })), - validate: (value) => - models.includes(typeof value === "string" ? value : "") - ? undefined - : `Unable to find model "${value}"`, - }); - if (!model || isCancel(model)) process.exit(1); - - const config = { baseURL, model } satisfies AppConfig; - - await fs.writeFile(configPath, JSON.stringify(config, null, " ")); - - outro(chalk.green("Configuration saved!")); - - return config; -} - -type Models = { - data: { - created: number; - id: string; - object: "model"; - owned_by: string; - }[]; -}; - -async function getModels(url: string) { - const response = await fetch(`${url}/models`); - const models = (await response.json()) as Models; - return models.data.map((model) => model.id); -} - -export async function loadConfig( - configFlag: boolean | undefined, -): Promise { - let config: AppConfig; - try { - const text = await fs.readFile(configPath, "utf8"); - config = JSON.parse(text); - } catch { - if (configFlag) return null; - console.error(`Config not found at ${configPath}`); - return null; - } - - return config; -} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..8281bf7 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,24 @@ +import os from "node:os"; +import chalk from "chalk"; + +const home = os.homedir(); +const configFile = Bun.file(`${home}/.lmc_config.json`); + +export interface AppConfig { + model?: string; +} + +export async function loadConfig(): Promise { + let config: AppConfig; + try { + config = await configFile.json(); + } catch { + config = {}; + } + return config; +} + +export async function saveConfig(config: AppConfig): Promise { + await configFile.write(JSON.stringify(config, null, " ")); + console.log(chalk.green("Configuration saved!")); +} diff --git a/src/conversation.ts b/src/conversation.ts deleted file mode 100644 index 96b1923..0000000 --- a/src/conversation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import fs from "node:fs/promises"; -import os from "node:os"; -import type { ModelMessage } from "ai"; - -const home = os.homedir(); -const convoPath = `${home}/.lmc_convo.json`; - -export interface IConversation { - model: string; - messages: ModelMessage[]; -} - -export class Conversation implements IConversation { - messages: ModelMessage[] = []; - - constructor(public model: string) {} - - async load() { - try { - const text = await fs.readFile(convoPath, "utf8"); - const convo: IConversation = JSON.parse(text); - this.model = convo.model; - this.messages = convo.messages; - } catch { - console.error("No conversation found, starting one."); - } - } - - async save() { - await fs.writeFile( - convoPath, - JSON.stringify({ - model: this.model, - messages: this.messages, - } satisfies IConversation), - ); - } -} diff --git a/src/conversation/index.ts b/src/conversation/index.ts new file mode 100644 index 0000000..557e6b5 --- /dev/null +++ b/src/conversation/index.ts @@ -0,0 +1,24 @@ +import os from "node:os"; +import type { ModelMessage } from "ai"; + +const home = os.homedir(); +export const convoFile = Bun.file(`${home}/.lmc_convo.json`); + +export interface Conversation { + model: string; + messages: ModelMessage[]; +} + +export async function loadConversation( + defaultModel: string, +): Promise { + try { + return await convoFile.json(); + } catch { + return { model: defaultModel, messages: [] }; + } +} + +export async function saveConversation(convo: Conversation): Promise { + await convoFile.write(JSON.stringify(convo)); +} diff --git a/src/errors.ts b/src/errors.ts deleted file mode 100644 index 2eac281..0000000 --- a/src/errors.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { chalkStderr } from "chalk"; -import wrapAnsi from "wrap-ansi"; - -export interface ErrorLike { - message: string; -} - -export const isErrorLike = (err: unknown): err is ErrorLike => - typeof err === "object" && - err !== null && - "message" in err && - typeof err.message === "string"; - -export function handleTopLevel(err: unknown) { - if (isErrorLike(err)) { - if (process.stderr.isTTY) { - const [columns] = process.stderr.getWindowSize(); - console.error(wrapAnsi(chalkStderr.red(err.message), columns)); - } else { - console.error(err.message); - } - } else { - console.error(String(err)); - } - - process.exit(1); -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 56c93ed..0000000 --- a/src/index.ts +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env node -import fs from "node:fs/promises"; -import consumers from "node:stream/consumers"; -import util from "node:util"; -import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; -import { streamText } from "ai"; - -import { initConfig, loadConfig } from "./config"; -import { Conversation } from "./conversation"; -import { handleTopLevel } from "./errors"; -import { - base, - commitDiff, - contextFence, - diffFence, - fileFence, - review, -} from "./prompts"; -import Renderer from "./render"; - -async function main() { - const { values: args, positionals } = util.parseArgs({ - args: process.argv.slice(2), - options: { - model: { type: "string", short: "m" }, - new: { type: "boolean", short: "n" }, - config: { type: "boolean", short: "c" }, - plain: { type: "boolean" }, - "plain-out": { type: "boolean" }, - "plain-err": { type: "boolean" }, - version: { type: "boolean", short: "v" }, - "git-message": { type: "boolean", short: "g" }, - review: { type: "boolean", short: "r" }, - file: { type: "string", short: "f", multiple: true }, - stdin: { type: "boolean", short: "i" }, - }, - strict: true, - allowPositionals: true, - }); - - if (args.version) { - console.log("0.0.0"); - return; - } - - const config = await loadConfig(args.config); - - if (args.config || !config) { - await initConfig(config); - return; - } - - const model = args.model ?? config.model; - - const plainOut = args["plain-out"] ?? args.plain ?? !process.stdout.isTTY; - const plainErr = args["plain-err"] ?? args.plain ?? !process.stderr.isTTY; - const renderer = new Renderer(plainOut, plainErr); - - const provider = createOpenAICompatible({ - name: "llama.cpp", - apiKey: "local", - baseURL: config.baseURL, - includeUsage: true, // Include usage information in streaming responses - }); - - const convo = new Conversation(model); - - if (!args.new) { - await convo.load(); - if (args.model) convo.model = args.model; - } - - const input = positionals.join(" ").trim(); - let stdinText = ""; - if (args.stdin) stdinText = await consumers.text(process.stdin); - const files = []; - for (const file of args.file ?? []) - files.push(fileFence(file, await fs.readFile(file, "utf8"))); - - let prompt = base(input, [ - ...files, - stdinText ? contextFence(stdinText) : "", - ]); - if (args["git-message"]) { - prompt = commitDiff(stdinText, [...files]); - } else if (args.review) { - prompt = review([...files, stdinText ? diffFence(stdinText) : "", input]); - } - - if (!prompt) throw new Error("No prompt given."); - - convo.messages.push({ role: "user", content: prompt }); - - const { fullStream, text, totalUsage } = streamText({ - model: provider(convo.model), - messages: convo.messages, - }); - - await renderer.renderStream(fullStream); - - convo.messages.push({ role: "assistant", content: await text }); - await convo.save(); - - await renderer.renderStats(totalUsage); -} - -main().catch(handleTopLevel); diff --git a/src/prompts.ts b/src/prompts.ts deleted file mode 100644 index 31fae99..0000000 --- a/src/prompts.ts +++ /dev/null @@ -1,31 +0,0 @@ -const blocks = (input?: string[]) => - input?.map((block) => (block ? `${block}\n` : "")).join("") ?? ""; - -export const base = (input: string, extra?: string[]) => - `${blocks(extra)}${input}`; - -export const fileFence = (name: string, input: string) => ` -${input} - -`; - -export const diffFence = (input: string) => ` -${input} -`; - -export const contextFence = (input: string) => ` -${input} -`; - -export const commitDiff = ( - input: string, - extra?: string[], -) => `${blocks(extra)}${diffFence(input)} -Generate a conventional commit message for the included context. If you are unable to generate a reasonable option, ask for more context.`; - -export const review = (input: string[]) => `${blocks(input)} -Review the included context for any potential bugs, best practices, performance issues, or security flaws. - -Today's date is ${new Date().toISOString()}, your version info is likely out-of-date. - -Let me know if you need any more context to complete your review.`; diff --git a/src/render/codeToANSI.ts b/src/render/codeToANSI.ts index fc54b8e..d5a081e 100644 --- a/src/render/codeToANSI.ts +++ b/src/render/codeToANSI.ts @@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import c from "chalk"; -import type { BundledLanguage, BundledTheme, HighlighterCore } from "shiki"; +import type { BundledLanguage, BundledTheme, HighlighterGeneric } from "shiki"; // From @shikijs/vscode-textmate enum FontStyle { @@ -37,7 +37,7 @@ enum FontStyle { } export function codeToANSI( - highlighter: HighlighterCore, + highlighter: HighlighterGeneric, code: string, lang: BundledLanguage, theme: BundledTheme, diff --git a/src/render/index.ts b/src/render/index.ts index 2819467..0e78648 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -1,5 +1,4 @@ import type { WriteStream } from "node:tty"; -import { spinner } from "@clack/prompts"; import type { AsyncIterableStream, LanguageModelUsage, @@ -8,29 +7,34 @@ import type { } from "ai"; import styles from "ansi-styles"; import { chalkStderr, supportsColorStderr } from "chalk"; -import stringWidth from "string-width"; import { renderMarkdown } from "./markdown"; export default class Renderer { private lineCount = 0; private charsSinceNL = 0; + private memOk = true; private buffer = ""; private unsafeIndex = 0; - private start = 0; - private stop = 0; constructor( private isPlain = false, private isPlainErr = false, - ) {} + ) { + process.stdout.addListener("drain", () => { + this.memOk = true; + }); + process.stderr.addListener("drain", () => { + this.memOk = true; + }); + } private write(text: string, pipe: WriteStream = process.stdout) { - pipe.write(text); + this.memOk = pipe.write(text); } private moveClearReset(y: number, pipe: WriteStream = process.stdout) { - pipe.moveCursor(-9999, y); - pipe.clearScreenDown(); + this.memOk = pipe.moveCursor(-9999, y); + this.memOk = pipe.clearScreenDown(); this.resetCounts(); } @@ -41,17 +45,16 @@ export default class Renderer { private checkContainment(text: string, pipe: WriteStream = process.stdout) { const [columns, rows] = pipe.getWindowSize(); - this.charsSinceNL += stringWidth(text); + this.charsSinceNL += text.length; if (text.includes("\n")) { this.lineCount += text.match(/\n/g)?.length ?? 1; - this.charsSinceNL = - stringWidth(text) - stringWidth(text.slice(0, text.lastIndexOf("\n"))); + this.charsSinceNL = text.length - text.lastIndexOf("\n") - 1; } else if (this.charsSinceNL > columns) { this.lineCount++; this.charsSinceNL = this.charsSinceNL - columns; } - if (this.lineCount >= Math.max(rows - 4, 1)) { + if (this.lineCount >= Math.max(rows - 10, 1)) { this.moveClearReset(-this.lineCount, pipe); } } @@ -84,22 +87,9 @@ export default class Renderer { async renderStream(stream: AsyncIterableStream>) { this.resetCounts(); - const spin = spinner({ - withGuide: false, - indicator: "timer", - onCancel: () => { - process.exit(130); - }, - }); - if (!this.isPlain) { - spin.start("Waiting for response"); - } - for await (const part of stream) { switch (part.type) { case "reasoning-start": { - this.start = Date.now(); - spin.clear(); if (supportsColorStderr) this.write( `${styles.dim.open}${styles.italic.open}`, @@ -121,8 +111,6 @@ export default class Renderer { } else { this.write("\n--- Done Thinking ---\n", process.stderr); } - const time = (Date.now() - this.start) / 1000; - this.write(`Thought for ${time.toFixed(2)}s\n`, process.stderr); if (supportsColorStderr) this.write( @@ -133,8 +121,6 @@ export default class Renderer { break; } case "text-start": { - if (this.start === 0) this.start = Date.now(); - spin.clear(); this.write("\n"); break; } @@ -147,7 +133,6 @@ export default class Renderer { break; } case "text-end": { - this.stop = Date.now(); if (this.buffer) { await this.render(this.buffer); this.buffer = ""; @@ -157,15 +142,21 @@ export default class Renderer { break; } } + + while (!this.memOk) await Bun.sleep(10); } } - async renderStats(usage: PromiseLike) { + async renderStats( + usage: PromiseLike, + start: number, + stop: number, + ) { const { inputTokens, outputTokens } = await usage; const context = (inputTokens ?? 0) / 1000; const output = (outputTokens ?? 0) / 1000; - const toks = (outputTokens ?? 0) / ((this.stop - this.start) / 1000); - const template = `Context: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Toks: ${toks.toFixed(2)}/s\n`; + const time = (stop - start) / 1000; + const template = `Context: ${context.toFixed(1)}k, Output: ${output.toFixed(1)}k, Time: ${time.toFixed(2)}s\n`; this.write(chalkStderr.dim(template), process.stderr); } } diff --git a/src/render/markdown.ts b/src/render/markdown.ts index 7190de8..b7f5c7e 100644 --- a/src/render/markdown.ts +++ b/src/render/markdown.ts @@ -1,192 +1,17 @@ -import { Cell, Table } from "@cliffy/table"; import chalk from "chalk"; -import { marked, Renderer, type Tokens } from "marked"; -import type { BundledLanguage, HighlighterCore } from "shiki"; -import { createHighlighterCore } from "shiki/core"; -import { createOnigurumaEngine } from "shiki/engine/oniguruma"; -import stringWidth from "string-width"; +import CliTable3 from "cli-table3"; +import { + type BundledLanguage, + type BundledTheme, + createHighlighter, + type HighlighterGeneric, +} from "shiki"; import terminalLink from "terminal-link"; -import wrapAnsi from "wrap-ansi"; import { codeToANSI } from "./codeToANSI"; -let highlighter: HighlighterCore | undefined; +let highlighter: HighlighterGeneric | undefined; let highligherLangs = ""; -class SWCell extends Cell { - public override get length() { - return stringWidth(this.toString()); - } -} - -class LmcRenderer extends Renderer { - private columns: number; - - constructor(columns: number) { - super(); - this.columns = columns; - } - - override heading({ tokens, depth }: Tokens.Heading) { - const colors = [ - chalk.cyan.underline, - chalk.blue, - chalk.green, - chalk.cyanBright, - chalk.blueBright, - chalk.greenBright, - ]; - const color = colors[depth - 1] ?? chalk.white; - const text = this.parser.parseInline(tokens); - return color.bold(`${"#".repeat(depth)} ${text}\n`); - } - - override paragraph({ tokens }: Tokens.Paragraph) { - const text = wrapAnsi(this.parser.parseInline(tokens), this.columns - 4); - return `\n${text}\n`; - } - - override blockquote({ tokens }: Tokens.Blockquote) { - const text = this.parser.parse(tokens).trim(); - const lines = `\n${text}\n`.split("\n"); - const toRender = lines.map((line) => `> ${line}`).join("\n"); - return `\n${toRender.replaceAll(">", chalk.dim(">"))}\n`; - } - - override code({ text, lang }: Tokens.Code) { - let toRender = text; - - if (highlighter && lang) { - try { - toRender = codeToANSI( - highlighter, - text, - lang as BundledLanguage, - "kanagawa-wave", - ); - } catch {} - } - - return `\n\`\`\`${chalk.blue(lang ?? "")}\n${toRender}\n\`\`\`\n`; - } - - override list({ items, ordered, start }: Tokens.List) { - const num = start || 1; - const renderedItems = items - .map((item, index) => - this.listitem(item, ordered ? num + index : undefined), - ) - .join("\n"); - - return `\n${renderedItems}\n`; - } - - override listitem({ tokens }: Tokens.ListItem, num?: number) { - const text = this.parser.parse(tokens).trim(); - let marker = ""; - if (num !== undefined) { - marker = `${num}.`; - } else { - marker = "•"; - } - - const item = `${marker} ${text}`; - return item - .split("\n") - .filter((line) => line.length > 0) - .map((line) => ` ${line}`) - .join("\n"); - } - - override checkbox({ checked }: Tokens.Checkbox): string { - return checked ? "[✓] " : "[ ] "; - } - - override hr() { - return `\n${chalk.dim("─".repeat(this.columns))}\n`; - } - - override table({ header, rows }: Tokens.Table) { - const headers = header.map((col) => { - const text = this.parser.parseInline(col.tokens); - return new SWCell(chalk.red.bold(text)); - }); - - const body = rows.map((row) => - row.map((col) => { - const text = this.parser.parseInline(col.tokens); - return new SWCell(text); - }), - ); - - const cols = this.columns - (headers.length * 3 + 1); - const maxWidth = Math.floor(cols / headers.length); - const table = new Table() - .header(headers) - .body(body) - .border() - .maxColWidth(maxWidth) - .toString(); - - return `\n${table}\n`; - } - - override strong({ tokens }: Tokens.Strong) { - const text = this.parser.parseInline(tokens); - return chalk.bold.rgb(245, 245, 245)(text); - } - - override em({ tokens }: Tokens.Em) { - const text = this.parser.parseInline(tokens); - return chalk.italic.rgb(245, 245, 245)(text); - } - - override del({ tokens }: Tokens.Del) { - const text = this.parser.parseInline(tokens); - return chalk.grey.strikethrough(text); - } - - override codespan({ text }: Tokens.Codespan) { - return chalk.bgRgb(165, 165, 192).rgb(0, 0, 0).bold(text); - } - - override link({ href, tokens }: Tokens.Link) { - const text = this.parser.parseInline(tokens); - return chalk.blueBright.underline(terminalLink(text, href)); - } - - override image({ text: _ }: Tokens.Image) { - return chalk.red("\nImages are unsupported (for now)\n"); - } - - override text(token: Tokens.Text | Tokens.Escape) { - if (token.type === "text" && token.tokens) { - const parsed = this.parser.parseInline(token.tokens); - return wrapAnsi(parsed, this.columns - 12); - } - return this.replaceLatex(token.text); - } - - override html({ text }: Tokens.HTML | Tokens.Tag) { - if (text === "
") return this.br(); - return chalk.red(text); - } - - override br() { - return "\n"; - } - - private replaceLatex(text: string) { - return text - .replaceAll("$\\rightarrow$", "-->") - .replaceAll("$\\leftarrow$", "<--") - .replaceAll("$\\leftrightarrow$", "<->") - .replaceAll("$\\Rightarrow$", "==>") - .replaceAll("$\\Leftarrow$", "<==") - .replaceAll("$\\Leftrightarrow$", "<=>") - .replaceAll("$\\to$", "->"); - } -} - export async function renderMarkdown( text: PromiseLike | string, columns: number, @@ -194,29 +19,98 @@ export async function renderMarkdown( const md = await text; const langs = md - .match(/```\S+\n/g) - ?.map((lang) => lang.replace("```", "").trimEnd()) - .sort(); + .match(/```[a-z]+\n/g) + ?.map((lang) => lang.replace("```", "").trimEnd()); if (langs && langs?.join() !== highligherLangs) { - const { bundledThemes, bundledLanguages, bundledLanguagesAlias } = - await import("shiki/bundle/full"); try { highligherLangs = langs.join(); - highlighter = await createHighlighterCore({ - langs: langs.map( - (lang) => - bundledLanguages[lang as BundledLanguage] || - bundledLanguagesAlias[lang], - ), - themes: [bundledThemes["kanagawa-wave"]], - engine: createOnigurumaEngine(import("shiki/wasm")), + highlighter = await createHighlighter({ + langs, + themes: ["kanagawa-wave", "kanagawa-lotus"], }); } catch {} } - const renderer = new LmcRenderer(columns); - const rendered = await marked.parse(md, { renderer }); + let currentTable = new CliTable3(); - return rendered.trimStart(); + const rendered = Bun.markdown.render(md, { + heading: (children, { level }) => { + const colors = [ + chalk.cyan.underline, + chalk.blue, + chalk.green, + chalk.cyanBright, + chalk.blueBright, + chalk.greenBright, + ]; + const color = colors[level - 1] ?? chalk.white; + return color.bold(`${"#".repeat(level)} ${children}\n\n`); + }, + paragraph: (children) => `${children}\n\n`, + blockquote: (children) => { + const lines = children.split("\n"); + return lines + .map((line) => `${chalk.dim("> ")}${line}`) + .join("\n") + .replace(/\n$/, "\n\n"); + }, + code: (children, meta) => { + let toRender = children; + + if (highlighter && meta?.language) { + try { + toRender = codeToANSI( + highlighter, + children, + meta.language as BundledLanguage, + "kanagawa-wave", + ); + } catch {} + } + + return `\`\`\`${chalk.blue(meta?.language ?? "")}\n${toRender}\`\`\`\n`; + }, + list: (children, { depth }) => + `${depth > 0 ? "\n" : ""}${children.trimEnd()}${depth === 0 ? "\n\n" : ""}`, + listItem: (children, { index, depth, ordered, start, checked }) => { + const indent = " ".repeat(depth); + let marker = ""; + if (checked !== undefined) { + marker = checked ? "[✓]" : "[ ]"; + } else if (ordered) { + const n = (start ?? 1) + index; + marker = `${n}.`; + } else { + marker = "•"; + } + return `${indent} ${marker} ${children}\n`; + }, + hr: () => `${chalk.dim("─".repeat(columns))}\n\n`, + table: (_children) => { + const toRender = currentTable.toString(); + currentTable = new CliTable3(); + return `${toRender}\n\n`; + }, + tr: (children) => { + if (children) currentTable.push(children.split(":tablesep:").slice(1)); + return ""; + }, + td: (children) => `:tablesep:${children}`, + th: (children) => { + currentTable.options.head.push(children); + return ""; + }, + strong: (children) => chalk.bold(children), + emphasis: (children) => chalk.italic(children), + strikethrough: (children) => chalk.grey.strikethrough(children), + codespan: (children) => chalk.bgWhite.black(` ${children} `), + link: (children, { href }) => + chalk.blueBright.underline(terminalLink(children, href)), + image: () => "", + text: (children) => children, + html: () => "", + }); + + return rendered.replace(/\n\n$/, "\n"); } diff --git a/src/tts.ts b/src/tts.ts deleted file mode 100644 index e6aab05..0000000 --- a/src/tts.ts +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env node -import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; -import process from "node:process"; -import readline from "node:readline"; -import consumers from "node:stream/consumers"; -import util from "node:util"; -import { Readability } from "@mozilla/readability"; -import chalk from "chalk"; -import { ExecaError, execa } from "execa"; -import { Window } from "happy-dom"; -import wrapAnsi from "wrap-ansi"; -import { handleTopLevel } from "./errors"; - -async function main() { - const start = Date.now(); - - const { values: args, positionals } = util.parseArgs({ - args: process.argv.slice(2), - options: { - wait: { type: "boolean" }, - w: { type: "boolean" }, - pause: { type: "boolean" }, - p: { type: "boolean" }, - url: { type: "boolean" }, - u: { type: "boolean" }, - edit: { type: "boolean" }, - e: { type: "boolean" }, - }, - strict: true, - allowPositionals: true, - }); - - const waitAndCombine = args.wait ?? args.w; - const pausePlayer = args.pause ?? args.p; - const readURL = args.url ?? args.u; - const editContent = args.edit ?? args.e; - - let input = positionals.join(" ").trim(); - - if (!input) input = (await consumers.text(process.stdin)).trim(); - - if (!input) { - throw new Error("No input was given. Use positional arguments or stdin."); - } - - if (readURL) { - const result = await fetch(input); - const window = new Window({ url: input }); - const doc = window.document; - doc.write(await result.text()); - await window.happyDOM.waitUntilComplete(); - const readable = new Readability(doc).parse(); - if (!readable?.textContent) { - throw new Error( - "Unable to read webpage. This usually means it's blocking scrapers.", - ); - } - input = readable.textContent.trim(); - } - - const home = os.homedir(); - const cwd = path.join(home, ".ttsc"); - - if (editContent) { - const file = path.join(cwd, "input.txt"); - await fs.writeFile(file, input, "utf8"); - const editor = process.env.EDITOR || "nano"; - await execa({ - cwd, - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", - })`${editor} ${file}`; - input = await fs.readFile(file, "utf8"); - } - - console.log(chalk.blue("Welcome to TTSC!")); - console.log( - chalk.yellow( - "Beginning TTS processing... (This will take extra time the first time)", - ), - ); - - const wavs = fs.glob(path.join(cwd, "*.wav")); - for await (const wav of wavs) await fs.rm(wav); - - const koko = execa({ - cwd, - input, - stdout: "pipe", - stderr: "inherit", - })`uv run -- python -u main.py`; - - if (!waitAndCombine) { - koko.catch(() => {}); - koko.then(() => { - const stop = Date.now(); - const duration = ((stop - start) / 1000).toFixed(2); - console.log(chalk.green(`\nProcessing finished: ${duration}s`)); - }); - } - - let first = true; - const files = []; - const rl = readline.createInterface({ input: koko.stdout }); - - for await (const line of rl) { - const trimmed = line.trim(); - if (!trimmed) continue; - - let info: { file: number; text: string }; - try { - info = JSON.parse(trimmed); - } catch { - console.log(trimmed); - continue; - } - - if (first) { - first = false; - const stop = Date.now(); - const duration = ((stop - start) / 1000).toFixed(2); - console.log(chalk.green(`\nTime-to-audio: ${duration}s`)); - } - - const file = path.join(cwd, `${info.file}.wav`); - - if (waitAndCombine) { - files.push(file); - process.stdout.write("."); - } else { - const [columns] = process.stdout.getWindowSize(); - const text = wrapAnsi(info.text, columns); - console.log(`\n${chalk.blue(file)}\n${text}\n`); - await execa({ - cwd, - stdin: "ignore", - stdout: "inherit", - stderr: "inherit", - })`mpv${pausePlayer ? " --pause" : ""} ${file}`; - } - } - - await koko; - if (!waitAndCombine) return; - - const stop = Date.now(); - const duration = ((stop - start) / 1000).toFixed(2); - console.log(chalk.green(`\nProcessing finished: ${duration}s`)); - - console.log(chalk.yellow(`Combining audio with sox...\n`)); - try { - await execa({ cwd, shell: true })`sox ${files} output.wav`; - } catch (err) { - if (err instanceof ExecaError && err.code === "ENOENT") { - console.error("Sox not found. Unable to combine outputs."); - console.log(chalk.yellow(`Audio available in ${cwd}`)); - return; - } else { - throw err; - } - } - - const [columns] = process.stdout.getWindowSize(); - console.log(wrapAnsi(input, columns)); - - console.log( - chalk.green(`Playing audio from ${path.join(cwd, "output.wav")}`), - ); - await execa({ - cwd, - stdin: "ignore", - stdout: "inherit", - stderr: "inherit", - })`mpv${pausePlayer ? " --pause" : ""} output.wav`; -} - -main().catch(handleTopLevel); diff --git a/testRender.ts b/testRender.ts new file mode 100644 index 0000000..a31dfe4 --- /dev/null +++ b/testRender.ts @@ -0,0 +1,6 @@ +import { renderMarkdown } from "./src/render/markdown.ts"; + +const text = await Bun.file("test.md").text(); + +const [columns] = process.stdout.getWindowSize(); +process.stdout.write(await renderMarkdown(text, columns)); diff --git a/tsconfig.json b/tsconfig.json index d593bb5..bfa0fea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,39 +1,29 @@ { - // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { - // Environment Settings - // See also https://aka.ms/tsconfig/module - "module": "esnext", - "target": "esnext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "noEmit": true, - // For nodejs: - "lib": ["esnext"], - "types": ["node"], - // and npm install -D @types/node - - // Stricter Typechecking Options - "noUncheckedIndexedAccess": true, - "exactOptionalPropertyTypes": true, - - // Style Options - "noImplicitReturns": true, - "noImplicitOverride": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": false, - - // Recommended Options - "strict": true, - "jsx": "react-jsx", - "allowImportingTsExtensions": false, - "allowArbitraryExtensions": true, - "verbatimModuleSyntax": true, - "isolatedModules": true, - "noUncheckedSideEffectImports": true, + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", "moduleDetection": "force", - "skipLibCheck": true + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false } } diff --git a/tts.ts b/tts.ts new file mode 100644 index 0000000..16f8b8b --- /dev/null +++ b/tts.ts @@ -0,0 +1,98 @@ +import os from "node:os"; +import { parseArgs } from "node:util"; +import { $, which } from "bun"; +import chalk from "chalk"; + +const { values: args } = parseArgs({ + args: Bun.argv, + options: { + wait: { type: "boolean" }, + w: { type: "boolean" }, + }, + strict: true, + allowPositionals: true, +}); + +const wait = args.wait ?? args.w; +const text = await Bun.stdin.text(); + +if (!text) { + console.error("No input given."); + process.exit(1); +} + +const home = os.homedir(); +const cwd = `${home}/.ttsc/`; +$.cwd(cwd); + +await $`rm *.wav`.nothrow(); + +console.log(chalk.blue("Welcome to TTSC!")); +console.log( + chalk.yellow( + "Beginning TTS processing... (This will take extra time the first time)", + ), +); + +const koko = Bun.spawn({ + cmd: ["uv", "run", "--", "python", "-u", "main.py"], + cwd, + stdin: Buffer.from(text), + stdout: "pipe", + stderr: "inherit", +}); + +if (!wait) { + koko.exited.then(() => console.log(chalk.green("\nProcessing finished."))); +} + +const decoder = new TextDecoder(); +for await (const line of koko.stdout) { + const decoded = decoder.decode(line).trim(); + + let infos: { file: number; text: string }[]; + try { + infos = Bun.JSONL.parse(decoded) as { file: number; text: string }[]; + } catch { + console.log(decoded); + continue; + } + + for (const info of infos) { + const file = `${home}/.ttsc/${info.file}.wav`; + console.log(`\n${chalk.blue(file)}:\n${info.text}`); + + if (!wait) { + await Bun.spawn({ + cmd: ["mpv", file], + cwd, + stdin: "ignore", + stdout: "inherit", + stderr: "inherit", + }).exited; + } + } +} + +await koko.exited; +if (!wait) process.exit(); + +console.log(chalk.green("\nProcessing finished.")); + +console.log(chalk.yellow(`Combining audio with sox...`)); +if (which("sox")) { + await $`sox *.wav output.wav`; + console.log(chalk.green(`Playing audio from ${cwd}output.wav`)); +} else { + console.error("Sox not found. Unable to combine outputs."); + console.log(chalk.yellow(`Audio available in ${cwd}`)); + process.exit(); +} + +await Bun.spawn({ + cmd: ["mpv", "--pause", "output.wav"], + cwd, + stdin: "ignore", + stdout: "inherit", + stderr: "inherit", +}).exited;