diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a148fe..b01e587 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -140,9 +140,18 @@ jobs: shell: bash run: | set -euo pipefail + mkdir -p dist make alfred-workflow cp integrations/alfred-workflow/workflow/arrowhead-search.alfredworkflow dist/arrowhead-search-"${GITHUB_REF_NAME#v}".alfredworkflow + - name: Package Raycast extension + shell: bash + run: | + set -euo pipefail + mkdir -p dist + make raycast-extension + cp integrations/raycast-extension/dist/arrowhead-search.raycast dist/arrowhead-search-"${GITHUB_REF_NAME#v}".raycast + - name: Upload artifacts uses: actions/upload-artifact@v4 with: @@ -150,6 +159,7 @@ jobs: path: | dist/*.tar.gz dist/*.alfredworkflow + dist/*.raycast dist/*.sha256 if-no-files-found: error @@ -222,6 +232,7 @@ jobs: files: | dist/*.tar.gz dist/*.alfredworkflow + dist/*.raycast dist/*.sha256 tag_name: ${{ github.ref_name }} name: ${{ github.ref_name }} diff --git a/Makefile b/Makefile index f59ee66..be79ed4 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ BIN_DIR := $(PREFIX)/bin CARGO_LOCKED := $(if $(filter 0,$(LOCKED)),,--locked) CARGO_FORCE := $(if $(filter 1,$(FORCE)),--force,) -.PHONY: install clean alfred-workflow +.PHONY: install clean alfred-workflow raycast-extension install: @set -euo pipefail; \ BIN_DIR="$(BIN_DIR)"; \ @@ -65,9 +65,13 @@ clean: alfred-workflow: @bash scripts/package-alfred-workflow.sh +raycast-extension: + @bash scripts/package-raycast-extension.sh + .PHONY: help help: @printf 'Available targets:\n' @printf ' install Build and install arrowhead-cli and arrowheadd (override PREFIX, LOCKED=0, FORCE=1 as needed)\n' @printf ' clean Remove target artifacts via cargo clean\n' @printf ' alfred-workflow Package integrations/alfred-workflow into workflow/arrowhead-search.alfredworkflow\n' + @printf ' raycast-extension Package integrations/raycast-extension into dist/arrowhead-search.raycast\n' diff --git a/README.md b/README.md index ff38953..5980698 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,13 @@ Semantic-only matches surface `"N/A"` in the BM25 column of the human-readable o - Run Script: `/usr/bin/python3 src/open_note.py` opens results in Obsidian by default; holding ⌘ routes to the macOS default editor (or a custom command configured via workflow variables). - Ensure the Arrowhead daemon is running before triggering searches. The workflow shells out to `arrowhead search --json` and opens notes in Obsidian by default (hold ⌘ to switch to the macOS default editor). +## Raycast Extension Integration + +- Preferred install: download the latest `arrowhead-search-.raycast` archive from [GitHub Releases](https://github.com/totocaster/arrowhead/releases) and import it via **Raycast → Extensions → Import Extension…**. +- Source lives under `integrations/raycast-extension/`. Run `make raycast-extension` to regenerate `dist/arrowhead-search.raycast`; the script runs `npm ci`, executes `ray build --environment dist --output build`, and zips the build artifacts for distribution. +- The `search` command shells out to `arrowhead search "" --json --include-paths`, debounces input (300 ms), and renders results with Obsidian/default editor actions plus copy and “Reveal in Finder” shortcuts. +- Vault detection mirrors the Alfred integration: honours `VAULT_PATH`/`ARROWHEAD_VAULT_PATH`, falls back to parsing `config.toml`, and resolves absolute note paths when the CLI payload omits them. + ## CLI Reference - `arrowhead init` — bootstrap a vault, seed configuration, and offer auto-start registration when requested. diff --git a/integrations/raycast-extension/.gitignore b/integrations/raycast-extension/.gitignore new file mode 100644 index 0000000..11cfc23 --- /dev/null +++ b/integrations/raycast-extension/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +build/ +.raycast/ +dist/ +*.log diff --git a/integrations/raycast-extension/README.md b/integrations/raycast-extension/README.md new file mode 100644 index 0000000..9a0254a --- /dev/null +++ b/integrations/raycast-extension/README.md @@ -0,0 +1,50 @@ +# Arrowhead Raycast Extension + +Search your Arrowhead vault from Raycast, preview results, and open notes in Obsidian or the system default editor. + +## Layout + +- `src/search.tsx` — Raycast command that shells out to `arrowhead search --json --include-paths` and renders a live-updating list. +- `package.json` — Extension manifest plus build tooling metadata. +- `assets/` — Icons bundled with the extension (`icon.png` mirrors the Alfred workflow icon). +- `build/` — Generated output when running `npm run build` (ignored by git). + +## Preferences + +Expose the following Raycast preferences (configured via the extensions panel): + +| Preference | Default | Description | +| --- | --- | --- | +| `searchMode` | `hybrid` | Search strategy (`fts`, `semantic`, or `hybrid`). | +| `resultLimit` | `15` | Maximum number of results Arrowhead should return. | +| `primaryEditor` | `obsidian` | Editor used for the primary open action (`obsidian` or `default`). | +| `vaultPath` | _(empty)_ | Optional vault override. Leave blank to read Arrowhead’s config. | +| `arrowheadCliPath` | _(empty)_ | Absolute path to the Arrowhead CLI when it is not on `PATH`. | + +The command auto-detects the configured vault when no override is supplied and maps the ⌘ modifier to the alternate editor (Obsidian ⇄ default). + +## CLI Contract + +- Invokes `arrowhead search "" --json --limit --include-paths`. +- Expects the daemon to be running and the vault indexed. +- Parses the JSON payload produced by `render_results` and mirrors the Alfred integration’s field mapping (title, preview, absolute path, reason, etc.). + +## Packaging + +- Install dependencies and build: `npm install && npm run build` (runs `ray build --environment dist --output build`). +- Bundle for distribution: `make raycast-extension` (see `scripts/package-raycast-extension.sh`). The script zips `package.json`, `build/`, and `assets/` into `dist/arrowhead-search.raycast`. +- Import into Raycast via **Extensions → Import Extension…** and selecting the generated `.raycast` archive. + +## Testing Checklist + +- Empty query shows placeholder copy instead of shelling out. +- Search runs for `fts`, `semantic`, and `hybrid` with the configured limit. +- Failure to locate the CLI or vault surfaces descriptive toasts and an empty-state error. +- Notes open in Obsidian by default and fall back to the system editor with ⌘. +- Secondary actions expose copy-to-clipboard and “Reveal in Finder” options when paths are available. + +## Implementation Notes + +- Ensures PATH includes common install locations (`~/.cargo/bin`, `/opt/homebrew/bin`, etc.) before invoking the CLI. +- Mirrors the vault resolution logic from the Alfred scripts (config parsing fallback, supports `VAULT_PATH` override). +- Uses a debounce when running the CLI to avoid hammering the daemon while the user types (300 ms delay). diff --git a/integrations/raycast-extension/assets/icon.png b/integrations/raycast-extension/assets/icon.png new file mode 100644 index 0000000..2aaa532 Binary files /dev/null and b/integrations/raycast-extension/assets/icon.png differ diff --git a/integrations/raycast-extension/package-lock.json b/integrations/raycast-extension/package-lock.json new file mode 100644 index 0000000..f8fb9d2 --- /dev/null +++ b/integrations/raycast-extension/package-lock.json @@ -0,0 +1,1651 @@ +{ + "name": "arrowhead", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arrowhead", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@raycast/api": "^1.103.6" + }, + "devDependencies": { + "@types/node": "^20.8.10", + "@types/react": "^17.0.43", + "@types/react-dom": "^17.0.20", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "typescript": "^4.3.5" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", + "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", + "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.19", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.19.tgz", + "integrity": "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", + "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.21.tgz", + "integrity": "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/external-editor": "^1.0.2", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.21.tgz", + "integrity": "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", + "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.5.tgz", + "integrity": "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.21.tgz", + "integrity": "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.21.tgz", + "integrity": "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.9.0.tgz", + "integrity": "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.0", + "@inquirer/confirm": "^5.1.19", + "@inquirer/editor": "^4.2.21", + "@inquirer/expand": "^4.0.21", + "@inquirer/input": "^4.2.5", + "@inquirer/number": "^3.0.21", + "@inquirer/password": "^4.0.21", + "@inquirer/rawlist": "^4.1.9", + "@inquirer/search": "^3.2.0", + "@inquirer/select": "^4.4.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.9.tgz", + "integrity": "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", + "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.0.tgz", + "integrity": "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", + "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@oclif/core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.8.0.tgz", + "integrity": "sha512-jteNUQKgJHLHFbbz806aGZqf+RJJ7t4gwF4MYa8fCwCxQ8/klJNWc0MvaJiBebk7Mc+J39mdlsB4XraaCKznFw==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-autocomplete": { + "version": "3.2.39", + "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.39.tgz", + "integrity": "sha512-OwAZNnSpuDjKyhAwoOJkFWxGswPFKBB4hpNIMsj6PUtbKwGBPmD+2wGGPgTsDioVwLmUELSb2bZ+1dxHfvXmvg==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4", + "ansis": "^3.16.0", + "debug": "^4.4.1", + "ejs": "^3.1.10" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-help": { + "version": "6.2.35", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.35.tgz", + "integrity": "sha512-ZMcQTsHaiCEOZIRZoynUQ+98fyM1Adoqx4LbOgYWRVKXKbavHPCZKm6F+/y0GpWscXVoeGnvJO6GIBsigrqaSA==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-not-found": { + "version": "3.2.72", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.72.tgz", + "integrity": "sha512-CRcqHGdcEL4l5cls5F9FvwKt04LkdG7WyFozOu2vP1/3w34S29zbw8Tx1gAzfBZDDme5ChSaqFXU5qbTLx5yYQ==", + "license": "MIT", + "dependencies": { + "@inquirer/prompts": "^7.9.0", + "@oclif/core": "^4.8.0", + "ansis": "^3.17.0", + "fast-levenshtein": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@raycast/api": { + "version": "1.103.6", + "resolved": "https://registry.npmjs.org/@raycast/api/-/api-1.103.6.tgz", + "integrity": "sha512-x34deBLt8BNCIztMjsYPU8eGz4xFp444PJzxybIvDt86ZCk6phlTLD11qtkyZKOURFIoncebNDVTJn5KdO09nA==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4.5.4", + "@oclif/plugin-autocomplete": "^3.2.35", + "@oclif/plugin-help": "^6.2.33", + "@oclif/plugin-not-found": "^3.2.68", + "@types/node": "22.13.10", + "@types/react": "19.0.10", + "esbuild": "^0.25.10", + "react": "19.0.0" + }, + "bin": { + "ray": "bin/run.js" + }, + "engines": { + "node": ">=22.14.0" + }, + "peerDependencies": { + "@types/node": "22.13.10", + "@types/react": "19.0.10", + "react-devtools": "6.1.1" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "react-devtools": { + "optional": true + } + } + }, + "node_modules/@raycast/api/node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@raycast/api/node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@raycast/api/node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@raycast/api/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "17.0.89", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.89.tgz", + "integrity": "sha512-I98SaDCar5lvEYl80ClRIUztH/hyWHR+I2f+5yTVp/MQ205HgYkA2b5mVdry/+nsEIrf8I65KA5V/PASx68MsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "^0.16", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.26.tgz", + "integrity": "sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "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/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-levenshtein": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.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==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/integrations/raycast-extension/package.json b/integrations/raycast-extension/package.json new file mode 100644 index 0000000..aae9991 --- /dev/null +++ b/integrations/raycast-extension/package.json @@ -0,0 +1,104 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "arrowhead", + "title": "Arrowhead Search", + "description": "Query Arrowhead notes directly from Raycast and open them in Obsidian or your default editor.", + "version": "0.1.0", + "icon": "assets/icon.png", + "author": "Arrowhead", + "categories": [ + "Productivity" + ], + "license": "MIT", + "commands": [ + { + "name": "search", + "title": "Search Arrowhead Notes", + "description": "Run Arrowhead CLI searches from Raycast.", + "mode": "view" + } + ], + "preferences": [ + { + "name": "searchMode", + "type": "dropdown", + "title": "Search Mode", + "description": "Choose which Arrowhead search strategy to run.", + "required": true, + "default": "hybrid", + "data": [ + { + "title": "Hybrid (balanced)", + "value": "hybrid" + }, + { + "title": "Full-text (fastest)", + "value": "fts" + }, + { + "title": "Semantic (vector)", + "value": "semantic" + } + ] + }, + { + "name": "resultLimit", + "type": "textfield", + "title": "Result Limit", + "description": "Maximum number of results to request from Arrowhead.", + "placeholder": "15", + "required": false, + "default": "15" + }, + { + "name": "primaryEditor", + "type": "dropdown", + "title": "Primary Editor", + "description": "Editor opened when hitting Return.", + "required": true, + "default": "obsidian", + "data": [ + { + "title": "Obsidian", + "value": "obsidian" + }, + { + "title": "System Default", + "value": "default" + } + ] + }, + { + "name": "vaultPath", + "type": "textfield", + "title": "Vault Path Override", + "description": "Optional path to an Arrowhead-compatible vault. Leave blank to read Arrowhead config.", + "placeholder": "/Users/you/Documents/Notes", + "required": false + }, + { + "name": "arrowheadCliPath", + "type": "textfield", + "title": "Arrowhead CLI Path", + "description": "Override path for the Arrowhead CLI if it is not on PATH.", + "placeholder": "/usr/local/bin/arrowhead", + "required": false + } + ], + "dependencies": { + "@raycast/api": "^1.103.6" + }, + "devDependencies": { + "@types/node": "^20.8.10", + "@types/react": "^17.0.43", + "@types/react-dom": "^17.0.20", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "typescript": "^4.3.5" + }, + "scripts": { + "build": "ray build --environment dist --output build", + "lint": "ray lint", + "dev": "ray develop" + } +} diff --git a/integrations/raycast-extension/raycast-env.d.ts b/integrations/raycast-extension/raycast-env.d.ts new file mode 100644 index 0000000..ee9ac88 --- /dev/null +++ b/integrations/raycast-extension/raycast-env.d.ts @@ -0,0 +1,35 @@ +/// + +/* 🚧 🚧 🚧 + * This file is auto-generated from the extension's manifest. + * Do not modify manually. Instead, update the `package.json` file. + * 🚧 🚧 🚧 */ + +/* eslint-disable @typescript-eslint/ban-types */ + +type ExtensionPreferences = { + /** Search Mode - Choose which Arrowhead search strategy to run. */ + "searchMode": "hybrid" | "fts" | "semantic", + /** Result Limit - Maximum number of results to request from Arrowhead. */ + "resultLimit": string, + /** Primary Editor - Editor opened when hitting Return. */ + "primaryEditor": "obsidian" | "default", + /** Vault Path Override - Optional path to an Arrowhead-compatible vault. Leave blank to read Arrowhead config. */ + "vaultPath"?: string, + /** Arrowhead CLI Path - Override path for the Arrowhead CLI if it is not on PATH. */ + "arrowheadCliPath"?: string +} + +/** Preferences accessible in all the extension's commands */ +declare type Preferences = ExtensionPreferences + +declare namespace Preferences { + /** Preferences accessible in the `search` command */ + export type Search = ExtensionPreferences & {} +} + +declare namespace Arguments { + /** Arguments passed to the `search` command */ + export type Search = {} +} + diff --git a/integrations/raycast-extension/src/search.tsx b/integrations/raycast-extension/src/search.tsx new file mode 100644 index 0000000..ebbc929 --- /dev/null +++ b/integrations/raycast-extension/src/search.tsx @@ -0,0 +1,629 @@ +import { Action, ActionPanel, Icon, List, Toast, getPreferenceValues, open, showHUD, showToast } from "@raycast/api"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { execFile } from "child_process"; +import { promisify } from "util"; +import fs from "fs"; +import os from "os"; +import path from "path"; + +const execFileAsync = promisify(execFile); +const DEFAULT_RESULT_LIMIT = 15; +const SEARCH_DEBOUNCE_MS = 300; +const CLI_TIMEOUT_MS = 5000; +const ADDITIONAL_PATH_DIRS = [ + path.join(os.homedir(), ".local", "bin"), + path.join(os.homedir(), ".local", "sbin"), + path.join(os.homedir(), ".cargo", "bin"), + path.join(os.homedir(), "bin"), + "/opt/homebrew/bin", + "/opt/homebrew/sbin", + "/usr/local/opt/arrowhead/bin", + "/usr/local/bin", + "/usr/local/sbin", +]; +const ALTERNATE_EDITOR: Record = { + obsidian: "default", + default: "obsidian", +}; + +type EditorChoice = "obsidian" | "default"; + +type Preferences = { + searchMode: "fts" | "semantic" | "hybrid"; + resultLimit?: string; + primaryEditor: EditorChoice; + vaultPath?: string; + arrowheadCliPath?: string; +}; + +type SearchResult = { + note_id?: string; + title?: string; + relative_path?: string; + absolute_path?: string; + preview?: string; + reason?: string; + score?: number; + bm25?: number; + metadata?: Record; +}; + +type SearchPayload = SearchResult[]; + +type QueryError = { + title: string; + message?: string; +}; + +type CommandState = + | { kind: "idle" } + | { kind: "loading" } + | { kind: "ready"; results: SearchPayload } + | { kind: "error"; error: QueryError }; + +export default function Command() { + const preferences = getPreferenceValues(); + const [query, setQuery] = useState(""); + const [state, setState] = useState({ kind: "idle" }); + + const config = useMemo(() => { + const limit = parseInt(preferences.resultLimit ?? "", 10); + const resultLimit = Number.isFinite(limit) && limit > 0 ? limit : DEFAULT_RESULT_LIMIT; + return { + searchMode: normalizeSearchMode(preferences.searchMode), + resultLimit, + primaryEditor: normalizeEditor(preferences.primaryEditor), + vaultPath: normalizeVault(preferences.vaultPath), + cliPath: resolveCliPath(preferences.arrowheadCliPath), + }; + }, [preferences]); + + useSearchEffect(query, config, setState); + + const placeholder = useMemo(() => { + const modeLabel = config.searchMode.toUpperCase(); + const editorLabel = describeEditor(config.primaryEditor); + return { + title: "Search Arrowhead notes", + description: `${modeLabel} search • Enter a query to fetch notes (Return opens in ${editorLabel}).`, + }; + }, [config]); + + const secondaryEditor = ALTERNATE_EDITOR[config.primaryEditor]; + const showDetail = state.kind === "ready" && state.results.some((item) => Boolean(item.preview?.trim())); + + return ( + + {query.trim().length === 0 ? ( + + ) : null} + {state.kind === "error" ? ( + + ) : null} + {state.kind === "ready" && state.results.length === 0 ? ( + + ) : null} + {state.kind === "ready" + ? state.results.map((result) => { + const noteId = result.note_id ?? ""; + const title = selectTitle(result); + const subtitle = selectSubtitle(result); + const absolutePath = resolveAbsolutePath(result, config.vaultPath); + const relativePath = result.relative_path; + const preview = result.preview?.trim(); + + return ( + + ) : undefined + } + actions={ + + openNote(result, config.primaryEditor, config.vaultPath)} + /> + openNote(result, secondaryEditor, config.vaultPath)} + /> + {absolutePath ? ( + <> + + + + ) : ( + + )} + + + } + /> + ); + }) + : null} + + ); +} + +function useSearchEffect( + query: string, + config: { + searchMode: "fts" | "semantic" | "hybrid"; + resultLimit: number; + cliPath: string; + vaultPath?: string; + primaryEditor: EditorChoice; + }, + setState: (state: CommandState) => void, +) { + const lastQueryRef = useRef(""); + + useEffect(() => { + const trimmed = query.trim(); + if (trimmed.length === 0) { + setState({ kind: "idle" }); + lastQueryRef.current = ""; + return; + } + + let isCancelled = false; + setState({ kind: "loading" }); + + const timer = setTimeout(async () => { + try { + const { results } = await runSearch(trimmed, config); + if (isCancelled) { + return; + } + lastQueryRef.current = trimmed; + setState({ kind: "ready", results }); + } catch (error) { + if (isCancelled) { + return; + } + const message = error instanceof Error ? error.message : String(error); + setState({ kind: "error", error: { title: "arrowhead search failed", message } }); + await showToast({ + style: Toast.Style.Failure, + title: "Arrowhead search failed", + message, + }); + } + }, SEARCH_DEBOUNCE_MS); + + return () => { + isCancelled = true; + clearTimeout(timer); + }; + }, [query, config.cliPath, config.resultLimit, config.searchMode, config.vaultPath, setState]); +} + +async function runSearch( + query: string, + config: { + searchMode: "fts" | "semantic" | "hybrid"; + resultLimit: number; + cliPath: string; + vaultPath?: string; + }, +): Promise<{ results: SearchPayload }> { + const env = augmentPath(process.env); + const args = [ + "search", + config.searchMode, + query, + "--json", + "--limit", + String(config.resultLimit), + "--include-paths", + ]; + + if (config.vaultPath && !args.includes("--vault")) { + args.push("--vault", config.vaultPath); + } + + try { + const { stdout } = await execFileAsync(config.cliPath, args, { + env, + timeout: CLI_TIMEOUT_MS, + maxBuffer: 10 * 1024 * 1024, + }); + const payload = JSON.parse(stdout); + if (!Array.isArray(payload)) { + throw new Error("Unexpected search payload (expected JSON array)."); + } + return { results: payload as SearchPayload }; + } catch (error) { + if (isNotFoundError(error)) { + throw new Error("arrowhead CLI not found. Adjust the extension preferences."); + } + if (error instanceof Error) { + const execError = error as NodeJS.ErrnoException & { killed?: boolean; stderr?: string }; + if (execError.killed) { + throw new Error("arrowhead search timed out. Try lowering the result limit."); + } + if (typeof execError.stderr === "string") { + const stderr = execError.stderr.trim(); + if (stderr.length > 0) { + throw new Error(stderr.split("\n")[0]); + } + } + throw error; + } + throw new Error(String(error)); + } +} + +async function openNote(result: SearchResult, editor: EditorChoice, vaultPath?: string) { + const absolutePath = resolveAbsolutePath(result, vaultPath); + const noteId = result.note_id ?? ""; + + if (!absolutePath) { + await showToast({ + style: Toast.Style.Failure, + title: "Unable to resolve note path", + message: "Check your vault configuration.", + }); + return; + } + + if (editor === "obsidian") { + const uri = obsidianUri(absolutePath, vaultPath); + if (uri) { + await open(uri); + return; + } + await openWithApp(absolutePath, "md.obsidian"); + return; + } + + await open(absolutePath); + await showHUD(`Opened ${noteId || path.basename(absolutePath)}`); +} + +function resolveCliPath(override?: string): string { + if (override && override.trim().length > 0) { + const expanded = expandHome(override.trim()); + return expanded; + } + + const envOverride = process.env.ARROWHEAD_CLI_PATH; + if (envOverride && envOverride.trim().length > 0) { + return expandHome(envOverride.trim()); + } + + const candidates = collectPathCandidates(); + for (const candidate of candidates) { + if (isExecutable(candidate)) { + return candidate; + } + } + return "arrowhead"; +} + +function collectPathCandidates(): string[] { + const currentPaths = (process.env.PATH ?? "") + .split(path.delimiter) + .filter(Boolean) + .map((entry) => path.join(entry, "arrowhead")); + const fallback = ADDITIONAL_PATH_DIRS.map((dir) => path.join(dir, "arrowhead")); + return [...currentPaths, ...fallback]; +} + +function isExecutable(candidate: string): boolean { + try { + const stats = fs.statSync(candidate); + if (!stats.isFile()) { + return false; + } + fs.accessSync(candidate, fs.constants.X_OK); + return true; + } catch { + return false; + } +} + +function augmentPath(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { + const updated = { ...env }; + const entries = new Set(); + const existing = env.PATH ? env.PATH.split(path.delimiter) : []; + for (const entry of ADDITIONAL_PATH_DIRS) { + entries.add(entry); + } + for (const entry of existing) { + entries.add(entry); + } + updated.PATH = Array.from(entries).join(path.delimiter); + return updated; +} + +function resolveAbsolutePath(result: SearchResult, vaultPath?: string): string | undefined { + const absolute = result.absolute_path?.trim(); + if (absolute) { + return absolute; + } + if (vaultPath) { + const relative = result.relative_path?.trim(); + if (relative) { + const joined = path.join(vaultPath, relative); + if (fs.existsSync(joined)) { + return path.resolve(joined); + } + } + const noteId = result.note_id?.trim(); + if (noteId) { + const candidate = path.join(vaultPath, `${noteId}.md`); + if (fs.existsSync(candidate)) { + return path.resolve(candidate); + } + } + } + return undefined; +} + +function obsidianUri(notePath: string, vaultPath?: string): string | undefined { + try { + const resolved = path.resolve(notePath); + const encodedPath = encodeURIComponent(resolved); + const params = [`path=${encodedPath}`]; + if (vaultPath) { + const vaultName = path.basename(vaultPath); + if (vaultName.trim().length > 0) { + params.push(`vault=${encodeURIComponent(vaultName)}`); + } + } + return `obsidian://open?${params.join("&")}`; + } catch { + return undefined; + } +} + +async function openWithApp(filePath: string, bundleId: string) { + const args = ["-b", bundleId, filePath]; + try { + await execFileAsync("open", args, { timeout: CLI_TIMEOUT_MS }); + } catch (error) { + await open(filePath); + if (error instanceof Error) { + await showToast({ + style: Toast.Style.Animated, + title: "Opened with default editor", + message: error.message, + }); + } + } +} + +function normalizeSearchMode(value: string | undefined): "fts" | "semantic" | "hybrid" { + if (!value) { + return "hybrid"; + } + const normalised = value.toLowerCase(); + if (normalised === "fts" || normalised === "semantic" || normalised === "hybrid") { + return normalised; + } + return "hybrid"; +} + +function normalizeEditor(value: string | undefined): EditorChoice { + if (!value) { + return "obsidian"; + } + const normalised = value.toLowerCase(); + return normalised === "default" ? "default" : "obsidian"; +} + +function normalizeVault(value: string | undefined): string | undefined { + if (!value || value.trim().length === 0) { + return inferVaultPath(); + } + const expanded = expandHome(value.trim()); + return fs.existsSync(expanded) ? expanded : undefined; +} + +function inferVaultPath(): string | undefined { + const envOverride = process.env.VAULT_PATH ?? process.env.ARROWHEAD_VAULT_PATH; + if (envOverride && envOverride.trim().length > 0) { + const expanded = expandHome(envOverride.trim()); + if (fs.existsSync(expanded)) { + return expanded; + } + } + + const configOverride = process.env.ARROWHEAD_CONFIG_PATH; + for (const candidate of configCandidates(configOverride)) { + const pathFromConfig = parseVaultFromConfig(candidate); + if (pathFromConfig) { + return pathFromConfig; + } + } + return undefined; +} + +function configCandidates(override?: string): string[] { + if (override && override.trim().length > 0) { + return [expandHome(override.trim())]; + } + + const home = os.homedir(); + const candidates = []; + if (process.platform === "darwin") { + candidates.push(path.join(home, "Library", "Application Support", "Arrowhead", "config.toml")); + } else if (process.platform === "win32") { + const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming"); + candidates.push(path.join(appData, "Arrowhead", "config.toml")); + } else { + const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(home, ".config"); + candidates.push(path.join(xdgConfig, "Arrowhead", "config.toml")); + } + candidates.push(path.join(home, ".config", "arrowhead", "config.toml")); + return candidates; +} + +function parseVaultFromConfig(configPath: string): string | undefined { + try { + const content = fs.readFileSync(configPath, "utf8"); + const lines = content.split(/\r?\n/); + for (const rawLine of lines) { + const line = rawLine.trim(); + if (!line || line.startsWith("#")) { + continue; + } + if (line.toLowerCase().startsWith("vault")) { + const [, rhs] = line.split("=", 2); + if (!rhs) { + continue; + } + const withoutComment = rhs.split("#", 1)[0].trim(); + const parsed = parseTomlString(withoutComment); + if (parsed) { + const expanded = expandHome(parsed); + if (fs.existsSync(expanded)) { + return expanded; + } + } + break; + } + } + } catch { + // Ignore read/parsing failures. + } + return undefined; +} + +function parseTomlString(value: string): string | undefined { + if (value.startsWith('"') && value.endsWith('"')) { + try { + return JSON.parse(value); + } catch { + return undefined; + } + } + if (value.startsWith("'") && value.endsWith("'")) { + return value.slice(1, -1); + } + return undefined; +} + +function expandHome(input: string): string { + if (!input.startsWith("~")) { + return input; + } + return path.join(os.homedir(), input.slice(1)); +} + +function selectTitle(result: SearchResult): string { + if (result.title && result.title.trim().length > 0) { + return result.title.trim(); + } + const metadata = result.metadata; + if (metadata) { + const metaTitle = metadata["title"]; + if (typeof metaTitle === "string" && metaTitle.trim().length > 0) { + return metaTitle.trim(); + } + } + return result.note_id ?? ""; +} + +function selectSubtitle(result: SearchResult): string { + const segments: string[] = []; + if (result.preview) { + segments.push(condenseWhitespace(result.preview)); + } + const scores: string[] = []; + if (typeof result.score === "number") { + scores.push(`score ${result.score.toFixed(3)}`); + } + if (typeof result.bm25 === "number") { + scores.push(`BM25 ${result.bm25.toFixed(2)}`); + } + if (scores.length > 0) { + segments.push(scores.join(" • ")); + } + if (result.reason && segments.length === 0) { + segments.push(result.reason); + } + return segments.join(" — "); +} + +function collectKeywords(result: SearchResult): string[] { + const keywords = new Set(); + if (result.title) { + keywords.add(result.title); + } + if (result.note_id) { + keywords.add(result.note_id); + } + if (result.relative_path) { + keywords.add(result.relative_path); + } + if (result.reason) { + keywords.add(result.reason); + } + if (result.preview) { + keywords.add(condenseWhitespace(result.preview)); + } + return Array.from(keywords).filter(Boolean); +} + +function buildAccessories(result: SearchResult): List.Item.Accessory[] { + const accessories: List.Item.Accessory[] = []; + if (typeof result.score === "number") { + accessories.push({ text: `score ${result.score.toFixed(3)}` }); + } + if (typeof result.bm25 === "number") { + accessories.push({ text: `BM25 ${result.bm25.toFixed(2)}` }); + } + if (result.reason) { + accessories.push({ tooltip: result.reason, icon: Icon.Info }); + } + return accessories; +} + +function describeEditor(editor: EditorChoice): string { + return editor === "obsidian" ? "Obsidian" : "the default editor"; +} + +function condenseWhitespace(value: string): string { + return value.replace(/\s+/g, " ").trim(); +} + +function isNotFoundError(error: unknown): boolean { + return Boolean(error && typeof error === "object" && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT"); +} diff --git a/integrations/raycast-extension/tsconfig.json b/integrations/raycast-extension/tsconfig.json new file mode 100644 index 0000000..baaf6f1 --- /dev/null +++ b/integrations/raycast-extension/tsconfig.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Arrowhead Raycast Extension", + "compilerOptions": { + "lib": ["es2020"], + "module": "commonjs", + "target": "es2020", + "moduleResolution": "node", + "types": ["node", "@raycast/api"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "resolveJsonModule": true + }, + "include": ["src"], + "exclude": ["node_modules", "build"] +} diff --git a/scripts/package-raycast-extension.sh b/scripts/package-raycast-extension.sh new file mode 100644 index 0000000..ce4b467 --- /dev/null +++ b/scripts/package-raycast-extension.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Package the Arrowhead Raycast extension into a .raycast archive. + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +EXT_ROOT="$ROOT_DIR/integrations/raycast-extension" +DIST_DIR="$EXT_ROOT/dist" +BUILD_DIR="$EXT_ROOT/build" +ARCHIVE_NAME="arrowhead-search.raycast" +OUTPUT_PATH="$DIST_DIR/$ARCHIVE_NAME" + +if [[ ! -f "$EXT_ROOT/package.json" ]]; then + echo "Missing Raycast extension manifest at $EXT_ROOT/package.json" >&2 + exit 1 +fi + +mkdir -p "$DIST_DIR" + +pushd "$EXT_ROOT" >/dev/null + +cleanup() { + rm -rf "$EXT_ROOT/node_modules" "$BUILD_DIR" "$tmpdir" +} +tmpdir="$(mktemp -d)" +trap cleanup EXIT + +if command -v npm >/dev/null 2>&1; then + npm ci >/dev/null +else + echo "npm is required to package the Raycast extension" >&2 + exit 1 +fi + +npm run build >/dev/null + +if [[ ! -d "$BUILD_DIR" ]]; then + echo "Raycast build output not found at $BUILD_DIR" >&2 + exit 1 +fi + +if [[ -f "$EXT_ROOT/raycast-env.d.ts" ]]; then + cp "$EXT_ROOT/raycast-env.d.ts" "$BUILD_DIR/" +fi + +rm -f "$OUTPUT_PATH" + +cp -R "$BUILD_DIR"/. "$tmpdir"/ +pushd "$tmpdir" >/dev/null +zip -qr "$OUTPUT_PATH" . +popd >/dev/null + +popd >/dev/null + +echo "Packaged Raycast extension at $OUTPUT_PATH"