From c1822b4fc070c9f5559069b779e1b911a3068cb3 Mon Sep 17 00:00:00 2001 From: Raghuram1234 Date: Thu, 6 Aug 2026 19:46:39 +0000 Subject: [PATCH 1/3] fix: make the TypeScript app buildable and wire up terminal, chat, and settings --- .dockerignore | 5 +- .github/workflows/ci.yml | 38 +- .github/workflows/pages.yml | 33 +- .gitignore | 3 + Dockerfile | 33 +- README.md | 67 +- docker-compose.yml | 2 +- index.html | 12 + package-lock.json | 2204 +++++++++++++++++++++++ package.json | 36 + src/core/types.ts | 2 + src/server/main.ts | 30 +- src/server/routes/github.ts | 25 +- src/server/routes/instances.ts | 45 +- src/server/services/ai.ts | 28 +- src/server/services/models.ts | 2 +- src/server/services/pty.ts | 67 +- src/ui/components/base.ts | 16 +- src/ui/components/instance-dashboard.ts | 128 +- src/ui/components/right-panel.ts | 88 +- src/ui/components/settings-modal.ts | 27 +- src/ui/components/title-bar.ts | 34 +- src/ui/main.ts | 28 +- src/ui/state.ts | 33 +- tsconfig.json | 21 + vite.config.ts | 14 + 26 files changed, 2770 insertions(+), 251 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.dockerignore b/.dockerignore index 951987a..613a413 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ -target -**/target -crates/tck-ui/dist +node_modules +**/node_modules dist release .git diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60a95ad..380c6b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,36 +16,24 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown - components: clippy - - - uses: Swatinem/rust-cache@v2 - - - name: Install Trunk - uses: jetli/trunk-action@v0.5.0 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - version: latest + node-version: "22" - - name: Check workspace (native crates) - run: cargo clippy -p tck-core -p tck-server -- -D warnings + - name: Install dependencies + run: npm install - - name: Build WASM UI - working-directory: crates/tck-ui - run: trunk build --release + - name: TypeScript check + run: npm run typecheck - - name: Build native server - run: cargo build -p tck-server --release + - name: Build UI + run: npm run build - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" + - name: Install tck-ai dependencies + working-directory: tck-ai + run: npm install - name: TypeScript check tck-ai working-directory: tck-ai - run: | - npm install - npm run typecheck + run: npm run typecheck diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 1ba02e9..39245a1 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -1,8 +1,8 @@ name: Deploy to GitHub Pages -# Publishes the WebAssembly UI to GitHub Pages on every push to main. -# The deployed site is static, so the terminal (which needs the native -# tck-server) is unavailable there; AI calls go directly to the provider. +# Publishes the static UI to GitHub Pages on every push to main. +# The deployed site has no server, so terminals are unavailable there; +# AI calls have to go directly to the provider from the browser. on: push: branches: [main] @@ -30,39 +30,22 @@ jobs: with: enablement: true - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown - - - uses: Swatinem/rust-cache@v2 - - - name: Install Trunk - uses: jetli/trunk-action@v0.5.0 - with: - version: latest - - name: Install Node.js uses: actions/setup-node@v4 with: node-version: "22" - - name: Build tck-ai browser bundle - working-directory: tck-ai - run: | - npm ci - npm run build:browser - cp dist/tck-ai.js ../crates/tck-ui/assets/ + - name: Install dependencies + run: npm install # Pages serves the repo at //, so assets must be referenced there. - - name: Build WASM UI - working-directory: crates/tck-ui - run: trunk build --release --public-url /keyboard/ + - name: Build UI + run: npm run build -- --base=/keyboard/ - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: crates/tck-ui/dist + path: dist deploy: needs: build diff --git a/.gitignore b/.gitignore index bbec715..5ff040f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ Thumbs.db Landing page and design system.zip landing/ .thumbnail + +# Node +node_modules/ diff --git a/Dockerfile b/Dockerfile index a533b70..571bcc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,24 @@ # T.C.K (TalentCloud Keyboard) — production image. -# Stage 1 builds the WASM UI (Trunk) and the native server (cargo); stage 2 is a +# Stage 1 installs dependencies and builds the static UI (Vite); stage 2 is a # slim runtime that serves the bundle and runs the terminal/AI/GitHub backend. # ---------- build ---------- -FROM rust:1-bookworm AS build +FROM node:22-bookworm-slim AS build WORKDIR /app -# WASM target + Trunk (prebuilt binary; Trunk fetches wasm-bindgen/wasm-opt itself). -ARG TRUNK_VERSION=v0.21.14 -RUN rustup target add wasm32-unknown-unknown \ - && curl -sSL "https://github.com/trunk-rs/trunk/releases/download/${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz" \ - | tar -xz -C /usr/local/bin +# node-pty is a native addon, so the build stage needs a toolchain. +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 make g++ \ + && rm -rf /var/lib/apt/lists/* -COPY . . +COPY package.json package-lock.json ./ +RUN npm ci -# Build the WebAssembly UI, then the server binary. -RUN cd crates/tck-ui && trunk build --release -RUN cargo build -p tck-server --release +COPY . . +RUN npm run build # ---------- runtime ---------- -FROM debian:bookworm-slim +FROM node:22-bookworm-slim # ca-certificates: outbound HTTPS to GitHub/Anthropic/providers. bash: the PTY shell. RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates bash git curl \ @@ -27,13 +26,15 @@ RUN apt-get update \ && useradd -m -u 10001 tck WORKDIR /app -COPY --from=build /app/target/release/tck-server /usr/local/bin/tck-server -COPY --from=build /app/crates/tck-ui/dist /app/dist +COPY --from=build /app/node_modules /app/node_modules +COPY --from=build /app/package.json /app/package.json +COPY --from=build /app/dist /app/dist +COPY --from=build /app/src /app/src ENV TCK_DIST=/app/dist \ - TCK_ADDR=0.0.0.0:3000 + PORT=3000 # Run as an unprivileged user — the terminal grants a shell, so limit its reach. USER tck EXPOSE 3000 -CMD ["tck-server"] +CMD ["npm", "run", "start"] diff --git a/README.md b/README.md index b27f7c0..a590f9b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # ⌨️ T.C.K — TalentCloud Keyboard [![CI](https://github.com/Raghuramcoding/keyboard/actions/workflows/ci.yml/badge.svg)](https://github.com/Raghuramcoding/keyboard/actions/workflows/ci.yml) -[![Rust](https://img.shields.io/badge/built%20with-Rust-orange?logo=rust)](https://www.rust-lang.org) +[![TypeScript](https://img.shields.io/badge/built%20with-TypeScript-3178c6?logo=typescript)](https://www.typescriptlang.org) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) **T.C.K (TalentCloud Keyboard)** is an AI-powered coding environment that runs -**in your browser** (or as a native desktop app). The UI compiles to WebAssembly -(Dioxus); a native server provides real shell terminals, and a TypeScript AI -service (`tck-ai`) uses the Vercel AI SDK for unified provider access. +**in your browser**. The UI is a TypeScript SPA built with Vite; a Node server +(Hono) provides real shell terminals over WebSocket, proxies AI requests, and +talks to GitHub. A standalone AI service (`tck-ai`) is also included for +deploying provider access separately. ## Features @@ -17,15 +18,13 @@ service (`tck-ai`) uses the Vercel AI SDK for unified provider access. own PTY session that keeps running even if you close the tab. - 💬 **AI chat instances** — create standalone AI chat sessions alongside your terminal tools. Chat histories persist across page reloads via `localStorage`. -- 🦀 **Rust core** — `tck-ui` (Dioxus/WASM frontend), `tck-server` (axum - backend), `tck-core` (shared types). AI service (`tck-ai`) uses **TypeScript + - Vercel AI SDK**. -- 🌐 **Browser or desktop** — run as a web app in any browser, or install as a - native desktop app (WebView2 on Windows, WebKit on macOS/Linux). +- 🧩 **TypeScript everywhere** — `src/ui` (Vite SPA), `src/server` (Hono + node-pty), + `src/core` (shared types). AI access uses the **Vercel AI SDK**. +- 🌐 **Runs in any browser** — served by the local Node server. - 📱 **Responsive** — a 3-column desktop layout that collapses to a touch-friendly tabbed layout on phones. - 💻 **Real terminals** — each tool instance gets its own genuine shell session - via a pseudo-terminal (`portable-pty`): PowerShell on Windows, `$SHELL` + via a pseudo-terminal (`node-pty`): PowerShell on Windows, `$SHELL` (zsh/bash) on macOS & Linux, streamed to the browser over WebSocket with reconnection support (64 KB ring buffer per session). - 🤖 **Multi-provider AI** — Ollama, Claude, and any OpenAI-compatible endpoint @@ -71,33 +70,32 @@ service (`tck-ai`) uses the Vercel AI SDK for unified provider access. └──────────────────────────────────────────────────────┘ ``` -| Crate | Target | Responsibility | -|-----------------|--------------------------|--------------------------------------| -| `tck-core` | both | Shared serde types, provider presets | -| `tck-ui` | `wasm32-unknown-unknown` | Dioxus UI compiled to WebAssembly | -| `tck-server` | native | axum server: AI proxy + PTY + static | -| `tck-desktop` | native + webview | Desktop app (embeds server + WASM) | +| Module | Target | Responsibility | +|-----------------|---------|---------------------------------------------| +| `src/core` | both | Shared types (instances, settings, AI) | +| `src/ui` | browser | Vite SPA: landing page, dashboard, chat | +| `src/server` | node | Hono server: AI proxy + PTY + static assets | +| `tck-ai` | node | Standalone AI service (Vercel AI SDK) | ## Quick start ### Prerequisites -- [Rust](https://rustup.rs) (stable) -- WASM target: `rustup target add wasm32-unknown-unknown` -- [Trunk](https://trunkrs.dev): `cargo install trunk` +- [Node.js](https://nodejs.org) 22 or newer ### Build & run ```bash -# 1. Build the WebAssembly UI -cd crates/tck-ui -trunk build --release -cd ../.. +# 1. Install dependencies +npm install -# 2. Run the server (serves the UI + provides terminals/AI) -cargo run -p tck-server --release +# 2. Build the UI into dist/ +npm run build -# 3. Open the app +# 3. Run the server (serves dist/ + provides terminals/AI) +npm start + +# 4. Open the app # http://127.0.0.1:3000 ``` @@ -119,14 +117,17 @@ Once the server is running, open the app and you'll see the **Dashboard** view: ```bash # Terminal 1 — the backend -cargo run -p tck-server +npm run dev:server # Terminal 2 — the UI with hot reload (proxies /api and /ws to :3000) -cd crates/tck-ui -trunk serve +npm run dev # open http://127.0.0.1:8080 ``` +> **Note:** the desktop app, Windows installer, and `scripts/build.ps1` below +> are from the earlier Rust/WASM implementation and are not buildable against +> the current TypeScript sources. + ## Desktop App T.C.K can run as a native desktop application with no browser chrome. @@ -186,8 +187,8 @@ your browser. See [installer/README.md](installer/README.md) for details. | Variable | Default | Meaning | |----------------------------|----------------------|------------------------------------------------| -| `TCK_ADDR` | `127.0.0.1:3000` | Address the server binds to | -| `TCK_DIST` | `crates/tck-ui/dist` | Directory of the built WASM bundle | +| `PORT` | `3000` | Port the server binds to | +| `TCK_DIST` | `dist` | Directory of the built UI bundle | | `TCK_GITHUB_CLIENT_ID` | — | GitHub OAuth App client id (enables Git) | | `TCK_GITHUB_CLIENT_SECRET` | — | GitHub OAuth App client secret | | `TCK_GITHUB_CALLBACK` | derived from Host | Override the OAuth callback URL (non-localhost)| @@ -236,8 +237,8 @@ phone can't run the native server itself, point it at a T.C.K server on your **same Wi-Fi**: ```bash -# Bind to all interfaces so the phone can reach you -TCK_ADDR=0.0.0.0:3000 cargo run -p tck-server --release +# The server listens on all interfaces, so the phone can reach it +npm start ``` Then on the phone open `http://:3000` (e.g. diff --git a/docker-compose.yml b/docker-compose.yml index 6f1042c..241a5e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: build: . restart: unless-stopped environment: - TCK_ADDR: "0.0.0.0:3000" + PORT: "3000" TCK_DIST: "/app/dist" # env_file passes values to the container literally (no interpolation), so # secrets and the bcrypt hash with '$' survive intact. Provides TCK_GITHUB_*. diff --git a/index.html b/index.html index 5114eb1..33c09be 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,7 @@ --fg: #e8eaf0; --mut: #888fa3; --acc: #5b8def; + --acc-solid: #5b8def; --acc-shadow: rgba(91,141,239,.4); --font-display: 'Space Grotesk', sans-serif; --font-body: 'Hanken Grotesk', sans-serif; @@ -33,6 +34,17 @@ --radius-lg: 24px; } + :root[data-theme="light"] { + --bg: #f4f5f8; + --surface: #ffffff; + --surface2: #eceef3; + --raised: #e6e9f0; + --raisedh: #dbdfe9; + --bd: #d2d7e2; + --fg: #1a1d26; + --mut: #5c6376; + } + *, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; height: 100%; overflow-x: clip; } body { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6aff078 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2204 @@ +{ + "name": "tck", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tck", + "version": "3.0.0", + "dependencies": { + "@ai-sdk/anthropic": "^4.0.6", + "@ai-sdk/deepseek": "^3.0.4", + "@ai-sdk/google": "^4.0.7", + "@ai-sdk/groq": "^4.0.4", + "@ai-sdk/mistral": "^4.0.4", + "@ai-sdk/openai": "^4.0.6", + "@ai-sdk/xai": "^4.0.5", + "@hono/node-server": "^2.0.10", + "@octokit/rest": "^21.1.1", + "ai": "^7.0.13", + "hono": "^4.12.27", + "node-pty": "^1.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@types/ws": "^8.18.0", + "tsx": "^4.19.0", + "typescript": "^5.8.0", + "vite": "^6.3.5" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "4.0.33", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.33.tgz", + "integrity": "sha512-7mxzS0O2NQVSWscKqliLTd0JG/PDjNzRM3c9J8JIJKbOeAxJUSxjKjETIxTRSfvBCmrhASu7vRdpV6+cZruRUw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/deepseek": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/@ai-sdk/deepseek/-/deepseek-3.0.24.tgz", + "integrity": "sha512-jEQb84nzvK79dRbZT6+7KL3xgyXmAUAL7LaSlbHQEvljNBK/ony2oyWporTx5vdFAnm/Or74t9LtYMK2sqy4oA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/gateway": { + "version": "4.0.43", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.43.tgz", + "integrity": "sha512-7KqJR7+8zKwxuZY7/G12QWRV8MDUMgrAUmR/Al99zN6OGB3KQe1Q9i2lAB8uyTBsxgdZjB07vH+ky+RG5m1aiA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23", + "@vercel/oidc": "3.2.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/google": { + "version": "4.0.36", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.36.tgz", + "integrity": "sha512-fn3YEV2SygMTMHRgAIG5wWNn3ni2ozL+yKKHY6c31nlaAk/YYvs4eoQusM2uZXUGO1tAuw4teylc/bDm/emktw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/groq": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@ai-sdk/groq/-/groq-4.0.24.tgz", + "integrity": "sha512-b4Ef0lkEfeKi2PdurFQlgPTwuCH0Ww4vyobl7izGqorFtHhJ7aErNii6vLSs31aTybOHrnAG9rXT7MMWIwmvsg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/mistral": { + "version": "4.0.25", + "resolved": "https://registry.npmjs.org/@ai-sdk/mistral/-/mistral-4.0.25.tgz", + "integrity": "sha512-dPO0wOwOEidzVH+e4kDkz7YVWb3q0SoqV7hzjFfg10JOiUzaEOuNWQxYxxy/8ncwFUJZeIxVC/G7hTGmcnB0aA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "4.0.33", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.33.tgz", + "integrity": "sha512-x/7wWdbwKfyKKTS6J0DNgMl9gDeDDc76lhMaCEF90/bOEUmLT/nV+kxlnBJvqzq/1h40wee7kt0TXOHKyZ+aSA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/openai-compatible": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.25.tgz", + "integrity": "sha512-6NMHwYsjF8QQ8sKEnC6tt9w1z87Z0XwKgOGe1ZB48SwH+HvFEHsn8nCEvKML3MUwRtVu0mxRG08FTjXmew/ELA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.6.tgz", + "integrity": "sha512-YYXjvs8F3q/BdEn9tBDoDuQACotfR7c5foGw/ADsM6iAVC1JabqEjQSkwHv/Kg2vNIr1c2AVHcQb+JYsYk76Qw==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "5.0.23", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.23.tgz", + "integrity": "sha512-mFvAAszenK8Xny3v+4Vntke5IcUkyzjss3gTBOxVWNrSIWmt/FhQ5gCgoJXW7IK11cklRaicMPwOXTnnBtaqFQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "4.0.6", + "@standard-schema/spec": "^1.1.0", + "@workflow/serde": "4.1.0", + "eventsource-parser": "^3.0.8", + "undici": "^7.28.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/xai": { + "version": "4.0.30", + "resolved": "https://registry.npmjs.org/@ai-sdk/xai/-/xai-4.0.30.tgz", + "integrity": "sha512-vf4a7rDDrUO3gCjf+vFhLFFgzb7z3w8cm6rItQP/ox/e+Dpv4z1fDydKNrCr1KzJ2z+lWfQB6PRRGUtdiC5Jkw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/openai-compatible": "3.0.25", + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.0.tgz", + "integrity": "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.6.tgz", + "integrity": "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz", + "integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.10.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.5.0.tgz", + "integrity": "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.10.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.4.tgz", + "integrity": "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz", + "integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==", + "license": "MIT", + "dependencies": { + "@octokit/core": "^6.1.4", + "@octokit/plugin-paginate-rest": "^11.4.2", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.3.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vercel/oidc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", + "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@workflow/serde": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@workflow/serde/-/serde-4.1.0.tgz", + "integrity": "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==", + "license": "Apache-2.0" + }, + "node_modules/ai": { + "version": "7.0.55", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.55.tgz", + "integrity": "sha512-B+oZTUFjrR7eV0mF9DUuaIaWvEoJM28yVvcnbUioqcyYYkvae4eY+KvvRkfti+UojsoKrwKiWHeNk0w+nUyvVg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "4.0.43", + "@ai-sdk/provider": "4.0.6", + "@ai-sdk/provider-utils": "5.0.23" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "license": "Apache-2.0" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "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" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hono": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.0.tgz", + "integrity": "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/nanoid": { + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-pty": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", + "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tsx": { + "version": "4.23.9", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.9.tgz", + "integrity": "sha512-6q8uTORRGauQVjqMQnKUucLFoeXZAfw6zKvG35GLbdKWbLdeOtZ3H4mhyA5mxuUd2o2cRTskhj59nLLQseUvUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/ws": { + "version": "8.21.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.2.tgz", + "integrity": "sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==", + "license": "MIT", + "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 + } + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6e6f5cb --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "tck", + "version": "3.0.0", + "private": true, + "type": "module", + "description": "T.C.K — TalentCloud Keyboard: TypeScript UI + Hono server", + "scripts": { + "dev": "vite", + "dev:server": "tsx watch src/server/main.ts", + "build": "vite build", + "start": "tsx src/server/main.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@ai-sdk/anthropic": "^4.0.6", + "@ai-sdk/deepseek": "^3.0.4", + "@ai-sdk/google": "^4.0.7", + "@ai-sdk/groq": "^4.0.4", + "@ai-sdk/mistral": "^4.0.4", + "@ai-sdk/openai": "^4.0.6", + "@ai-sdk/xai": "^4.0.5", + "@hono/node-server": "^2.0.10", + "@octokit/rest": "^21.1.1", + "ai": "^7.0.13", + "hono": "^4.12.27", + "node-pty": "^1.0.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@types/ws": "^8.18.0", + "tsx": "^4.19.0", + "typescript": "^5.8.0", + "vite": "^6.3.5" + } +} diff --git a/src/core/types.ts b/src/core/types.ts index a3748f2..20c9103 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -13,6 +13,8 @@ export enum InstanceKind { Terminal, Chat } export enum AgentState { Idle, Working, Done, Blocked, Error } export enum InstanceStatus { Running, Stopped, Error } export interface InstanceInfo { id: string; kind: InstanceKind; label: string; command?: string; cwd?: string; status: InstanceStatus; error?: string; created_at: number; agent_state: AgentState; last_output_at: number; } +export interface CreateInstanceRequest { kind: InstanceKind; label?: string; command?: string; cwd?: string; } +export interface UpdateInstanceRequest { label?: string; status?: InstanceStatus; agent_state?: AgentState; } export type EventMessage = | { type: 'agent_state_changed'; instance_id: string; old_state: AgentState; new_state: AgentState; hint: string; timestamp: number; } | { type: 'instance_status_changed'; instance_id: string; status: InstanceStatus; timestamp: number; } diff --git a/src/server/main.ts b/src/server/main.ts index a1c6959..d3a04f3 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -1,19 +1,43 @@ import { Hono } from 'hono'; import { cors } from 'hono/cors'; +import { serveStatic } from '@hono/node-server/serve-static'; import { serve } from '@hono/node-server'; +import { WebSocketServer } from 'ws'; import { aiRoutes } from './routes/ai.js'; import { instanceRoutes } from './routes/instances.js'; import { githubRoutes } from './routes/github.js'; +import { ptyService } from './services/pty.js'; + +const PORT = Number(process.env.PORT ?? 3000); +const DIST = process.env.TCK_DIST ?? 'dist'; const app = new Hono(); app.use('/*', cors()); -app.get('/', (c) => c.text('T.C.K Server Running')); +app.get('/health', (c) => c.json({ status: 'ok' })); app.route('/api/ai', aiRoutes); app.route('/api/instances', instanceRoutes); app.route('/api/github', githubRoutes); -serve({ fetch: app.fetch, port: 3000 }, () => { - console.log('T.C.K Server listening on http://localhost:3000'); +// Serve the built UI (falling back to index.html for hash-routed views). +app.use('/*', serveStatic({ root: DIST })); +app.get('*', serveStatic({ path: `${DIST}/index.html` })); + +const server = serve({ fetch: app.fetch, port: PORT }, () => { + console.log(`T.C.K server listening on http://localhost:${PORT}`); +}); + +const wss = new WebSocketServer({ noServer: true }); + +server.on('upgrade', (req, socket, head) => { + const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`); + const match = /^\/ws\/pty\/(.+)$/.exec(url.pathname); + if (!match) { + socket.destroy(); + return; + } + wss.handleUpgrade(req, socket, head, (ws) => { + ptyService.handleTerminalWs(ws, match[1]); + }); }); diff --git a/src/server/routes/github.ts b/src/server/routes/github.ts index a086b66..4169794 100644 --- a/src/server/routes/github.ts +++ b/src/server/routes/github.ts @@ -3,9 +3,15 @@ import { Octokit } from '@octokit/rest'; export const githubRoutes = new Hono(); +function callbackUrl(requestUrl: string): string { + if (process.env.TCK_GITHUB_CALLBACK) return process.env.TCK_GITHUB_CALLBACK; + return new URL('/api/github/callback', requestUrl).toString(); +} + githubRoutes.get('/auth', (c) => { const clientId = process.env.TCK_GITHUB_CLIENT_ID; - const redirectUrl = encodeURIComponent(`http://localhost:3000/api/github/callback`); + if (!clientId) return c.json({ error: 'TCK_GITHUB_CLIENT_ID is not configured' }, 503); + const redirectUrl = encodeURIComponent(callbackUrl(c.req.url)); return c.redirect(`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUrl}&scope=repo,user`); }); @@ -15,19 +21,26 @@ githubRoutes.get('/callback', async (c) => { const clientId = process.env.TCK_GITHUB_CLIENT_ID; const clientSecret = process.env.TCK_GITHUB_CLIENT_SECRET; + if (!clientId || !clientSecret) { + return c.json({ error: 'GitHub OAuth is not configured' }, 503); + } const resp = await fetch('https://github.com/login/oauth/access_token', { method: 'POST', headers: { 'Accept': 'application/json' }, body: new URLSearchParams({ - client_id: clientId!, - client_secret: clientSecret!, - code: code, + client_id: clientId, + client_secret: clientSecret, + code, + redirect_uri: callbackUrl(c.req.url), }), }); - const data = await resp.json(); - return c.json(data); + const data = await resp.json() as { access_token?: string; error?: string }; + if (!data.access_token) { + return c.json({ error: data.error ?? 'Failed to exchange OAuth code' }, 502); + } + return c.json({ access_token: data.access_token }); }); githubRoutes.get('/repos', async (c) => { diff --git a/src/server/routes/instances.ts b/src/server/routes/instances.ts index 6ede828..8f0cdb4 100644 --- a/src/server/routes/instances.ts +++ b/src/server/routes/instances.ts @@ -1,43 +1,66 @@ +import { randomUUID } from 'node:crypto'; import { Hono } from 'hono'; import { ptyService } from '../services/pty.js'; -import type { CreateInstanceRequest, UpdateInstanceRequest, InstanceInfo } from '../../core/types.js'; +import { + AgentState, + InstanceKind, + InstanceStatus, + type CreateInstanceRequest, + type InstanceInfo, + type UpdateInstanceRequest, +} from '../../core/types.js'; export const instanceRoutes = new Hono(); const instances: Map = new Map(); instanceRoutes.post('/', async (c) => { const body = await c.req.json(); - const id = Math.random().toString(36).substring(2, 9); + const id = randomUUID(); const info: InstanceInfo = { id, - kind: body.kind, + kind: body.kind ?? InstanceKind.Terminal, label: body.label || 'New Instance', command: body.command, cwd: body.cwd, - status: 0, // Running + status: InstanceStatus.Running, created_at: Date.now(), - agent_state: 0, // Idle + agent_state: AgentState.Idle, last_output_at: Date.now(), }; + + if (info.kind === InstanceKind.Terminal) { + try { + ptyService.createInstance(id, info.command ?? '', info.cwd ?? ''); + } catch (err) { + info.status = InstanceStatus.Error; + info.agent_state = AgentState.Error; + info.error = err instanceof Error ? err.message : 'Failed to start terminal'; + } + } + instances.set(id, info); - return c.json(info); + return c.json(info, 201); }); instanceRoutes.get('/', (c) => { return c.json(Array.from(instances.values())); }); -instanceRoutes.patch(`/:id`, async (c) => { +instanceRoutes.patch('/:id', async (c) => { const id = c.req.param('id'); - const body = await c.req.json(); const inst = instances.get(id); - if (!inst) return c.json({ error: "Not found" }, 404); - if (body.label) inst.label = body.label; + if (!inst) return c.json({ error: 'Not found' }, 404); + + const body = await c.req.json(); + if (body.label !== undefined) inst.label = body.label; + if (body.status !== undefined) inst.status = body.status; + if (body.agent_state !== undefined) inst.agent_state = body.agent_state; return c.json(inst); }); -instanceRoutes.delete(`/:id`, (c) => { +instanceRoutes.delete('/:id', (c) => { const id = c.req.param('id'); + if (!instances.has(id)) return c.json({ error: 'Not found' }, 404); ptyService.removeInstance(id); instances.delete(id); return c.json({ success: true }); diff --git a/src/server/services/ai.ts b/src/server/services/ai.ts index 3ddc1a9..d5fde88 100644 --- a/src/server/services/ai.ts +++ b/src/server/services/ai.ts @@ -8,13 +8,17 @@ import { createDeepSeek } from "@ai-sdk/deepseek"; import { createXai } from "@ai-sdk/xai"; import type { GenerateRequest, GenerateResponse } from '../../core/types.js'; -const SDK_MAPPED_PROVIDERS = new Set([ - "anthropic", "openai", "google", "groq", "mistral", "deepseek", "xai", -]); +const PROVIDER_ALIASES: Record = { + claude: "anthropic", + gemini: "google", + ollama: "openai-compatible", + openrouter: "openai-compatible", +}; -function sdkModelString(provider: string, model: string): string { - return SDK_MAPPED_PROVIDERS.has(provider) ? `${provider}/${model}` : model; -} +const DEFAULT_BASE_URLS: Record = { + ollama: "http://127.0.0.1:11434/v1", + openrouter: "https://openrouter.ai/api/v1", +}; function createProviderClient(provider: string, apiKey?: string, baseUrl?: string): any { switch (provider) { @@ -34,14 +38,16 @@ function createProviderClient(provider: string, apiKey?: string, baseUrl?: strin } export async function generate(req: GenerateRequest): Promise { - const { provider, model, prompt, system, apiKey, baseUrl, temperature, maxTokens } = req; - const providerClient = createProviderClient(provider, apiKey, baseUrl); + const { provider, model, prompt, system, api_key, base_url, temperature, max_tokens } = req; + const resolvedProvider = PROVIDER_ALIASES[provider] ?? provider; + const resolvedBaseUrl = base_url || DEFAULT_BASE_URLS[provider]; + const providerClient = createProviderClient(resolvedProvider, api_key, resolvedBaseUrl); const result = await generateText({ - model: providerClient(sdkModelString(provider, model)), + model: providerClient(model), prompt, system, ...(temperature !== undefined ? { temperature } : {}), - ...(maxTokens !== undefined ? { maxTokens } : {}), + ...(max_tokens !== undefined ? { maxOutputTokens: max_tokens } : {}), }); - return { response: result.text, model: model }; + return { response: result.text, model }; } diff --git a/src/server/services/models.ts b/src/server/services/models.ts index 3da4ec7..0d091b4 100644 --- a/src/server/services/models.ts +++ b/src/server/services/models.ts @@ -20,7 +20,7 @@ export async function fetchCatalog(): Promise { if (cache && now < cache.expiresAt) return cache.data; const resp = await fetch(MODELS_DEV_API); if (!resp.ok) throw new Error(`models.dev API returned ${resp.status}`); - const data = await resp.json() as ModelsDev laCatalog; + const data = await resp.json() as ModelsDevCatalog; cache = { data, expiresAt: now + CACHE_TTL_MS }; return data; } diff --git a/src/server/services/pty.ts b/src/server/services/pty.ts index 2c57057..c60287c 100644 --- a/src/server/services/pty.ts +++ b/src/server/services/pty.ts @@ -1,49 +1,70 @@ import * as pty from 'node-pty'; -import { WebSocket } from 'ws'; -import { InstanceInfo, InstanceStatus, AgentState } from '../../core/types.js'; +import type { WebSocket } from 'ws'; + +type PtyProcess = pty.IPty; + +interface PtySession { + pty: PtyProcess; + ws?: WebSocket; + /** Recent output, replayed when a client (re)connects. */ + buffer: string; +} + +const BUFFER_LIMIT = 64 * 1024; export class PtyService { - private instances = new Map(); + private sessions = new Map(); - createInstance(id: string, command: string, cwd: string): any { - const ptyProcess = pty.spawn(command || 'bash', [], { + createInstance(id: string, command: string, cwd: string): PtyProcess { + const shell = command || process.env.SHELL || 'bash'; + const ptyProcess = pty.spawn(shell, [], { name: 'xterm-color', cols: 80, rows: 24, cwd: cwd || process.cwd(), - env: process.env, + env: process.env as Record, + }); + + const session: PtySession = { pty: ptyProcess, buffer: '' }; + this.sessions.set(id, session); + + ptyProcess.onData((data: string) => { + session.buffer = (session.buffer + data).slice(-BUFFER_LIMIT); + if (session.ws?.readyState === 1) session.ws.send(data); }); + ptyProcess.onExit(() => { + this.sessions.delete(id); + }); + return ptyProcess; } handleTerminalWs(ws: WebSocket, instanceId: string) { - const ptyProcess = this.instances.get(instanceId)?.pty; - if (!ptyProcess) return; + const session = this.sessions.get(instanceId); + if (!session) { + ws.close(1008, 'unknown instance'); + return; + } - ptyProcess.onData((data: string) => { - ws.send(data); - }); + session.ws = ws; + if (session.buffer) ws.send(session.buffer); ws.on('message', (data) => { - ptyProcess.write(data.toString()); + session.pty.write(data.toString()); }); + // Detach the socket but keep the shell alive so it survives a reload. ws.on('close', () => { - ptyProcess.kill(); - this.instances.delete(instanceId); + if (session.ws === ws) session.ws = undefined; }); } - registerInstance(id: string, pty: any, ws: WebSocket) { - this.instances.set(id, { pty, ws }); - } - removeInstance(id: string) { - const inst = this.instances.get(id); - if (inst) { - inst.pty.kill(); - this.instances.delete(id); - } + const session = this.sessions.get(id); + if (!session) return; + session.ws?.close(); + session.pty.kill(); + this.sessions.delete(id); } } diff --git a/src/ui/components/base.ts b/src/ui/components/base.ts index 255ab47..f74b87f 100644 --- a/src/ui/components/base.ts +++ b/src/ui/components/base.ts @@ -1,8 +1,20 @@ +import type { AppState } from '../state.js'; + export abstract class BaseComponent { - constructor(protected state: any, protected element: HTMLElement) {} + constructor(protected state: AppState, protected element: HTMLElement) {} abstract render(): void; - protected createEl(tag: string, props: any = {}, children: any[] = []): HTMLElement { + /** Escape untrusted text before it is interpolated into an HTML template. */ + protected escape(value: string): string { + return value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + protected createEl(tag: string, props: Record = {}, children: Array = []): HTMLElement { const el = document.createElement(tag); Object.assign(el, props); children.forEach(child => { diff --git a/src/ui/components/instance-dashboard.ts b/src/ui/components/instance-dashboard.ts index fdf28d6..240e545 100644 --- a/src/ui/components/instance-dashboard.ts +++ b/src/ui/components/instance-dashboard.ts @@ -1,43 +1,109 @@ import { BaseComponent } from './base.js'; -import { InstanceInfo, AgentState } from '../../core/types.js'; +import { InstanceKind, InstanceStatus, type InstanceInfo } from '../../core/types.js'; export class InstanceDashboard extends BaseComponent { + private socket?: WebSocket; + + /** Close the terminal socket so a re-render does not leak connections. */ + dispose(): void { + this.socket?.close(); + this.socket = undefined; + } + render(): void { this.element.innerHTML = ` -
-
-
- - main.ts - -
- src/ui · main -
- +
+
+ + main.ts +
-
-
- ${Array.from({length: 20}, (_, i) => `
${i+1}
`).join('')} -
-
-
// Unified TypeScript Codebase
-
// T.C.K rewritten for efficiency.
-
+ src/ui · main +
+ +
+
+
+ ${Array.from({ length: 20 }, (_, i) => `
${i + 1}
`).join('')} +
+
+
// Unified TypeScript Codebase
+
// T.C.K rewritten for efficiency.
+
+
+
+
+ + Terminal — connecting… +
+ shell
-
-
- - Terminal — connected -
- zsh -
-
-
- $ - -
+
+
+ $ +
`; + + void this.connectTerminal(); + } + + private async connectTerminal(): Promise { + const status = this.element.querySelector('#terminal-status'); + const output = this.element.querySelector('#terminal-output'); + const input = this.element.querySelector('#terminal-input'); + if (!status || !output || !input) return; + + let instance: InstanceInfo; + try { + const resp = await fetch('/api/instances', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ kind: InstanceKind.Terminal, label: 'Terminal' }), + }); + if (!resp.ok) throw new Error(`server returned ${resp.status}`); + instance = await resp.json() as InstanceInfo; + } catch (err) { + status.textContent = 'Terminal — unavailable (no local server)'; + output.textContent = `Could not start a terminal: ${err instanceof Error ? err.message : 'request failed'}\n`; + input.disabled = true; + return; + } + + if (instance.status === InstanceStatus.Error) { + status.textContent = 'Terminal — failed to start'; + output.textContent = `${instance.error ?? 'Unknown error'}\n`; + input.disabled = true; + return; + } + + this.state.instances = [instance]; + this.state.persistInstances(); + + const wsProto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const socket = new WebSocket(`${wsProto}//${window.location.host}/ws/pty/${instance.id}`); + this.socket = socket; + + socket.addEventListener('open', () => { + status.textContent = 'Terminal — connected'; + input.disabled = false; + }); + socket.addEventListener('message', (event: MessageEvent) => { + output.textContent += event.data; + output.scrollTop = output.scrollHeight; + }); + socket.addEventListener('close', () => { + status.textContent = 'Terminal — disconnected'; + input.disabled = true; + }); + + input.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key !== 'Enter') return; + e.preventDefault(); + if (socket.readyState !== WebSocket.OPEN) return; + socket.send(`${input.value}\n`); + input.value = ''; + }); } } diff --git a/src/ui/components/right-panel.ts b/src/ui/components/right-panel.ts index 59d3c78..6e48d99 100644 --- a/src/ui/components/right-panel.ts +++ b/src/ui/components/right-panel.ts @@ -13,7 +13,7 @@ export class RightPanel extends BaseComponent { AI Chat
- ${this.state.model} + ${this.escape(this.state.model)}
@@ -31,37 +31,69 @@ export class RightPanel extends BaseComponent {
`; - this.element.querySelector('#chat-send')?.addEventListener('click', async () => { - const input = this.element.querySelector('#chat-input') as HTMLTextAreaElement; + const input = this.element.querySelector('#chat-input'); + const sendBtn = this.element.querySelector('#chat-send'); + const messages = this.element.querySelector('#chat-messages'); + if (!input || !sendBtn || !messages) return; + + const send = async () => { const text = input.value.trim(); - if (!text) return; + if (!text || sendBtn.disabled) return; - const messages = this.element.querySelector('#chat-messages'); - messages.innerHTML += ` -
-
You
-
${text}
-
- `; + this.appendMessage(messages, 'You', text, false); input.value = ''; + sendBtn.disabled = true; - // Call AI API - const resp = await fetch('/api/ai/generate', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - provider: this.state.provider, - model: this.state.model, - prompt: text, - }), - }); - const data = await resp.json(); - messages.innerHTML += ` -
-
Assistant
-
${data.response || data.error}
-
- `; + try { + const resp = await fetch('/api/ai/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + provider: this.state.provider, + model: this.state.model, + prompt: text, + api_key: this.state.provider === 'claude' + ? this.state.settings.claude_api_key || undefined + : undefined, + base_url: this.state.provider === 'ollama' + ? this.state.settings.ollama_host || undefined + : undefined, + }), + }); + const data = await resp.json() as { response?: string; error?: string }; + this.appendMessage(messages, 'Assistant', data.response ?? data.error ?? 'No response', true); + } catch (err) { + const reason = err instanceof Error ? err.message : 'Request failed'; + this.appendMessage(messages, 'Assistant', `Request failed: ${reason}`, true); + } finally { + sendBtn.disabled = false; + } + }; + + sendBtn.addEventListener('click', () => void send()); + input.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + void send(); + } }); } + + /** Append a chat bubble. Text is set via textContent so it is never parsed as HTML. */ + private appendMessage(container: HTMLElement, who: string, text: string, isAi: boolean): void { + const wrapper = document.createElement('div'); + wrapper.className = isAi ? 'msg-ai' : 'msg-user'; + + const label = document.createElement('div'); + label.className = isAi ? 'msg-who ai' : 'msg-who'; + label.textContent = who; + + const bubble = document.createElement('div'); + bubble.className = isAi ? 'msg-bubble-ai' : 'msg-bubble-user'; + bubble.textContent = text; + + wrapper.append(label, bubble); + container.appendChild(wrapper); + container.scrollTop = container.scrollHeight; + } } diff --git a/src/ui/components/settings-modal.ts b/src/ui/components/settings-modal.ts index 9f58ba8..2d32292 100644 --- a/src/ui/components/settings-modal.ts +++ b/src/ui/components/settings-modal.ts @@ -20,11 +20,11 @@ export class SettingsModal extends BaseComponent { `; - this.element.querySelector('#modalClose')?.addEventListener('click', () => { + const close = () => { this.state.showSettings = false; this.render(); - }); - this.element.querySelector('#modalCancel')?.addEventListener('click', () => { - this.state.showSettings = false; - this.render(); - }); + }; + + this.element.querySelector('#modalClose')?.addEventListener('click', close); + this.element.querySelector('#modalCancel')?.addEventListener('click', close); this.element.querySelector('#modalSave')?.addEventListener('click', () => { - this.state.showSettings = false; - this.render(); + const claudeKey = this.element.querySelector('#claudeKey'); + const ollamaHost = this.element.querySelector('#ollamaHost'); + if (claudeKey) this.state.settings.claude_api_key = claudeKey.value; + if (ollamaHost) this.state.settings.ollama_host = ollamaHost.value; + this.state.persistSettings(); + close(); + }); + this.element.querySelector('#settingsModal')?.addEventListener('click', (e: Event) => { + // Only a click on the backdrop itself dismisses the modal. + if (e.target === e.currentTarget) close(); }); } } diff --git a/src/ui/components/title-bar.ts b/src/ui/components/title-bar.ts index 41749cf..0e24670 100644 --- a/src/ui/components/title-bar.ts +++ b/src/ui/components/title-bar.ts @@ -1,5 +1,16 @@ import { BaseComponent } from './base.js'; +const PROVIDERS = [ + { id: 'ollama', label: 'Ollama' }, + { id: 'claude', label: 'Claude' }, + { id: 'openrouter', label: 'OpenRouter' }, + { id: 'openai', label: 'OpenAI' }, +]; + +function currentTheme(): string { + return document.documentElement.dataset.theme ?? 'dark'; +} + export class TitleBar extends BaseComponent { render(): void { this.element.innerHTML = ` @@ -19,13 +30,10 @@ export class TitleBar extends BaseComponent {
- - + +
`; - this.element.querySelector('#providerSelect')?.addEventListener('change', (e: any) => { - this.state.provider = e.target.value; + this.element.querySelector('#providerSelect')?.addEventListener('change', (e: Event) => { + this.state.provider = (e.target as HTMLSelectElement).value; + }); + this.element.querySelector('#modelInput')?.addEventListener('input', (e: Event) => { + this.state.model = (e.target as HTMLInputElement).value; + const pill = document.querySelector('.model-pill'); + if (pill) pill.textContent = this.state.model; }); - this.element.querySelector('#modelInput')?.addEventListener('input', (e: any) => { - this.state.model = e.target.value; + this.element.querySelector('#themeBtn')?.addEventListener('click', () => { + document.documentElement.dataset.theme = currentTheme() === 'dark' ? 'light' : 'dark'; + this.render(); }); this.element.querySelector('#settingsBtn')?.addEventListener('click', () => { this.state.showSettings = true; diff --git a/src/ui/main.ts b/src/ui/main.ts index 7e91685..a7ea0b2 100644 --- a/src/ui/main.ts +++ b/src/ui/main.ts @@ -5,13 +5,17 @@ import { RightPanel } from './components/right-panel.js'; import { SettingsModal } from './components/settings-modal.js'; import { InstanceDashboard } from './components/instance-dashboard.js'; +let dashboard: InstanceDashboard | undefined; +let settingsModal: SettingsModal | undefined; + export function initApp(): void { state.loadInstances(); + state.loadSettings(); renderApp(); - window.addEventListener('hashchange', () => { - renderApp(); - }); + window.addEventListener('hashchange', renderApp); + // Re-render only the modal so open/close does not tear down the terminal. + window.addEventListener('app-state-changed', () => settingsModal?.render()); } function renderApp(): void { @@ -21,6 +25,10 @@ function renderApp(): void { const hash = window.location.hash; const showLanding = !hash || hash === '#'; + dashboard?.dispose(); + dashboard = undefined; + settingsModal = undefined; + if (showLanding) { root.innerHTML = `
@@ -65,9 +73,7 @@ function renderApp(): void {
-
-
-
+
@@ -82,9 +88,15 @@ function renderApp(): void { if (titlebarEl) new TitleBar(state, titlebarEl).render(); if (sidebarEl) new Sidebar(state, sidebarEl).render(); - if (dashboardEl) new InstanceDashboard(state, dashboardEl).render(); + if (dashboardEl) { + dashboard = new InstanceDashboard(state, dashboardEl); + dashboard.render(); + } if (rightPanelEl) new RightPanel(state, rightPanelEl).render(); - if (settingsEl) new SettingsModal(state, settingsEl).render(); + if (settingsEl) { + settingsModal = new SettingsModal(state, settingsEl); + settingsModal.render(); + } } initApp(); diff --git a/src/ui/state.ts b/src/ui/state.ts index e92c9a7..bc1563b 100644 --- a/src/ui/state.ts +++ b/src/ui/state.ts @@ -1,18 +1,43 @@ import { Settings, InstanceInfo } from '../core/types.js'; +const INSTANCES_KEY = 'tck.instances'; +const SETTINGS_KEY = 'tck.settings'; + export class AppState { instances: InstanceInfo[] = []; settings: Settings = { providers: [], commands: [], claude_api_key: '', ollama_host: '', background: '' }; provider = 'claude'; - model = 'claude-opus-4-8'; + model = 'claude-sonnet-4-5'; showSettings = false; persistInstances() { - localStorage.setItem('tck.instances', JSON.stringify(this.instances)); + localStorage.setItem(INSTANCES_KEY, JSON.stringify(this.instances)); } + loadInstances() { - const data = localStorage.getItem('tck.instances'); - if (data) this.instances = JSON.parse(data); + this.instances = readJson(INSTANCES_KEY) ?? []; + } + + persistSettings() { + localStorage.setItem(SETTINGS_KEY, JSON.stringify(this.settings)); + } + + loadSettings() { + const stored = readJson>(SETTINGS_KEY); + if (stored) this.settings = { ...this.settings, ...stored }; } } + +function readJson(key: string): T | null { + const raw = localStorage.getItem(key); + if (!raw) return null; + try { + return JSON.parse(raw) as T; + } catch { + // Corrupt entry — drop it rather than breaking startup. + localStorage.removeItem(key); + return null; + } +} + export const state = new AppState(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0ec7d26 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["node"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noEmit": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": false + }, + "include": ["src/**/*.ts", "vite.config.ts"], + "exclude": ["node_modules", "dist", "tck-ai"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..53bfc65 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; + +// The UI is a plain TypeScript SPA; `index.html` at the repo root is the entry. +export default defineConfig({ + root: '.', + build: { outDir: 'dist', emptyOutDir: true }, + server: { + port: 8080, + proxy: { + '/api': 'http://127.0.0.1:3000', + '/ws': { target: 'ws://127.0.0.1:3000', ws: true }, + }, + }, +}); From 389548af538e9375202dbc2b25d2f7266dc2ffc4 Mon Sep 17 00:00:00 2001 From: Raghuram1234 Date: Thu, 6 Aug 2026 19:47:08 +0000 Subject: [PATCH 2/3] chore: drop Rust-era CodeQL language and PR checklist --- .github/pull_request_template.md | 6 +++--- .github/workflows/codeql.yml | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2324017..36ff3b1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,9 +4,9 @@ ## Checklist -- [ ] `cargo clippy -p tck-core -p tck-server -- -D warnings` passes -- [ ] `trunk build --release` (in `crates/tck-ui`) passes -- [ ] `cargo build -p tck-server --release` passes +- [ ] `npm run typecheck` passes +- [ ] `npm run build` passes +- [ ] `npm run typecheck` in `tck-ai` passes (if that service changed) - [ ] I matched the existing code style ## Notes diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c6724ca..8d8b553 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,9 +25,6 @@ jobs: # JS/TS are analyzed together as one CodeQL language. - language: javascript-typescript build-mode: none - # Rust only supports build-mode: none (no autobuild/manual mode). - - language: rust - build-mode: none steps: - name: Checkout repository From f91c39e58846b408569f02206edec234310f8b68 Mon Sep 17 00:00:00 2001 From: Raghuram1234 Date: Thu, 6 Aug 2026 19:56:28 +0000 Subject: [PATCH 3/3] fix: strip ANSI/OSC escape sequences from terminal output --- src/ui/components/instance-dashboard.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ui/components/instance-dashboard.ts b/src/ui/components/instance-dashboard.ts index 240e545..4308c06 100644 --- a/src/ui/components/instance-dashboard.ts +++ b/src/ui/components/instance-dashboard.ts @@ -1,6 +1,16 @@ import { BaseComponent } from './base.js'; import { InstanceKind, InstanceStatus, type InstanceInfo } from '../../core/types.js'; +// The output pane is plain text, so drop the control sequences a real shell +// emits: OSC strings (incl. VS Code's 633 shell integration) and CSI/escape codes. +const OSC = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g; +const CSI = /\x1b\[[0-9;?]*[ -/]*[@-~]/g; +const ESC = /\x1b[@-Z\\-_]/g; + +function stripAnsi(data: string): string { + return data.replace(OSC, '').replace(CSI, '').replace(ESC, ''); +} + export class InstanceDashboard extends BaseComponent { private socket?: WebSocket; @@ -90,7 +100,7 @@ export class InstanceDashboard extends BaseComponent { input.disabled = false; }); socket.addEventListener('message', (event: MessageEvent) => { - output.textContent += event.data; + output.textContent += stripAnsi(event.data); output.scrollTop = output.scrollHeight; }); socket.addEventListener('close', () => {