diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..3a4aa3c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug Report +about: Create a report to help us improve +title: "[BUG] " +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environment (please complete the following information):** + - OS: [e.g. macOS, Windows] + - App Version [e.g. 1.0.0] + +**Additional context** +Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..46cdc58 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea for this project +title: "[FEAT] " +labels: feature +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..c2f00d8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +--- +name: Pull Request +about: Propose a change to the codebase +--- + +## Summary of Changes + +Please provide a brief, high-level summary of the changes in this pull request. + +## Related Issue + +- Closes #XXX + +*Please replace `XXX` with the issue number that this pull request resolves. If it does not resolve a specific issue, please explain why this change is needed.* + +## Contributor's Self-Review Checklist + +Please check the boxes that apply. This is a reminder of what we look for in a good pull request. + +- [ ] I have read the [CONTRIBUTING.md](https://github.com/your-org/your-repo/blob/main/CONTRIBUTING.md) document. +- [ ] My code follows the project's coding style and architectural patterns as described in [DESIGN_PATTERNS.md](https://github.com/your-org/your-repo/blob/main/docs/DESIGN_PATTERNS.md). +- [ ] I have added or updated relevant tests for my changes. +- [ ] I have updated the documentation to reflect my changes (if applicable). +- [ ] My changes have been tested locally and are working as expected. + +## Additional Context (Optional) + +Add any other context or screenshots about the pull request here. \ No newline at end of file diff --git a/.github/workflows/assign-on-comment.yml b/.github/workflows/assign-on-comment.yml new file mode 100644 index 0000000..7c73f60 --- /dev/null +++ b/.github/workflows/assign-on-comment.yml @@ -0,0 +1,62 @@ +name: Assign on Comment + +on: + issue_comment: + types: [created] + +jobs: + # Job 1: Any contributor can self-assign + self-assign: + # Only run if the comment is exactly '/assign' + if: github.event.comment.body == '/assign' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Assign commenter to the issue + uses: actions/github-script@v7 + with: + script: | + // Assign the commenter as the assignee + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + assignees: [context.actor] + }); + // Add a rocket (๐Ÿš€) reaction to indicate success + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket' + }); + + # Job 2: Admin can assign others + assign-others: + # Only run if the comment starts with '/assign @' and the commenter is in the admin group + if: startsWith(github.event.comment.body, '/assign @') && contains(fromJson('["OWNER", "COLLABORATOR", "MEMBER"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Assign mentioned user + uses: actions/github-script@v7 + with: + script: | + const mention = context.payload.comment.body.split(' ')[1]; + const assignee = mention.substring(1); // Remove '@' + // Assign the mentioned user as the assignee + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + assignees: [assignee] + }); + // Add a thumbs up (+1) reaction to indicate success + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1' + }); \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26ad1a2..fb77d69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,52 +2,57 @@ Thank you for considering contributing to **Glass by Pickle**! Contributions make the open-source community vibrant, innovative, and collaborative. We appreciate every contribution you makeโ€”big or small. -## ๐Ÿ“Œ Contribution Guidelines - -### ๐Ÿ‘ฅ Avoid Work Duplication - -Before creating an issue or submitting a pull request (PR), please check existing [Issues](https://github.com/pickle-com/glass/issues) and [Pull Requests](https://github.com/pickle-com/glass/pulls) to prevent duplicate efforts. - -### โœ… Start with Approved Issues - -- **Feature Requests**: Please wait for approval from core maintainers before starting work. Issues needing approval are marked with the `๐Ÿšจ needs approval` label. -- **Bugs & Improvements**: You may begin immediately without explicit approval. - -### ๐Ÿ“ Clearly Document Your Work - -Provide enough context and detail to allow easy understanding. Issues and PRs should clearly communicate the problem or feature and stand alone without external references. - -### ๐Ÿ’ก Summarize Pull Requests - -Include a brief summary at the top of your PR, describing the intent and scope of your changes. - -### ๐Ÿ”— Link Related Issues - -Use GitHub keywords (`Closes #123`, `Fixes #456`) to auto-link and close issues upon PR merge. - -### ๐Ÿงช Include Testing Information - -Clearly state how your changes were tested. - -> Example: -> "Tested locally on macOS 14, confirmed all features working as expected." - -### ๐Ÿง  Future-Proof Your Descriptions - -Document trade-offs, edge cases, and temporary workarounds clearly to help future maintainers understand your decisions. +This document guides you through the entire contribution process, from finding an issue to getting your pull request merged. --- -## ๐Ÿ”– Issue Priorities +## ๐Ÿš€ Contribution Workflow -| Issue Type | Priority | -|----------------------------------------------------|---------------------| -| Minor enhancements & non-core feature requests | ๐ŸŸข Low Priority | -| UX improvements & minor bugs | ๐ŸŸก Medium Priority | -| Core functionalities & essential features | ๐ŸŸ  High Priority | -| Critical bugs & breaking issues | ๐Ÿ”ด Urgent | -| +To ensure a smooth and effective workflow, all contributions must go through the following process. Please follow these steps carefully. +### 1. Find or Create an Issue + +All work begins with an issue. This is the central place to discuss new ideas and track progress. + +- Browse our existing [**Issues**](https://github.com/pickle-com/glass/issues) to find something you'd like to work on. We recommend looking for issues labeled `good first issue` if you're new! +- If you have a new idea or find a bug that hasn't been reported, please **create a new issue** using our templates. + +### 2. Claim the Issue + +To avoid duplicate work, you must claim an issue before you start coding. + +- On the issue you want to work on, leave a comment with the command: + ``` + /assign + ``` +- Our GitHub bot will automatically assign the issue to you. Once your profile appears in the **`Assignees`** section on the right, you are ready to start development. + +### 3. Fork & Create a Branch + +Now it's time to set up your local environment. + +1. **Fork** the repository to your own GitHub account. +2. **Clone** your forked repository to your local machine. +3. **Create a new branch** from `main`. A clear branch name is recommended. + - For new features: `feat/short-description` (e.g., `feat/user-login-ui`) + - For bug fixes: `fix/short-description` (e.g., `fix/header-rendering-bug`) + +### 4. Develop + +Write your code! As you work, please adhere to our quality standards. + +- **Code Style & Quality**: Our project uses `Prettier` and `ESLint` to maintain a consistent code style. +- **Architecture & Design Patterns**: All new code must be consistent with the project's architecture. Please read our **[Design Patterns Guide](https://github.com/pickle-com/glass/blob/main/docs/DESIGN_PATTERNS.md)** before making significant changes. + +### 5. Create a Pull Request (PR) + +Once your work is ready, create a Pull Request to the `main` branch of the original repository. + +- **Fill out the PR Template**: Our template will appear automatically. Please provide a clear summary of your changes. +- **Link the Issue**: In the PR description, include the line `Closes #XXX` (e.g., `Closes #123`) to link it to the issue you resolved. This is mandatory. +- **Code Review**: A maintainer will review your code, provide feedback, and merge it. + +--- # Developing @@ -85,11 +90,4 @@ Please ensure that you can make a full production build before pushing code. npm run lint ``` -If you get errors, be sure to fix them before committing. - - -## Making a Pull Request - -- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) when creating your PR. (This option isn't available if you're [contributing from a fork belonging to an organization](https://github.com/orgs/community/discussions/5634)) -- If your PR refers to or fixes an issue, add `refs #XXX` or `fixes #XXX` to the PR description. Replace `XXX` with the respective issue number. See more about [linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). -- Lastly, make sure to keep your branches updated (e.g., click the `Update branch` button on the GitHub PR page). \ No newline at end of file +If you get errors, be sure to fix them before committing. \ No newline at end of file diff --git a/README.md b/README.md index e5510e2..0afea2c 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ npm run setup **Currently Supporting:** - OpenAI API: Get OpenAI API Key [here](https://platform.openai.com/api-keys) - Gemini API: Get Gemini API Key [here](https://aistudio.google.com/apikey) -- Local LLM (WIP) +- Local LLM Ollama & Whisper ### Liquid Glass Design (coming soon) @@ -115,8 +115,6 @@ We have a list of [help wanted](https://github.com/pickle-com/glass/issues?q=is% | Status | Issue | Description | |--------|--------------------------------|---------------------------------------------------| -| ๐Ÿšง WIP | Local LLM Support | Supporting Local LLM to power AI answers | -| ๐Ÿšง WIP | Firebase Data Storage Issue | Session & ask should be saved in firebase for signup users | | ๐Ÿšง WIP | Liquid Glass | Liquid Glass UI for MacOS 26 | ### Changelog @@ -125,7 +123,7 @@ We have a list of [help wanted](https://github.com/pickle-com/glass/issues?q=is% - Jul 6: Full code refactoring has done. - Jul 7: Now support Claude, LLM/STT model selection - Jul 8: Now support Windows(beta), Improved AEC by Rust(to seperate mic/system audio), shortcut editing(beta) - +- Jul 8: Now support Local LLM & STT, Firebase Data Storage ## About Pickle diff --git a/docs/DESIGN_PATTERNS.md b/docs/DESIGN_PATTERNS.md new file mode 100644 index 0000000..bdc5fb6 --- /dev/null +++ b/docs/DESIGN_PATTERNS.md @@ -0,0 +1,126 @@ +# Glass: Design Patterns and Architectural Overview + +Welcome to the Glass project! This document is the definitive guide to the architectural patterns, conventions, and design philosophy that guide our development. Adhering to these principles is essential for building new features, maintaining the quality of our codebase, and ensuring a stable, consistent developer experience. + +The architecture is designed to be modular, robust, and clear, with a strict separation of concerns. + +--- + +## Core Architectural Principles + +These are the fundamental rules that govern the entire application. + +1. **Centralized Data Logic**: All data persistence logic (reading from or writing to a database) is centralized within the **Electron Main Process**. The UI layers (both Electron's renderer and the web dashboard) are forbidden from accessing data sources directly. +2. **Feature-Based Modularity**: Code is organized by feature (`src/features`) to promote encapsulation and separation of concerns. A new feature should be self-contained within its own directory. +3. **Dual-Database Repositories**: The data access layer uses a **Repository Pattern** that abstracts away the underlying database. Every repository that handles user data **must** have two implementations: one for the local `SQLite` database and one for the cloud `Firebase` database. Both must expose an identical interface. +4. **AI Provider Abstraction**: AI model interactions are abstracted using a **Factory Pattern**. To add a new provider (e.g., a new LLM), you only need to create a new provider module that conforms to the base interface in `src/common/ai/providers/` and register it in the `factory.js`. +5. **Single Source of Truth for Schema**: The schema for the local SQLite database is defined in a single location: `src/common/config/schema.js`. Any change to the database structure **must** be updated here. +6. **Encryption by Default**: All sensitive user data **must** be encrypted before being persisted to Firebase. This includes, but is not limited to, API keys, conversation titles, transcription text, and AI-generated summaries. This is handled automatically by the `createEncryptedConverter` Firestore helper. + +--- + +## I. Electron Application Architecture (`src/`) + +This section details the architecture of the core desktop application. + +### 1. Overall Pattern: Service-Repository + +The Electron app's logic is primarily built on a **Service-Repository** pattern, with the Views being the HTML/JS files in the `src/app` and `src/features` directories. + +- **Views** (`*.html`, `*View.js`): The UI layer. Views are responsible for rendering the interface and capturing user interactions. They are intentionally kept "dumb" and delegate all significant logic to a corresponding Service. +- **Services** (`*Service.js`): Services contain the application's business logic. They act as the intermediary between Views and Repositories. For example, `sttService` contains the logic for STT, while `summaryService` handles the logic for generating summaries. +- **Repositories** (`*.repository.js`): Repositories are responsible for all data access. They are the *only* part of the application that directly interacts with `sqliteClient` or `firebaseClient`. + +**Location of Modules:** +- **Feature-Specific**: If a service or repository is used by only one feature, it should reside within that feature's directory (e.g., `src/features/listen/summary/summaryService.js`). +- **Common**: If a service or repository is shared across multiple features (like `authService` or `userRepository`), it must be placed in `src/common/services/` or `src/common/repositories/` respectively. + +### 2. Data Persistence: The Dual Repository Factory + +The application dynamically switches between using the local SQLite database and the cloud-based Firebase Firestore. + +- **SQLite**: The default data store for all users, especially those not logged in. This ensures full offline functionality. The low-level client is `src/common/services/sqliteClient.js`. +- **Firebase**: Used exclusively for users who are authenticated. This enables data synchronization across devices and with the web dashboard. + +The selection mechanism is a sophisticated **Factory and Adapter Pattern** located in the `index.js` file of each repository directory (e.g., `src/common/repositories/session/index.js`). + +**How it works:** +1. **Service Call**: A service makes a call to a high-level repository function, like `sessionRepository.create('ask')`. The service is unaware of the user's state or the underlying database. +2. **Repository Selection (Factory)**: The `index.js` adapter logic first determines which underlying repository to use. It imports and calls `authService.getCurrentUser()` to check the login state. If the user is logged in, it selects `firebase.repository.js`; otherwise, it defaults to `sqlite.repository.js`. +3. **UID Injection (Adapter)**: The adapter then retrieves the current user's ID (`uid`) from `authService.getCurrentUserId()`. It injects this `uid` into the actual, low-level repository call (e.g., `firebaseRepository.create(uid, 'ask')`). +4. **Execution**: The selected repository (`sqlite` or `firebase`) executes the data operation. + +This powerful pattern accomplishes two critical goals: +- It makes the services completely agnostic about the underlying data source. +- It frees the services from the responsibility of managing and passing user IDs for every database query. + +**Visualizing the Data Flow** + +```mermaid +graph TD + subgraph "Electron Main Process" + A -- User Action --> B[Service Layer]; + B -- Data Request --> C[Repository Factory]; + C -- Check Login Status --> D{Decision}; + D -- No --> E[SQLite Repository]; + D -- Yes --> F[Firebase Repository]; + E -- Access Local DB --> G[(SQLite)]; + F -- Access Cloud DB --> H[(Firebase)]; + G -- Return Data --> B; + H -- Return Data --> B; + B -- Update UI --> A; + end + + style A fill:#D6EAF8,stroke:#3498DB + style G fill:#E8DAEF,stroke:#8E44AD + style H fill:#FADBD8,stroke:#E74C3C +``` + +--- + +## II. Web Dashboard Architecture (`pickleglass_web/`) + +This section details the architecture of the Next.js web application, which serves as the user-facing dashboard for account management and cloud data viewing. + +### 1. Frontend, Backend, and Main Process Communication + +The web dashboard has a more complex, three-part architecture: + +1. **Next.js Frontend (`app/`):** The React-based user interface. +2. **Node.js Backend (`backend_node/`):** An Express.js server that acts as an intermediary. +3. **Electron Main Process (`src/`):** The ultimate authority for all local data access. + +Crucially, **the web dashboard's backend cannot access the local SQLite database directly**. It must communicate with the Electron main process to request data. + +### 2. The IPC Data Flow + +When the web frontend needs data that resides in the local SQLite database (e.g., viewing a non-synced session), it follows this precise flow: + +1. **HTTP Request**: The Next.js frontend makes a standard API call to its own Node.js backend (e.g., `GET /api/conversations`). +2. **IPC Request**: The Node.js backend receives the HTTP request. It **does not** contain any database logic. Instead, it uses the `ipcRequest` helper from `backend_node/ipcBridge.js`. +3. **IPC Emission**: `ipcRequest` sends an event to the Electron main process over an IPC channel (`web-data-request`). It passes three things: the desired action (e.g., `'get-sessions'`), a unique channel name for the response, and a payload. +4. **Main Process Listener**: The Electron main process has a listener (`ipcMain.on('web-data-request', ...)`) that receives this request. It identifies the action and calls the appropriate **Service** or **Repository** to fetch the data from the SQLite database. +5. **IPC Response**: Once the data is retrieved, the main process sends it back to the web backend using the unique response channel provided in the request. +6. **HTTP Response**: The web backend's `ipcRequest` promise resolves with the data, and the backend sends it back to the Next.js frontend as a standard JSON HTTP response. + +This round-trip ensures our core principle of centralizing data logic in the main process is never violated. + +**Visualizing the IPC Data Flow** + +```mermaid +sequenceDiagram + participant FE as Next.js Frontend + participant BE as Node.js Backend + participant Main as Electron Main Process + + FE->>+BE: 1. HTTP GET /api/local-data + Note over BE: Receives local data request + + BE->>+Main: 2. ipcRequest('get-data', responseChannel) + Note over Main: Receives request, fetches data from SQLite
via Service/Repository + + Main-->>-BE: 3. ipcResponse on responseChannel (data) + Note over BE: Receives data, prepares HTTP response + + BE-->>-FE: 4. HTTP 200 OK (JSON data) +``` \ No newline at end of file diff --git a/electron-builder.yml b/electron-builder.yml index 79b81fb..174740b 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -34,6 +34,8 @@ extraResources: asarUnpack: - "src/assets/SystemAudioDump" + - "**/node_modules/sharp/**/*" + - "**/node_modules/@img/**/*" # Windows configuration win: diff --git a/package-lock.json b/package-lock.json index bad6efc..92218cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pickle-glass", - "version": "0.2.3", + "version": "0.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pickle-glass", - "version": "0.2.3", + "version": "0.2.4", "hasInstallScript": true, "license": "GPL-3.0", "dependencies": { @@ -24,6 +24,7 @@ "firebase": "^11.10.0", "firebase-admin": "^13.4.0", "jsonwebtoken": "^9.0.2", + "keytar": "^7.9.0", "node-fetch": "^2.7.0", "openai": "^4.70.0", "react-hot-toast": "^2.5.2", @@ -46,7 +47,8 @@ "electron": "^30.5.1", "electron-builder": "^26.0.12", "electron-reloader": "^1.2.3", - "esbuild": "^0.25.5" + "esbuild": "^0.25.5", + "prettier": "^3.6.2" }, "optionalDependencies": { "electron-liquid-glass": "^1.0.1" @@ -54,8 +56,6 @@ }, "node_modules/@anthropic-ai/sdk": { "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.56.0.tgz", - "integrity": "sha512-SLCB8M8+VMg1cpCucnA1XWHGWqVSZtIWzmOdDOEu3eTFZMB+A0sGZ1ESO5MHDnqrNTXz3safMrWx9x4rMZSOqA==", "license": "MIT", "bin": { "anthropic-ai-sdk": "bin/cli" @@ -63,8 +63,6 @@ }, "node_modules/@develar/schema-utils": { "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, "license": "MIT", "dependencies": { @@ -81,8 +79,6 @@ }, "node_modules/@electron-forge/cli": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.8.1.tgz", - "integrity": "sha512-QI3EShutfq9Y+2TWWrPjm4JZM3eSAKzoQvRZdVhAfVpUbyJ8K23VqJShg3kGKlPf9BXHAGvE+8LyH5s2yDr1qA==", "dev": true, "funding": [ { @@ -119,8 +115,6 @@ }, "node_modules/@electron-forge/core": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.8.1.tgz", - "integrity": "sha512-jkh0QPW5p0zmruu1E8+2XNufc4UMxy13WLJcm7hn9jbaXKLkMbKuEvhrN1tH/9uGp1mhr/t8sC4N67gP+gS87w==", "dev": true, "funding": [ { @@ -175,8 +169,6 @@ }, "node_modules/@electron-forge/core-utils": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.8.1.tgz", - "integrity": "sha512-mRoPLDNZgmjyOURE/K0D3Op53XGFmFRgfIvFC7c9S/BqsRpovVblrqI4XxPRdNmH9dvhd8On9gGz+XIYAKD3aQ==", "dev": true, "license": "MIT", "dependencies": { @@ -194,10 +186,62 @@ "node": ">= 16.4.0" } }, + "node_modules/@electron-forge/core-utils/node_modules/@electron/rebuild": { + "version": "3.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron-forge/core/node_modules/@electron/rebuild": { + "version": "3.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/@electron-forge/maker-base": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.1.tgz", - "integrity": "sha512-GUZqschGuEBzSzE0bMeDip65IDds48DZXzldlRwQ+85SYVA6RMU2AwDDqx3YiYsvP2OuxKruuqIJZtOF5ps4FQ==", "dev": true, "license": "MIT", "dependencies": { @@ -211,8 +255,6 @@ }, "node_modules/@electron-forge/maker-deb": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.8.1.tgz", - "integrity": "sha512-tjjeesQtCP5Xht1X7gl4+K9bwoETPmQfBkOVAY/FZIxPj40uQh/hOUtLX2tYENNGNVZ1ryDYRs8TuPi+I41Vfw==", "dev": true, "license": "MIT", "dependencies": { @@ -228,8 +270,6 @@ }, "node_modules/@electron-forge/maker-dmg": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-dmg/-/maker-dmg-7.8.1.tgz", - "integrity": "sha512-l449QvY2Teu+J9rHnjkTHEm/wOJ1LRfmrQ2QkGtFoTRcqvFWdUAEN8nK2/08w3j2h6tvOY3QSUjRzXrhJZRNRA==", "dev": true, "license": "MIT", "dependencies": { @@ -246,8 +286,6 @@ }, "node_modules/@electron-forge/maker-rpm": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.1.tgz", - "integrity": "sha512-TF6wylft3BHkw9zdHcxmjEPBZYgTIc0jE31skFnMEQ/aExbNRiNaCZvsXy+7ptTWZxhxUKRc9KHhLFRMCmOK8g==", "dev": true, "license": "MIT", "dependencies": { @@ -263,8 +301,6 @@ }, "node_modules/@electron-forge/maker-squirrel": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.1.tgz", - "integrity": "sha512-qT1PMvT7ALF0ONOkxlA0oc0PiFuKCAKgoMPoxYo9gGOqFvnAb+TBcnLxflQ4ashE/ZkrHpykr4LcDJxqythQTA==", "dev": true, "license": "MIT", "dependencies": { @@ -281,8 +317,6 @@ }, "node_modules/@electron-forge/maker-zip": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.8.1.tgz", - "integrity": "sha512-unIxEoV1lnK4BLVqCy3L2y897fTyg8nKY1WT4rrpv0MUKnQG4qmigDfST5zZNNHHaulEn/ElAic2GEiP7d6bhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -298,8 +332,6 @@ }, "node_modules/@electron-forge/plugin-auto-unpack-natives": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.8.1.tgz", - "integrity": "sha512-4URAgWX9qqqKe6Bfad0VmpFRrwINYMODfKGd2nFQrfHxmBtdpXnsWlLwVGE/wGssIQaTMI5bWQ6F2RNeXTgnhA==", "dev": true, "license": "MIT", "dependencies": { @@ -312,8 +344,6 @@ }, "node_modules/@electron-forge/plugin-base": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.8.1.tgz", - "integrity": "sha512-iCZC2d7CbsZ9l6j5d+KPIiyQx0U1QBfWAbKnnQhWCSizjcrZ7A9V4sMFZeTO6+PVm48b/r9GFPm+slpgZtYQLg==", "dev": true, "license": "MIT", "dependencies": { @@ -325,8 +355,6 @@ }, "node_modules/@electron-forge/plugin-fuses": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-fuses/-/plugin-fuses-7.8.1.tgz", - "integrity": "sha512-dYTwvbV1HcDOIQ0wTybpdtPq6YoBYXIWBTb7DJuvFu/c/thj1eoEdnbwr8mT9hEivjlu5p4ls46n16P5EtZ0oA==", "dev": true, "license": "MIT", "dependencies": { @@ -342,8 +370,6 @@ }, "node_modules/@electron-forge/publisher-base": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.8.1.tgz", - "integrity": "sha512-z2C+C4pcFxyCXIFwXGDcxhU8qtVUPZa3sPL6tH5RuMxJi77768chLw2quDWk2/dfupcSELXcOMYCs7aLysCzeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -355,8 +381,6 @@ }, "node_modules/@electron-forge/shared-types": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.1.tgz", - "integrity": "sha512-guLyGjIISKQQRWHX+ugmcjIOjn2q/BEzCo3ioJXFowxiFwmZw/oCZ2KlPig/t6dMqgUrHTH5W/F0WKu0EY4M+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -369,10 +393,35 @@ "node": ">= 16.4.0" } }, + "node_modules/@electron-forge/shared-types/node_modules/@electron/rebuild": { + "version": "3.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/@electron-forge/template-base": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.8.1.tgz", - "integrity": "sha512-k8jEUr0zWFWb16ZGho+Es2OFeKkcbTgbC6mcH4eNyF/sumh/4XZMcwRtX1i7EiZAYiL9sVxyI6KVwGu254g+0g==", "dev": true, "license": "MIT", "dependencies": { @@ -389,8 +438,6 @@ }, "node_modules/@electron-forge/template-vite": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.8.1.tgz", - "integrity": "sha512-qzSlJaBYYqQAbBdLk4DqAE3HCNz4yXbpkb+VC74ddL4JGwPdPU57DjCthr6YetKJ2FsOVy9ipovA8HX5UbXpAg==", "dev": true, "license": "MIT", "dependencies": { @@ -404,8 +451,6 @@ }, "node_modules/@electron-forge/template-vite-typescript": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.8.1.tgz", - "integrity": "sha512-CccQhwUjZcc6svzuOi3BtbDal591DzyX2J5GPa6mwVutDP8EMtqJL1VyOHdcWO/7XjI6GNAD0fiXySOJiUAECA==", "dev": true, "license": "MIT", "dependencies": { @@ -419,8 +464,6 @@ }, "node_modules/@electron-forge/template-webpack": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.8.1.tgz", - "integrity": "sha512-DA77o9kTCHrq+W211pyNP49DyAt0d1mzMp2gisyNz7a+iKvlv2DsMAeRieLoCQ44akb/z8ZsL0YLteSjKLy4AA==", "dev": true, "license": "MIT", "dependencies": { @@ -434,8 +477,6 @@ }, "node_modules/@electron-forge/template-webpack-typescript": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.8.1.tgz", - "integrity": "sha512-h922E+6zWwym1RT6WKD79BLTc4H8YxEMJ7wPWkBX59kw/exsTB/KFdiJq6r82ON5jSJ+Q8sDGqSmDWdyCfo+Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -449,8 +490,6 @@ }, "node_modules/@electron-forge/tracer": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.1.tgz", - "integrity": "sha512-r2i7aHVp2fylGQSPDw3aTcdNfVX9cpL1iL2MKHrCRNwgrfR+nryGYg434T745GGm1rNQIv5Egdkh5G9xf00oWA==", "dev": true, "license": "MIT", "dependencies": { @@ -462,8 +501,6 @@ }, "node_modules/@electron/asar": { "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", - "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", "dev": true, "license": "MIT", "dependencies": { @@ -480,8 +517,6 @@ }, "node_modules/@electron/asar/node_modules/commander": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", "engines": { @@ -490,8 +525,6 @@ }, "node_modules/@electron/fuses": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", - "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", "dev": true, "license": "MIT", "dependencies": { @@ -505,8 +538,6 @@ }, "node_modules/@electron/fuses/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -521,8 +552,6 @@ }, "node_modules/@electron/get": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", - "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -543,8 +572,6 @@ }, "node_modules/@electron/get/node_modules/fs-extra": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { @@ -558,8 +585,6 @@ }, "node_modules/@electron/get/node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -568,8 +593,6 @@ }, "node_modules/@electron/get/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -578,8 +601,6 @@ }, "node_modules/@electron/get/node_modules/universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { @@ -613,8 +634,6 @@ }, "node_modules/@electron/node-gyp/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==", "dev": true, "license": "MIT", "dependencies": { @@ -623,9 +642,6 @@ }, "node_modules/@electron/node-gyp/node_modules/glob": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -644,8 +660,6 @@ }, "node_modules/@electron/node-gyp/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -657,8 +671,6 @@ }, "node_modules/@electron/notarize": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", - "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, "license": "MIT", "dependencies": { @@ -672,8 +684,6 @@ }, "node_modules/@electron/notarize/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -688,8 +698,6 @@ }, "node_modules/@electron/osx-sign": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", - "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -710,8 +718,6 @@ }, "node_modules/@electron/packager": { "version": "18.3.6", - "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.6.tgz", - "integrity": "sha512-1eXHB5t+SQKvUiDpWGpvr90ZSSbXj+isrh3YbjCTjKT4bE4SQrKSBfukEAaBvp67+GXHFtCHjQgN9qSTFIge+Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -747,8 +753,6 @@ }, "node_modules/@electron/packager/node_modules/fs-extra": { "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, "license": "MIT", "dependencies": { @@ -760,39 +764,8 @@ "node": ">=14.14" } }, - "node_modules/@electron/rebuild": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.2.tgz", - "integrity": "sha512-19/KbIR/DAxbsCkiaGMXIdPnMCJLkcf8AvGnduJtWBs/CBwiAjY1apCqOLVxrXg+rtXFCngbXhBanWjxLUt1Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.45.0", - "node-api-version": "^0.2.0", - "ora": "^5.1.0", - "read-binary-file-arch": "^1.0.6", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" - }, - "bin": { - "electron-rebuild": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, "node_modules/@electron/universal": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", - "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", "dev": true, "license": "MIT", "dependencies": { @@ -810,8 +783,6 @@ }, "node_modules/@electron/universal/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==", "dev": true, "license": "MIT", "dependencies": { @@ -820,8 +791,6 @@ }, "node_modules/@electron/universal/node_modules/fs-extra": { "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, "license": "MIT", "dependencies": { @@ -835,8 +804,6 @@ }, "node_modules/@electron/universal/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -851,8 +818,6 @@ }, "node_modules/@electron/windows-sign": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", - "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -871,8 +836,6 @@ }, "node_modules/@electron/windows-sign/node_modules/fs-extra": { "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, "license": "MIT", "dependencies": { @@ -886,86 +849,14 @@ }, "node_modules/@emnapi/runtime": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", - "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", "cpu": [ "arm64" ], @@ -979,373 +870,12 @@ "node": ">=18" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@fastify/busboy": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", - "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", "license": "MIT" }, "node_modules/@firebase/ai": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz", - "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==", "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", @@ -1364,8 +894,6 @@ }, "node_modules/@firebase/analytics": { "version": "0.10.17", - "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz", - "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1380,8 +908,6 @@ }, "node_modules/@firebase/analytics-compat": { "version": "0.2.23", - "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz", - "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==", "license": "Apache-2.0", "dependencies": { "@firebase/analytics": "0.10.17", @@ -1396,14 +922,10 @@ }, "node_modules/@firebase/analytics-types": { "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", - "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", "license": "Apache-2.0" }, "node_modules/@firebase/app": { "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", - "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1418,8 +940,6 @@ }, "node_modules/@firebase/app-check": { "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz", - "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1436,8 +956,6 @@ }, "node_modules/@firebase/app-check-compat": { "version": "0.3.26", - "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz", - "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==", "license": "Apache-2.0", "dependencies": { "@firebase/app-check": "0.10.1", @@ -1456,20 +974,14 @@ }, "node_modules/@firebase/app-check-interop-types": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", - "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", "license": "Apache-2.0" }, "node_modules/@firebase/app-check-types": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", - "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", "license": "Apache-2.0" }, "node_modules/@firebase/app-compat": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", - "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", "license": "Apache-2.0", "dependencies": { "@firebase/app": "0.13.2", @@ -1484,14 +996,10 @@ }, "node_modules/@firebase/app-types": { "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", - "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", "license": "Apache-2.0" }, "node_modules/@firebase/auth": { "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", - "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1514,8 +1022,6 @@ }, "node_modules/@firebase/auth-compat": { "version": "0.5.28", - "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz", - "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==", "license": "Apache-2.0", "dependencies": { "@firebase/auth": "1.10.8", @@ -1533,14 +1039,10 @@ }, "node_modules/@firebase/auth-interop-types": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", - "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", "license": "Apache-2.0" }, "node_modules/@firebase/auth-types": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", - "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", @@ -1549,8 +1051,6 @@ }, "node_modules/@firebase/component": { "version": "0.6.18", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz", - "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==", "license": "Apache-2.0", "dependencies": { "@firebase/util": "1.12.1", @@ -1562,8 +1062,6 @@ }, "node_modules/@firebase/data-connect": { "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz", - "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==", "license": "Apache-2.0", "dependencies": { "@firebase/auth-interop-types": "0.2.4", @@ -1578,8 +1076,6 @@ }, "node_modules/@firebase/database": { "version": "1.0.20", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz", - "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==", "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", @@ -1596,8 +1092,6 @@ }, "node_modules/@firebase/database-compat": { "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz", - "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1613,8 +1107,6 @@ }, "node_modules/@firebase/database-types": { "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz", - "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==", "license": "Apache-2.0", "dependencies": { "@firebase/app-types": "0.9.3", @@ -1623,8 +1115,6 @@ }, "node_modules/@firebase/firestore": { "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz", - "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1644,8 +1134,6 @@ }, "node_modules/@firebase/firestore-compat": { "version": "0.3.53", - "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz", - "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1663,8 +1151,6 @@ }, "node_modules/@firebase/firestore-types": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", - "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", @@ -1673,8 +1159,6 @@ }, "node_modules/@firebase/functions": { "version": "0.12.9", - "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz", - "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==", "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", @@ -1693,8 +1177,6 @@ }, "node_modules/@firebase/functions-compat": { "version": "0.3.26", - "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz", - "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1712,14 +1194,10 @@ }, "node_modules/@firebase/functions-types": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", - "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", "license": "Apache-2.0" }, "node_modules/@firebase/installations": { "version": "0.6.18", - "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz", - "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1733,8 +1211,6 @@ }, "node_modules/@firebase/installations-compat": { "version": "0.2.18", - "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz", - "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1749,8 +1225,6 @@ }, "node_modules/@firebase/installations-types": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", - "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x" @@ -1758,8 +1232,6 @@ }, "node_modules/@firebase/logger": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", - "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -1770,8 +1242,6 @@ }, "node_modules/@firebase/messaging": { "version": "0.12.22", - "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz", - "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1787,8 +1257,6 @@ }, "node_modules/@firebase/messaging-compat": { "version": "0.2.22", - "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz", - "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1802,14 +1270,10 @@ }, "node_modules/@firebase/messaging-interop-types": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", - "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", "license": "Apache-2.0" }, "node_modules/@firebase/performance": { "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz", - "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1825,8 +1289,6 @@ }, "node_modules/@firebase/performance-compat": { "version": "0.2.20", - "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz", - "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1842,14 +1304,10 @@ }, "node_modules/@firebase/performance-types": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", - "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", "license": "Apache-2.0" }, "node_modules/@firebase/remote-config": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz", - "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1864,8 +1322,6 @@ }, "node_modules/@firebase/remote-config-compat": { "version": "0.2.18", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz", - "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1881,14 +1337,10 @@ }, "node_modules/@firebase/remote-config-types": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", - "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", "license": "Apache-2.0" }, "node_modules/@firebase/storage": { "version": "0.13.14", - "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz", - "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1904,8 +1356,6 @@ }, "node_modules/@firebase/storage-compat": { "version": "0.3.24", - "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz", - "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==", "license": "Apache-2.0", "dependencies": { "@firebase/component": "0.6.18", @@ -1923,8 +1373,6 @@ }, "node_modules/@firebase/storage-types": { "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", - "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", "license": "Apache-2.0", "peerDependencies": { "@firebase/app-types": "0.x", @@ -1933,8 +1381,6 @@ }, "node_modules/@firebase/util": { "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz", - "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1946,21 +1392,15 @@ }, "node_modules/@firebase/webchannel-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", - "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==", "license": "Apache-2.0" }, "node_modules/@gar/promisify": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true, "license": "MIT" }, "node_modules/@google-cloud/firestore": { "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.2.tgz", - "integrity": "sha512-BQCSbjWJndCZ6bj8BSGhi4EM1TDFs9HgZXXsJ7d2kEPo+x64fB3OnQE8ffmn3vWADqeAmYBPaMPF/k6PHETXKA==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -1976,8 +1416,6 @@ }, "node_modules/@google-cloud/paginator": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", - "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -1990,8 +1428,6 @@ }, "node_modules/@google-cloud/projectify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", - "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "license": "Apache-2.0", "optional": true, "engines": { @@ -2000,8 +1436,6 @@ }, "node_modules/@google-cloud/promisify": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", - "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "license": "Apache-2.0", "optional": true, "engines": { @@ -2010,8 +1444,6 @@ }, "node_modules/@google-cloud/storage": { "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.16.0.tgz", - "integrity": "sha512-7/5LRgykyOfQENcm6hDKP8SX/u9XxE5YOiWOkgkwcoO+cG8xT/cyOvp9wwN3IxfdYgpHs8CE7Nq2PKX2lNaEXw==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2037,8 +1469,6 @@ }, "node_modules/@google-cloud/storage/node_modules/mime": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", "optional": true, "bin": { @@ -2050,8 +1480,6 @@ }, "node_modules/@google-cloud/storage/node_modules/uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "license": "MIT", "optional": true, "bin": { @@ -2060,8 +1488,6 @@ }, "node_modules/@google/genai": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.8.0.tgz", - "integrity": "sha512-n3KiMFesQCy2R9iSdBIuJ0JWYQ1HZBJJkmt4PPZMGZKvlgHhBAGw1kUMyX+vsAIzprN3lK45DI755lm70wPOOg==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^9.14.2", @@ -2083,8 +1509,6 @@ }, "node_modules/@google/generative-ai": { "version": "0.24.1", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", - "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" @@ -2092,8 +1516,6 @@ }, "node_modules/@grpc/grpc-js": { "version": "1.9.15", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", - "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.7.8", @@ -2105,8 +1527,6 @@ }, "node_modules/@grpc/proto-loader": { "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", "license": "Apache-2.0", "dependencies": { "lodash.camelcase": "^4.3.0", @@ -2123,14 +1543,10 @@ }, "node_modules/@hapi/hoek": { "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -2138,8 +1554,6 @@ }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", "cpu": [ "arm64" ], @@ -2158,32 +1572,8 @@ "@img/sharp-libvips-darwin-arm64": "1.1.0" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", "cpu": [ "arm64" ], @@ -2196,22 +1586,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-arm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", @@ -2534,8 +1908,6 @@ }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "dev": true, "license": "MIT", "engines": { @@ -2544,8 +1916,6 @@ }, "node_modules/@isaacs/brace-expansion": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", "dev": true, "license": "MIT", "dependencies": { @@ -2557,8 +1927,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", "dependencies": { @@ -2575,15 +1943,11 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2600,8 +1964,6 @@ }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "license": "MIT", "optional": true, "funding": { @@ -2611,8 +1973,6 @@ }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, "funding": [ { @@ -2634,8 +1994,6 @@ }, "node_modules/@malept/flatpak-bundler": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2650,8 +2008,6 @@ }, "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2666,8 +2022,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -2680,8 +2034,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -2690,8 +2042,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -2704,8 +2054,6 @@ }, "node_modules/@npmcli/fs": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "license": "ISC", "dependencies": { @@ -2718,9 +2066,6 @@ }, "node_modules/@npmcli/move-file": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, "license": "MIT", "dependencies": { @@ -2733,8 +2078,6 @@ }, "node_modules/@opentelemetry/api": { "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "optional": true, "engines": { @@ -2743,8 +2086,6 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", "optional": true, @@ -2754,32 +2095,22 @@ }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1", @@ -2788,38 +2119,26 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, "node_modules/@sideway/address": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" @@ -2827,20 +2146,14 @@ }, "node_modules/@sideway/formula": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", "license": "BSD-3-Clause" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", "license": "BSD-3-Clause" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", "engines": { @@ -2852,8 +2165,6 @@ }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, "license": "MIT", "dependencies": { @@ -2865,8 +2176,6 @@ }, "node_modules/@tootallnate/once": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "devOptional": true, "license": "MIT", "engines": { @@ -2875,8 +2184,6 @@ }, "node_modules/@types/appdmg": { "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@types/appdmg/-/appdmg-0.5.5.tgz", - "integrity": "sha512-G+n6DgZTZFOteITE30LnWj+HRVIGr7wMlAiLWOO02uJFWVEitaPU9JVXm9wJokkgshBawb2O1OykdcsmkkZfgg==", "dev": true, "license": "MIT", "optional": true, @@ -2886,8 +2193,6 @@ }, "node_modules/@types/body-parser": { "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", "dependencies": { "@types/connect": "*", @@ -2896,8 +2201,6 @@ }, "node_modules/@types/cacheable-request": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, "license": "MIT", "dependencies": { @@ -2909,15 +2212,11 @@ }, "node_modules/@types/caseless": { "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "license": "MIT", "optional": true }, "node_modules/@types/connect": { "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -2925,8 +2224,6 @@ }, "node_modules/@types/debug": { "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2935,8 +2232,6 @@ }, "node_modules/@types/express": { "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "license": "MIT", "dependencies": { "@types/body-parser": "*", @@ -2947,8 +2242,6 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -2959,8 +2252,6 @@ }, "node_modules/@types/fs-extra": { "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, "license": "MIT", "dependencies": { @@ -2969,21 +2260,15 @@ }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true, "license": "MIT" }, "node_modules/@types/http-errors": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { "@types/ms": "*", @@ -2992,8 +2277,6 @@ }, "node_modules/@types/keyv": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "license": "MIT", "dependencies": { @@ -3002,27 +2285,19 @@ }, "node_modules/@types/long": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "license": "MIT", "optional": true }, "node_modules/@types/mime": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { "version": "20.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", - "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3030,8 +2305,6 @@ }, "node_modules/@types/node-fetch": { "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -3040,8 +2313,6 @@ }, "node_modules/@types/plist": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "license": "MIT", "optional": true, @@ -3052,20 +2323,14 @@ }, "node_modules/@types/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT" }, "node_modules/@types/request": { "version": "2.48.12", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", - "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "license": "MIT", "optional": true, "dependencies": { @@ -3077,8 +2342,6 @@ }, "node_modules/@types/request/node_modules/form-data": { "version": "2.5.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", - "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", "license": "MIT", "optional": true, "dependencies": { @@ -3094,8 +2357,6 @@ }, "node_modules/@types/responselike": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "license": "MIT", "dependencies": { @@ -3104,8 +2365,6 @@ }, "node_modules/@types/send": { "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -3114,8 +2373,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -3125,23 +2382,17 @@ }, "node_modules/@types/tough-cookie": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "license": "MIT", "optional": true }, "node_modules/@types/verror": { "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/@types/yauzl": { "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "license": "MIT", "optional": true, @@ -3151,8 +2402,6 @@ }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true, "license": "MIT", "engines": { @@ -3161,22 +2410,16 @@ }, "node_modules/7zip-bin": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true, "license": "MIT" }, "node_modules/abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "license": "ISC" }, "node_modules/abort-controller": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -3187,8 +2430,6 @@ }, "node_modules/accepts": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { "mime-types": "~2.1.34", @@ -3200,8 +2441,6 @@ }, "node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "engines": { "node": ">= 14" @@ -3209,8 +2448,6 @@ }, "node_modules/agentkeepalive": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "license": "MIT", "dependencies": { "humanize-ms": "^1.2.1" @@ -3221,8 +2458,6 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "license": "MIT", "dependencies": { @@ -3235,8 +2470,6 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -3252,8 +2485,6 @@ }, "node_modules/ajv-formats": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -3269,8 +2500,6 @@ }, "node_modules/ajv-formats/node_modules/ajv": { "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3285,14 +2514,10 @@ }, "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/ajv-keywords": { "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3301,8 +2526,6 @@ }, "node_modules/ansi-escapes": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "license": "MIT", "dependencies": { @@ -3317,8 +2540,6 @@ }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -3330,8 +2551,6 @@ }, "node_modules/ansi-regex": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -3343,8 +2562,6 @@ }, "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" @@ -3358,8 +2575,6 @@ }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -3372,15 +2587,11 @@ }, "node_modules/app-builder-bin": { "version": "5.0.0-alpha.12", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", - "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", "dev": true, "license": "MIT" }, "node_modules/app-builder-lib": { "version": "26.0.12", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.0.12.tgz", - "integrity": "sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==", "dev": true, "license": "MIT", "dependencies": { @@ -3428,8 +2639,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/asar": { "version": "3.2.18", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", - "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", "dev": true, "license": "MIT", "dependencies": { @@ -3446,8 +2655,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/asar/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -3459,8 +2666,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/osx-sign": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3481,8 +2686,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/osx-sign/node_modules/isbinaryfile": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "license": "MIT", "engines": { @@ -3494,12 +2697,10 @@ }, "node_modules/app-builder-lib/node_modules/@electron/rebuild": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", - "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", "dev": true, "license": "MIT", "dependencies": { - "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@electron/node-gyp": "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2", "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", @@ -3523,8 +2724,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/universal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", - "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", "dev": true, "license": "MIT", "dependencies": { @@ -3542,8 +2741,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/universal/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==", "dev": true, "license": "MIT", "dependencies": { @@ -3552,8 +2749,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/fs-extra": { "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, "license": "MIT", "dependencies": { @@ -3567,8 +2762,6 @@ }, "node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -3583,8 +2776,6 @@ }, "node_modules/app-builder-lib/node_modules/commander": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, "license": "MIT", "engines": { @@ -3593,8 +2784,6 @@ }, "node_modules/app-builder-lib/node_modules/dotenv": { "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -3606,8 +2795,6 @@ }, "node_modules/app-builder-lib/node_modules/isbinaryfile": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", - "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", "dev": true, "license": "MIT", "engines": { @@ -3619,8 +2806,6 @@ }, "node_modules/app-builder-lib/node_modules/minimatch": { "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { @@ -3635,8 +2820,6 @@ }, "node_modules/app-builder-lib/node_modules/pe-library": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", - "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", "dev": true, "license": "MIT", "engines": { @@ -3650,8 +2833,6 @@ }, "node_modules/app-builder-lib/node_modules/resedit": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", - "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", "dev": true, "license": "MIT", "dependencies": { @@ -3668,8 +2849,6 @@ }, "node_modules/appdmg": { "version": "0.6.6", - "resolved": "https://registry.npmjs.org/appdmg/-/appdmg-0.6.6.tgz", - "integrity": "sha512-GRmFKlCG+PWbcYF4LUNonTYmy0GjguDy6Jh9WP8mpd0T6j80XIJyXBiWlD0U+MLNhqV9Nhx49Gl9GpVToulpLg==", "dev": true, "license": "MIT", "optional": true, @@ -3698,20 +2877,14 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "node_modules/array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, "node_modules/arrify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "license": "MIT", "optional": true, "engines": { @@ -3720,8 +2893,6 @@ }, "node_modules/assert-plus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "license": "MIT", "optional": true, @@ -3731,8 +2902,6 @@ }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "license": "MIT", "optional": true, @@ -3742,16 +2911,12 @@ }, "node_modules/async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", "dev": true, "license": "MIT", "optional": true }, "node_modules/async-exit-hook": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, "license": "MIT", "engines": { @@ -3760,8 +2925,6 @@ }, "node_modules/async-retry": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "license": "MIT", "optional": true, "dependencies": { @@ -3770,14 +2933,10 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, "license": "ISC", "engines": { @@ -3786,8 +2945,6 @@ }, "node_modules/atomically": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", "license": "MIT", "engines": { "node": ">=10.12.0" @@ -3795,8 +2952,6 @@ }, "node_modules/author-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", - "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", "dev": true, "license": "MIT", "engines": { @@ -3805,8 +2960,6 @@ }, "node_modules/axios": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -3816,15 +2969,11 @@ }, "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==", "dev": true, "license": "MIT" }, "node_modules/base32-encode": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-1.2.0.tgz", - "integrity": "sha512-cHFU8XeRyx0GgmoWi5qHMCVRiqU6J3MHWxVgun7jggCBUpVzm1Ir7M9dYr2whjSNc3tFeXfQ/oZjQu/4u55h9A==", "dev": true, "license": "MIT", "optional": true, @@ -3834,8 +2983,6 @@ }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -3854,8 +3001,6 @@ }, "node_modules/better-sqlite3": { "version": "9.6.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz", - "integrity": "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3865,8 +3010,6 @@ }, "node_modules/bignumber.js": { "version": "9.3.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", - "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", "license": "MIT", "engines": { "node": "*" @@ -3874,8 +3017,6 @@ }, "node_modules/binary-extensions": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", "engines": { @@ -3887,8 +3028,6 @@ }, "node_modules/bindings": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "license": "MIT", "dependencies": { "file-uri-to-path": "1.0.0" @@ -3896,8 +3035,6 @@ }, "node_modules/bl": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -3907,15 +3044,11 @@ }, "node_modules/bluebird": { "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, "license": "MIT" }, "node_modules/body-parser": { "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -3938,8 +3071,6 @@ }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -3947,8 +3078,6 @@ }, "node_modules/body-parser/node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -3959,23 +3088,16 @@ }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/boolean": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT", "optional": true }, "node_modules/bplist-creator": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz", - "integrity": "sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA==", "dev": true, "license": "MIT", "optional": true, @@ -3985,8 +3107,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3996,8 +3116,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -4009,8 +3127,6 @@ }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { "type": "github", @@ -4033,8 +3149,6 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", "engines": { @@ -4043,21 +3157,15 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, "node_modules/buffer-from": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "node_modules/builder-util": { "version": "26.0.11", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.0.11.tgz", - "integrity": "sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==", "dev": true, "license": "MIT", "dependencies": { @@ -4082,8 +3190,6 @@ }, "node_modules/builder-util-runtime": { "version": "9.3.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz", - "integrity": "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==", "license": "MIT", "dependencies": { "debug": "^4.3.4", @@ -4095,8 +3201,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4104,8 +3208,6 @@ }, "node_modules/cacache": { "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4134,8 +3236,6 @@ }, "node_modules/cacache/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==", "dev": true, "license": "MIT", "dependencies": { @@ -4144,9 +3244,6 @@ }, "node_modules/cacache/node_modules/glob": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -4165,8 +3262,6 @@ }, "node_modules/cacache/node_modules/lru-cache": { "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -4175,8 +3270,6 @@ }, "node_modules/cacache/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -4188,8 +3281,6 @@ }, "node_modules/cacheable-lookup": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, "license": "MIT", "engines": { @@ -4198,8 +3289,6 @@ }, "node_modules/cacheable-request": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "license": "MIT", "dependencies": { @@ -4217,8 +3306,6 @@ }, "node_modules/cacheable-request/node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", "dependencies": { @@ -4233,8 +3320,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4246,8 +3331,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4262,8 +3345,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -4279,8 +3360,6 @@ }, "node_modules/chokidar": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -4304,8 +3383,6 @@ }, "node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, "license": "ISC", "engines": { @@ -4314,8 +3391,6 @@ }, "node_modules/chrome-trace-event": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", "engines": { @@ -4324,15 +3399,11 @@ }, "node_modules/chromium-pickle-js": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", "dev": true, "license": "MIT" }, "node_modules/ci-info": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -4347,8 +3418,6 @@ }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "license": "MIT", "engines": { @@ -4357,8 +3426,6 @@ }, "node_modules/cli-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "license": "MIT", "dependencies": { @@ -4373,8 +3440,6 @@ }, "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==", "dev": true, "license": "MIT", "engines": { @@ -4386,8 +3451,6 @@ }, "node_modules/cli-truncate": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "license": "MIT", "optional": true, @@ -4404,8 +3467,6 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -4418,8 +3479,6 @@ }, "node_modules/cliui/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" @@ -4427,8 +3486,6 @@ }, "node_modules/cliui/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" @@ -4439,8 +3496,6 @@ }, "node_modules/cliui/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", @@ -4456,8 +3511,6 @@ }, "node_modules/clone": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "license": "MIT", "engines": { @@ -4466,8 +3519,6 @@ }, "node_modules/clone-response": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "license": "MIT", "dependencies": { @@ -4479,8 +3530,6 @@ }, "node_modules/color": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1", @@ -4492,8 +3541,6 @@ }, "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" @@ -4504,14 +3551,10 @@ }, "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/color-string": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", "dependencies": { "color-name": "^1.0.0", @@ -4520,15 +3563,11 @@ }, "node_modules/colorette": { "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -4539,8 +3578,6 @@ }, "node_modules/commander": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "license": "MIT", "engines": { @@ -4549,8 +3586,6 @@ }, "node_modules/compare-version": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, "license": "MIT", "engines": { @@ -4559,15 +3594,11 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/conf": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", - "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", "license": "MIT", "dependencies": { "ajv": "^8.6.3", @@ -4590,8 +3621,6 @@ }, "node_modules/conf/node_modules/ajv": { "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -4606,14 +3635,10 @@ }, "node_modules/conf/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/config-file-ts": { "version": "0.2.8-rc1", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", - "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", "dev": true, "license": "MIT", "dependencies": { @@ -4623,8 +3648,6 @@ }, "node_modules/config-file-ts/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==", "dev": true, "license": "MIT", "dependencies": { @@ -4633,8 +3656,6 @@ }, "node_modules/config-file-ts/node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { @@ -4654,8 +3675,6 @@ }, "node_modules/config-file-ts/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -4670,8 +3689,6 @@ }, "node_modules/config-file-ts/node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -4680,8 +3697,6 @@ }, "node_modules/content-disposition": { "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -4692,8 +3707,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4701,8 +3714,6 @@ }, "node_modules/cookie": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4710,22 +3721,16 @@ }, "node_modules/cookie-signature": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, "node_modules/core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true, "license": "MIT", "optional": true }, "node_modules/cors": { "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -4737,8 +3742,6 @@ }, "node_modules/crc": { "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, "license": "MIT", "optional": true, @@ -4748,15 +3751,11 @@ }, "node_modules/cross-dirname": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", - "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -4770,8 +3769,6 @@ }, "node_modules/cross-zip": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-4.0.1.tgz", - "integrity": "sha512-n63i0lZ0rvQ6FXiGQ+/JFCKAUyPFhLQYJIqKaa+tSJtfKeULF/IDNDAbdnSIxgS4NTuw2b0+lj8LzfITuq+ZxQ==", "dev": true, "funding": [ { @@ -4794,14 +3791,10 @@ }, "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/date-time": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", "dev": true, "license": "MIT", "dependencies": { @@ -4813,8 +3806,6 @@ }, "node_modules/debounce-fn": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", "license": "MIT", "dependencies": { "mimic-fn": "^3.0.0" @@ -4828,8 +3819,6 @@ }, "node_modules/debug": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4845,8 +3834,6 @@ }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" @@ -4860,8 +3847,6 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "license": "MIT", "engines": { "node": ">=10" @@ -4872,8 +3857,6 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", "engines": { "node": ">=4.0.0" @@ -4881,8 +3864,6 @@ }, "node_modules/defaults": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "license": "MIT", "dependencies": { @@ -4894,8 +3875,6 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, "license": "MIT", "engines": { @@ -4904,8 +3883,6 @@ }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", "optional": true, @@ -4923,8 +3900,6 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", "optional": true, @@ -4942,8 +3917,6 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -4951,8 +3924,6 @@ }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4960,8 +3931,6 @@ }, "node_modules/destroy": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", "engines": { "node": ">= 0.8", @@ -4970,8 +3939,6 @@ }, "node_modules/detect-libc": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -4979,16 +3946,12 @@ }, "node_modules/detect-node": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, "license": "MIT", "optional": true }, "node_modules/dir-compare": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", - "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4998,8 +3961,6 @@ }, "node_modules/dmg-builder": { "version": "26.0.12", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.0.12.tgz", - "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "dev": true, "license": "MIT", "dependencies": { @@ -5016,8 +3977,6 @@ }, "node_modules/dmg-license": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, "license": "MIT", "optional": true, @@ -5043,8 +4002,6 @@ }, "node_modules/dot-prop": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "license": "MIT", "dependencies": { "is-obj": "^2.0.0" @@ -5058,8 +4015,6 @@ }, "node_modules/dotenv": { "version": "17.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.1.tgz", - "integrity": "sha512-GLjkduuAL7IMJg/ZnOPm9AnWKJ82mSE2tzXLaJ/6hD6DhwGfZaXG77oB8qbReyiczNxnbxQKyh0OE5mXq0bAHA==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -5070,8 +4025,6 @@ }, "node_modules/dotenv-expand": { "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5086,8 +4039,6 @@ }, "node_modules/dotenv-expand/node_modules/dotenv": { "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -5099,8 +4050,6 @@ }, "node_modules/ds-store": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ds-store/-/ds-store-0.1.6.tgz", - "integrity": "sha512-kY21M6Lz+76OS3bnCzjdsJSF7LBpLYGCVfavW8TgQD2XkcqIZ86W0y9qUDZu6fp7SIZzqosMDW2zi7zVFfv4hw==", "dev": true, "license": "MIT", "optional": true, @@ -5112,8 +4061,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5126,8 +4073,6 @@ }, "node_modules/duplexify": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", "license": "MIT", "optional": true, "dependencies": { @@ -5139,15 +4084,11 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -5155,14 +4096,10 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/ejs": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5177,8 +4114,6 @@ }, "node_modules/electron": { "version": "30.5.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-30.5.1.tgz", - "integrity": "sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5196,8 +4131,6 @@ }, "node_modules/electron-builder": { "version": "26.0.12", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.0.12.tgz", - "integrity": "sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==", "dev": true, "license": "MIT", "dependencies": { @@ -5222,8 +4155,6 @@ }, "node_modules/electron-builder-squirrel-windows": { "version": "26.0.12", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.0.12.tgz", - "integrity": "sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==", "dev": true, "license": "MIT", "peer": true, @@ -5235,8 +4166,6 @@ }, "node_modules/electron-installer-common": { "version": "0.10.4", - "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.4.tgz", - "integrity": "sha512-8gMNPXfAqUE5CfXg8RL0vXpLE9HAaPkgLXVoHE3BMUzogMWenf4LmwQ27BdCUrEhkjrKl+igs2IHJibclR3z3Q==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5263,8 +4192,6 @@ }, "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -5287,8 +4214,6 @@ }, "node_modules/electron-installer-common/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "optional": true, @@ -5304,8 +4229,6 @@ }, "node_modules/electron-installer-debian": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", - "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", "dev": true, "license": "MIT", "optional": true, @@ -5332,8 +4255,6 @@ }, "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -5356,8 +4277,6 @@ }, "node_modules/electron-installer-debian/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==", "dev": true, "license": "MIT", "optional": true, @@ -5367,8 +4286,6 @@ }, "node_modules/electron-installer-debian/node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "optional": true, @@ -5380,8 +4297,6 @@ }, "node_modules/electron-installer-debian/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "optional": true, @@ -5397,8 +4312,6 @@ }, "node_modules/electron-installer-debian/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==", "dev": true, "license": "MIT", "optional": true, @@ -5411,8 +4324,6 @@ }, "node_modules/electron-installer-debian/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==", "dev": true, "license": "MIT", "optional": true, @@ -5430,8 +4341,6 @@ }, "node_modules/electron-installer-debian/node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "optional": true, @@ -5450,8 +4359,6 @@ }, "node_modules/electron-installer-debian/node_modules/yargs-parser": { "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "optional": true, @@ -5461,8 +4368,6 @@ }, "node_modules/electron-installer-dmg": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/electron-installer-dmg/-/electron-installer-dmg-5.0.1.tgz", - "integrity": "sha512-qOa1aAQdX57C+vzhDk3549dd/PRlNL4F8y736MTD1a43qptD+PvHY97Bo9gSf+OZ8iUWE7BrYSpk/FgLUe40EA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5483,8 +4388,6 @@ }, "node_modules/electron-installer-redhat": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz", - "integrity": "sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw==", "dev": true, "license": "MIT", "optional": true, @@ -5510,8 +4413,6 @@ }, "node_modules/electron-installer-redhat/node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -5534,8 +4435,6 @@ }, "node_modules/electron-installer-redhat/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==", "dev": true, "license": "MIT", "optional": true, @@ -5545,8 +4444,6 @@ }, "node_modules/electron-installer-redhat/node_modules/cliui": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "optional": true, @@ -5558,8 +4455,6 @@ }, "node_modules/electron-installer-redhat/node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "optional": true, @@ -5575,8 +4470,6 @@ }, "node_modules/electron-installer-redhat/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==", "dev": true, "license": "MIT", "optional": true, @@ -5589,8 +4482,6 @@ }, "node_modules/electron-installer-redhat/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==", "dev": true, "license": "MIT", "optional": true, @@ -5608,8 +4499,6 @@ }, "node_modules/electron-installer-redhat/node_modules/yargs": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "optional": true, @@ -5628,8 +4517,6 @@ }, "node_modules/electron-installer-redhat/node_modules/yargs-parser": { "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "optional": true, @@ -5639,8 +4526,6 @@ }, "node_modules/electron-is-dev": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", - "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", "dev": true, "license": "MIT" }, @@ -5667,8 +4552,6 @@ }, "node_modules/electron-publish": { "version": "26.0.11", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.0.11.tgz", - "integrity": "sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==", "dev": true, "license": "MIT", "dependencies": { @@ -5684,8 +4567,6 @@ }, "node_modules/electron-reloader": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/electron-reloader/-/electron-reloader-1.2.3.tgz", - "integrity": "sha512-aDnACAzNg0QvQhzw7LYOx/nVS10mEtbuG6M0QQvNQcLnJEwFs6is+EGRCnM+KQlQ4KcTbdwnt07nd7ZjHpY4iw==", "dev": true, "license": "MIT", "dependencies": { @@ -5701,8 +4582,6 @@ }, "node_modules/electron-squirrel-startup": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz", - "integrity": "sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==", "license": "Apache-2.0", "dependencies": { "debug": "^2.2.0" @@ -5710,8 +4589,6 @@ }, "node_modules/electron-squirrel-startup/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -5719,14 +4596,10 @@ }, "node_modules/electron-squirrel-startup/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/electron-store": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz", - "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==", "license": "MIT", "dependencies": { "conf": "^10.2.0", @@ -5738,8 +4611,6 @@ }, "node_modules/electron-updater": { "version": "6.6.2", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.6.2.tgz", - "integrity": "sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==", "license": "MIT", "dependencies": { "builder-util-runtime": "9.3.1", @@ -5754,8 +4625,6 @@ }, "node_modules/electron-winstaller": { "version": "5.4.0", - "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", - "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5775,8 +4644,6 @@ }, "node_modules/electron-winstaller/node_modules/fs-extra": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", "dependencies": { @@ -5790,8 +4657,6 @@ }, "node_modules/electron-winstaller/node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -5800,8 +4665,6 @@ }, "node_modules/electron-winstaller/node_modules/universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { @@ -5810,8 +4673,6 @@ }, "node_modules/electron/node_modules/@electron/get": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5832,8 +4693,6 @@ }, "node_modules/electron/node_modules/fs-extra": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { @@ -5847,8 +4706,6 @@ }, "node_modules/electron/node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -5857,8 +4714,6 @@ }, "node_modules/electron/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -5867,8 +4722,6 @@ }, "node_modules/electron/node_modules/universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { @@ -5877,22 +4730,16 @@ }, "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/encode-utf8": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", "dev": true, "license": "MIT", "optional": true }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5900,8 +4747,6 @@ }, "node_modules/encoding": { "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "license": "MIT", "optional": true, "dependencies": { @@ -5910,8 +4755,6 @@ }, "node_modules/end-of-stream": { "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -5919,8 +4762,6 @@ }, "node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "license": "MIT", "engines": { "node": ">=6" @@ -5928,15 +4769,11 @@ }, "node_modules/err-code": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true, "license": "MIT" }, "node_modules/error-ex": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "license": "MIT", "dependencies": { @@ -5945,8 +4782,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5954,8 +4789,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5963,8 +4796,6 @@ }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5975,8 +4806,6 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5990,16 +4819,12 @@ }, "node_modules/es6-error": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/esbuild": { "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6040,8 +4865,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -6049,14 +4872,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "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==", "dev": true, "license": "MIT", "optional": true, @@ -6069,8 +4888,6 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6078,8 +4895,6 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { "node": ">=6" @@ -6087,15 +4902,11 @@ }, "node_modules/eventemitter3": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true, "license": "MIT" }, "node_modules/execa": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "license": "MIT", "dependencies": { @@ -6113,8 +4924,6 @@ }, "node_modules/execa/node_modules/cross-spawn": { "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "license": "MIT", "dependencies": { @@ -6130,8 +4939,6 @@ }, "node_modules/execa/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -6140,8 +4947,6 @@ }, "node_modules/execa/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -6150,8 +4955,6 @@ }, "node_modules/execa/node_modules/shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -6163,8 +4966,6 @@ }, "node_modules/execa/node_modules/shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { @@ -6173,8 +4974,6 @@ }, "node_modules/execa/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -6186,8 +4985,6 @@ }, "node_modules/expand-template": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "license": "(MIT OR WTFPL)", "engines": { "node": ">=6" @@ -6195,15 +4992,11 @@ }, "node_modules/exponential-backoff": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", - "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", "dev": true, "license": "Apache-2.0" }, "node_modules/express": { "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -6248,8 +5041,6 @@ }, "node_modules/express/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -6257,20 +5048,14 @@ }, "node_modules/express/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -6290,8 +5075,6 @@ }, "node_modules/extract-zip/node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6306,8 +5089,6 @@ }, "node_modules/extsprintf": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", "dev": true, "engines": [ "node >=0.6.0" @@ -6317,8 +5098,6 @@ }, "node_modules/farmhash-modern": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", - "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -6326,14 +5105,10 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -6349,15 +5124,11 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-uri": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "funding": [ { "type": "github", @@ -6372,8 +5143,6 @@ }, "node_modules/fast-xml-parser": { "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", "funding": [ { "type": "github", @@ -6391,8 +5160,6 @@ }, "node_modules/fastq": { "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "license": "ISC", "dependencies": { @@ -6401,8 +5168,6 @@ }, "node_modules/faye-websocket": { "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" @@ -6413,8 +5178,6 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6423,14 +5186,10 @@ }, "node_modules/file-uri-to-path": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "license": "MIT" }, "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==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6439,8 +5198,6 @@ }, "node_modules/filelist/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==", "dev": true, "license": "MIT", "dependencies": { @@ -6449,8 +5206,6 @@ }, "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==", "dev": true, "license": "ISC", "dependencies": { @@ -6462,8 +5217,6 @@ }, "node_modules/filename-reserved-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", "dev": true, "license": "MIT", "engines": { @@ -6472,8 +5225,6 @@ }, "node_modules/filenamify": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", "dev": true, "license": "MIT", "dependencies": { @@ -6490,8 +5241,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -6503,8 +5252,6 @@ }, "node_modules/finalhandler": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -6521,8 +5268,6 @@ }, "node_modules/finalhandler/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -6530,14 +5275,10 @@ }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -6553,8 +5294,6 @@ }, "node_modules/firebase": { "version": "11.10.0", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz", - "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==", "license": "Apache-2.0", "dependencies": { "@firebase/ai": "1.4.1", @@ -6589,8 +5328,6 @@ }, "node_modules/firebase-admin": { "version": "13.4.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.4.0.tgz", - "integrity": "sha512-Y8DcyKK+4pl4B93ooiy1G8qvdyRMkcNFfBSh+8rbVcw4cW8dgG0VXCCTp5NUwub8sn9vSPsOwpb9tE2OuFmcfQ==", "license": "Apache-2.0", "dependencies": { "@fastify/busboy": "^3.0.0", @@ -6614,8 +5351,6 @@ }, "node_modules/firebase-admin/node_modules/@types/node": { "version": "22.16.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.0.tgz", - "integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -6623,8 +5358,6 @@ }, "node_modules/flora-colossus": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", - "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", "dev": true, "license": "MIT", "dependencies": { @@ -6637,8 +5370,6 @@ }, "node_modules/fmix": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fmix/-/fmix-0.1.0.tgz", - "integrity": "sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==", "dev": true, "license": "MIT", "optional": true, @@ -6648,8 +5379,6 @@ }, "node_modules/follow-redirects": { "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -6668,8 +5397,6 @@ }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { @@ -6685,8 +5412,6 @@ }, "node_modules/foreground-child/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==", "dev": true, "license": "ISC", "engines": { @@ -6698,8 +5423,6 @@ }, "node_modules/form-data": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -6714,14 +5437,10 @@ }, "node_modules/form-data-encoder": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", "license": "MIT" }, "node_modules/formdata-node": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "license": "MIT", "dependencies": { "node-domexception": "1.0.0", @@ -6733,8 +5452,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6742,8 +5459,6 @@ }, "node_modules/fresh": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6751,14 +5466,10 @@ }, "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, "node_modules/fs-extra": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -6771,8 +5482,6 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "license": "ISC", "dependencies": { @@ -6784,8 +5493,6 @@ }, "node_modules/fs-temp": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/fs-temp/-/fs-temp-1.2.1.tgz", - "integrity": "sha512-okTwLB7/Qsq82G6iN5zZJFsOfZtx2/pqrA7Hk/9fvy+c+eJS9CvgGXT2uNxwnI14BDY9L/jQPkaBgSvlKfSW9w==", "dev": true, "license": "MIT", "optional": true, @@ -6795,10 +5502,7 @@ }, "node_modules/fs-xattr": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/fs-xattr/-/fs-xattr-0.3.1.tgz", - "integrity": "sha512-UVqkrEW0GfDabw4C3HOrFlxKfx0eeigfRne69FxSBdHIP8Qt5Sq6Pu3RM9KmMlkygtC4pPKkj5CiPO5USnj2GA==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -6810,30 +5514,11 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, - "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/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6841,15 +5526,11 @@ }, "node_modules/functional-red-black-tree": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "license": "MIT", "optional": true }, "node_modules/galactus": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", - "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6863,17 +5544,12 @@ }, "node_modules/gar": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", - "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT", "optional": true }, "node_modules/gaxios": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", @@ -6888,8 +5564,6 @@ }, "node_modules/gaxios/node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", "engines": { "node": ">=8" @@ -6900,8 +5574,6 @@ }, "node_modules/gaxios/node_modules/uuid": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -6913,8 +5585,6 @@ }, "node_modules/gcp-metadata": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "license": "Apache-2.0", "dependencies": { "gaxios": "^6.1.1", @@ -6927,8 +5597,6 @@ }, "node_modules/generate-function": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", "dev": true, "license": "MIT", "optional": true, @@ -6938,8 +5606,6 @@ }, "node_modules/generate-object-property": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==", "dev": true, "license": "MIT", "optional": true, @@ -6949,8 +5615,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -6958,8 +5622,6 @@ }, "node_modules/get-folder-size": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", - "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", "dev": true, "license": "MIT", "optional": true, @@ -6973,8 +5635,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6997,8 +5657,6 @@ }, "node_modules/get-package-info": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", - "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", "dev": true, "license": "MIT", "dependencies": { @@ -7013,8 +5671,6 @@ }, "node_modules/get-package-info/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { @@ -7023,15 +5679,11 @@ }, "node_modules/get-package-info/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, "license": "MIT" }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7043,8 +5695,6 @@ }, "node_modules/get-stream": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "license": "MIT", "dependencies": { @@ -7056,15 +5706,10 @@ }, "node_modules/github-from-package": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "license": "MIT" }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -7084,8 +5729,6 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -7097,8 +5740,6 @@ }, "node_modules/global-agent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -7116,8 +5757,6 @@ }, "node_modules/global-dirs": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "license": "MIT", "dependencies": { @@ -7132,8 +5771,6 @@ }, "node_modules/globalthis": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "optional": true, @@ -7150,8 +5787,6 @@ }, "node_modules/goober": { "version": "2.1.16", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", - "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", "license": "MIT", "peerDependencies": { "csstype": "^3.0.10" @@ -7159,8 +5794,6 @@ }, "node_modules/google-auth-library": { "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", @@ -7176,8 +5809,6 @@ }, "node_modules/google-gax": { "version": "4.6.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", - "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -7200,8 +5831,6 @@ }, "node_modules/google-gax/node_modules/@grpc/grpc-js": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", - "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -7214,8 +5843,6 @@ }, "node_modules/google-gax/node_modules/uuid": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -7228,8 +5855,6 @@ }, "node_modules/google-logging-utils": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -7237,8 +5862,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7249,8 +5872,6 @@ }, "node_modules/got": { "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "license": "MIT", "dependencies": { @@ -7275,14 +5896,10 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/gtoken": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "license": "MIT", "dependencies": { "gaxios": "^6.0.0", @@ -7294,8 +5911,6 @@ }, "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==", "dev": true, "license": "MIT", "engines": { @@ -7304,8 +5919,6 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "optional": true, @@ -7318,8 +5931,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7330,8 +5941,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -7345,8 +5954,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7357,8 +5964,6 @@ }, "node_modules/hosted-git-info": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "license": "ISC", "dependencies": { @@ -7370,8 +5975,6 @@ }, "node_modules/html-entities": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", - "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", "funding": [ { "type": "github", @@ -7387,15 +5990,11 @@ }, "node_modules/http-cache-semantics": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-errors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { "depd": "2.0.0", @@ -7410,14 +6009,10 @@ }, "node_modules/http-parser-js": { "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", "license": "MIT" }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { @@ -7430,8 +6025,6 @@ }, "node_modules/http2-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "license": "MIT", "dependencies": { @@ -7444,8 +6037,6 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -7457,8 +6048,6 @@ }, "node_modules/humanize-ms": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "license": "MIT", "dependencies": { "ms": "^2.0.0" @@ -7466,8 +6055,6 @@ }, "node_modules/iconv-corefoundation": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, "license": "MIT", "optional": true, @@ -7484,16 +6071,12 @@ }, "node_modules/iconv-corefoundation/node_modules/node-addon-api": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7505,14 +6088,10 @@ }, "node_modules/idb": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "license": "ISC" }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -7531,8 +6110,6 @@ }, "node_modules/image-size": { "version": "0.7.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", - "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", "dev": true, "license": "MIT", "optional": true, @@ -7545,8 +6122,6 @@ }, "node_modules/imul": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/imul/-/imul-1.0.1.tgz", - "integrity": "sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==", "dev": true, "license": "MIT", "optional": true, @@ -7556,8 +6131,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -7566,8 +6139,6 @@ }, "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==", "dev": true, "license": "MIT", "engines": { @@ -7576,16 +6147,11 @@ }, "node_modules/infer-owner": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", "dev": true, "license": "ISC" }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", "dependencies": { @@ -7595,14 +6161,10 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ini": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, "license": "ISC", "engines": { @@ -7611,8 +6173,6 @@ }, "node_modules/interpret": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { @@ -7621,8 +6181,6 @@ }, "node_modules/ip-address": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "license": "MIT", "dependencies": { @@ -7635,8 +6193,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -7644,15 +6200,11 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { @@ -7664,8 +6216,6 @@ }, "node_modules/is-ci": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7677,8 +6227,6 @@ }, "node_modules/is-core-module": { "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -7693,8 +6241,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -7703,8 +6249,6 @@ }, "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" @@ -7712,8 +6256,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -7725,8 +6267,6 @@ }, "node_modules/is-interactive": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, "license": "MIT", "engines": { @@ -7735,23 +6275,17 @@ }, "node_modules/is-lambda": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true, "license": "MIT" }, "node_modules/is-my-ip-valid": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz", - "integrity": "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg==", "dev": true, "license": "MIT", "optional": true }, "node_modules/is-my-json-valid": { "version": "2.20.6", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz", - "integrity": "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw==", "dev": true, "license": "MIT", "optional": true, @@ -7765,8 +6299,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -7775,8 +6307,6 @@ }, "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "license": "MIT", "engines": { "node": ">=8" @@ -7784,16 +6314,12 @@ }, "node_modules/is-property": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", "dev": true, "license": "MIT", "optional": true }, "node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { @@ -7802,8 +6328,6 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", "engines": { @@ -7815,8 +6339,6 @@ }, "node_modules/isbinaryfile": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "license": "MIT", "engines": { @@ -7828,15 +6350,11 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -7851,8 +6369,6 @@ }, "node_modules/jake": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7870,15 +6386,11 @@ }, "node_modules/jake/node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, "node_modules/jiti": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", "bin": { @@ -7887,8 +6399,6 @@ }, "node_modules/joi": { "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -7900,8 +6410,6 @@ }, "node_modules/jose": { "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -7909,8 +6417,6 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7921,15 +6427,11 @@ }, "node_modules/jsbn": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", "dev": true, "license": "MIT" }, "node_modules/json-bigint": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -7937,36 +6439,26 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-schema-typed": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", "license": "BSD-2-Clause" }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, "license": "ISC", "optional": true }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -7978,8 +6470,6 @@ }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -7990,8 +6480,6 @@ }, "node_modules/jsonpointer": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true, "license": "MIT", "optional": true, @@ -8001,8 +6489,6 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", "dependencies": { "jws": "^3.2.2", @@ -8023,8 +6509,6 @@ }, "node_modules/jsonwebtoken/node_modules/jwa": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -8034,8 +6518,6 @@ }, "node_modules/jsonwebtoken/node_modules/jws": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "license": "MIT", "dependencies": { "jwa": "^1.4.1", @@ -8044,8 +6526,6 @@ }, "node_modules/junk": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", - "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", "dev": true, "license": "MIT", "engines": { @@ -8054,8 +6534,6 @@ }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -8065,8 +6543,6 @@ }, "node_modules/jwks-rsa": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", - "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", "license": "MIT", "dependencies": { "@types/express": "^4.17.20", @@ -8082,18 +6558,31 @@ }, "node_modules/jws": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "license": "MIT", "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keytar/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -8102,19 +6591,13 @@ }, "node_modules/lazy-val": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", - "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "license": "MIT" }, "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + "version": "1.1.5" }, "node_modules/listr2": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", - "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, "license": "MIT", "dependencies": { @@ -8131,8 +6614,6 @@ }, "node_modules/listr2/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -8144,8 +6625,6 @@ }, "node_modules/listr2/node_modules/cli-truncate": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, "license": "MIT", "dependencies": { @@ -8161,15 +6640,11 @@ }, "node_modules/listr2/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/listr2/node_modules/is-fullwidth-code-point": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "license": "MIT", "engines": { @@ -8181,8 +6656,6 @@ }, "node_modules/listr2/node_modules/slice-ansi": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8198,8 +6671,6 @@ }, "node_modules/listr2/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { @@ -8216,8 +6687,6 @@ }, "node_modules/load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8232,8 +6701,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -8248,89 +6715,59 @@ }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", "dev": true, "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { @@ -8346,8 +6783,6 @@ }, "node_modules/log-update": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", - "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "license": "MIT", "dependencies": { @@ -8366,8 +6801,6 @@ }, "node_modules/log-update/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -8379,8 +6812,6 @@ }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "license": "MIT", "engines": { @@ -8392,8 +6823,6 @@ }, "node_modules/log-update/node_modules/slice-ansi": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8409,14 +6838,10 @@ }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "license": "MIT", "engines": { @@ -8425,8 +6850,6 @@ }, "node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -8437,8 +6860,6 @@ }, "node_modules/lru-memoizer": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", - "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", "license": "MIT", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -8447,10 +6868,7 @@ }, "node_modules/macos-alias": { "version": "0.2.12", - "resolved": "https://registry.npmjs.org/macos-alias/-/macos-alias-0.2.12.tgz", - "integrity": "sha512-yiLHa7cfJcGRFq4FrR4tMlpNHb4Vy4mWnpajlSSIFM5k4Lv8/7BbbDLzCAVogWNl0LlLhizRp1drXv0hK9h0Yw==", "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -8462,8 +6880,6 @@ }, "node_modules/make-fetch-happen": { "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "license": "ISC", "dependencies": { @@ -8490,8 +6906,6 @@ }, "node_modules/make-fetch-happen/node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8503,8 +6917,6 @@ }, "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "license": "MIT", "dependencies": { @@ -8518,8 +6930,6 @@ }, "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "license": "MIT", "dependencies": { @@ -8532,8 +6942,6 @@ }, "node_modules/make-fetch-happen/node_modules/lru-cache": { "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -8542,8 +6950,6 @@ }, "node_modules/map-age-cleaner": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "license": "MIT", "dependencies": { @@ -8555,8 +6961,6 @@ }, "node_modules/matcher": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, "license": "MIT", "optional": true, @@ -8569,8 +6973,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -8578,8 +6980,6 @@ }, "node_modules/media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8587,8 +6987,6 @@ }, "node_modules/mem": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "license": "MIT", "dependencies": { @@ -8602,8 +7000,6 @@ }, "node_modules/mem/node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { @@ -8612,8 +7008,6 @@ }, "node_modules/merge-descriptors": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8621,8 +7015,6 @@ }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -8631,8 +7023,6 @@ }, "node_modules/methods": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8640,8 +7030,6 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -8654,8 +7042,6 @@ }, "node_modules/mime": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "license": "MIT", "bin": { @@ -8667,8 +7053,6 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8676,8 +7060,6 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -8688,8 +7070,6 @@ }, "node_modules/mimic-fn": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", "license": "MIT", "engines": { "node": ">=8" @@ -8697,8 +7077,6 @@ }, "node_modules/mimic-response": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, "license": "MIT", "engines": { @@ -8707,8 +7085,6 @@ }, "node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -8720,8 +7096,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8729,8 +7103,6 @@ }, "node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "license": "ISC", "dependencies": { @@ -8742,8 +7114,6 @@ }, "node_modules/minipass-collect": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, "license": "ISC", "dependencies": { @@ -8755,8 +7125,6 @@ }, "node_modules/minipass-fetch": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, "license": "MIT", "dependencies": { @@ -8773,8 +7141,6 @@ }, "node_modules/minipass-flush": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "license": "ISC", "dependencies": { @@ -8786,8 +7152,6 @@ }, "node_modules/minipass-pipeline": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, "license": "ISC", "dependencies": { @@ -8799,8 +7163,6 @@ }, "node_modules/minipass-sized": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, "license": "ISC", "dependencies": { @@ -8812,8 +7174,6 @@ }, "node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "license": "MIT", "dependencies": { @@ -8826,8 +7186,6 @@ }, "node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", "bin": { @@ -8839,20 +7197,14 @@ }, "node_modules/mkdirp-classic": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, "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/murmur-32": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/murmur-32/-/murmur-32-0.2.0.tgz", - "integrity": "sha512-ZkcWZudylwF+ir3Ld1n7gL6bI2mQAzXvSobPwVtu8aYi2sbXeipeSkdcanRLzIofLcM5F53lGaKm2dk7orBi7Q==", "dev": true, "license": "MIT", "optional": true, @@ -8864,22 +7216,16 @@ }, "node_modules/nan": { "version": "2.22.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", - "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", "dev": true, "license": "MIT", "optional": true }, "node_modules/napi-build-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, "node_modules/negotiator": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8887,15 +7233,11 @@ }, "node_modules/nice-try": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true, "license": "MIT" }, "node_modules/node-abi": { "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", "license": "MIT", "dependencies": { "semver": "^7.3.5" @@ -8916,8 +7258,6 @@ }, "node_modules/node-api-version": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", - "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8926,9 +7266,6 @@ }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -8946,8 +7283,6 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -8966,8 +7301,6 @@ }, "node_modules/node-forge": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -8975,8 +7308,6 @@ }, "node_modules/node-gyp-build": { "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "license": "MIT", "optional": true, "bin": { @@ -8987,8 +7318,6 @@ }, "node_modules/nopt": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, "license": "ISC", "dependencies": { @@ -9003,8 +7332,6 @@ }, "node_modules/normalize-package-data": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9016,15 +7343,11 @@ }, "node_modules/normalize-package-data/node_modules/hosted-git-info": { "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, "license": "ISC" }, "node_modules/normalize-package-data/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -9033,8 +7356,6 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -9043,8 +7364,6 @@ }, "node_modules/normalize-url": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, "license": "MIT", "engines": { @@ -9056,8 +7375,6 @@ }, "node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", "dependencies": { @@ -9069,8 +7386,6 @@ }, "node_modules/npm-run-path/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -9079,8 +7394,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9088,8 +7401,6 @@ }, "node_modules/object-hash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", "optional": true, "engines": { @@ -9098,8 +7409,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -9110,8 +7419,6 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "optional": true, @@ -9121,8 +7428,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -9133,8 +7438,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -9142,8 +7445,6 @@ }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -9157,8 +7458,6 @@ }, "node_modules/onetime/node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", "engines": { "node": ">=6" @@ -9166,8 +7465,6 @@ }, "node_modules/openai": { "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -9196,8 +7493,6 @@ }, "node_modules/openai/node_modules/@types/node": { "version": "18.19.115", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.115.tgz", - "integrity": "sha512-kNrFiTgG4a9JAn1LMQeLOv3MvXIPokzXziohMrMsvpYgLpdEt/mMiVYc4sGKtDfyxM5gIDF4VgrPRyCw4fHOYg==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -9205,14 +7500,10 @@ }, "node_modules/openai/node_modules/undici-types": { "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, "node_modules/ora": { "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9235,8 +7526,6 @@ }, "node_modules/ora/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==", "dev": true, "license": "MIT", "engines": { @@ -9245,8 +7534,6 @@ }, "node_modules/ora/node_modules/cli-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "license": "MIT", "dependencies": { @@ -9258,8 +7545,6 @@ }, "node_modules/ora/node_modules/restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { @@ -9272,8 +7557,6 @@ }, "node_modules/ora/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==", "dev": true, "license": "MIT", "dependencies": { @@ -9285,8 +7568,6 @@ }, "node_modules/p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, "license": "MIT", "engines": { @@ -9295,8 +7576,6 @@ }, "node_modules/p-defer": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", "dev": true, "license": "MIT", "engines": { @@ -9305,8 +7584,6 @@ }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { @@ -9315,8 +7592,6 @@ }, "node_modules/p-is-promise": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", "dev": true, "license": "MIT", "engines": { @@ -9325,8 +7600,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -9341,8 +7614,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -9357,8 +7628,6 @@ }, "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9373,8 +7642,6 @@ }, "node_modules/p-try": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "license": "MIT", "engines": { "node": ">=6" @@ -9382,15 +7649,11 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parse-author": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", - "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", "dev": true, "license": "MIT", "dependencies": { @@ -9402,8 +7665,6 @@ }, "node_modules/parse-color": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", - "integrity": "sha512-fuDHYgFHJGbpGMgw9skY/bj3HL/Jrn4l/5rSspy00DoT4RyLnDcRvPxdZ+r6OFwIsgAuhDh4I09tAId4mI12bw==", "dev": true, "license": "MIT", "optional": true, @@ -9413,15 +7674,11 @@ }, "node_modules/parse-color/node_modules/color-convert": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", "dev": true, "optional": true }, "node_modules/parse-json": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9433,8 +7690,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9442,8 +7697,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -9452,8 +7705,6 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { @@ -9462,8 +7713,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -9472,15 +7721,11 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -9496,15 +7741,11 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, "node_modules/path-scurry/node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -9513,14 +7754,10 @@ }, "node_modules/path-to-regexp": { "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/path-type": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9532,8 +7769,6 @@ }, "node_modules/pe-library": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", - "integrity": "sha512-nh39Mo1eGWmZS7y+mK/dQIqg7S1lp38DpRxkyoHf0ZcUs/HDc+yyTjuOtTvSMZHmfSLuSQaX945u05Y2Q6UWZg==", "dev": true, "license": "MIT", "engines": { @@ -9547,15 +7782,11 @@ }, "node_modules/pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, "license": "MIT" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -9567,8 +7798,6 @@ }, "node_modules/pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "license": "MIT", "engines": { @@ -9577,8 +7806,6 @@ }, "node_modules/pkg-up": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "license": "MIT", "dependencies": { "find-up": "^3.0.0" @@ -9589,8 +7816,6 @@ }, "node_modules/pkg-up/node_modules/find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "license": "MIT", "dependencies": { "locate-path": "^3.0.0" @@ -9601,8 +7826,6 @@ }, "node_modules/pkg-up/node_modules/locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "license": "MIT", "dependencies": { "p-locate": "^3.0.0", @@ -9614,8 +7837,6 @@ }, "node_modules/pkg-up/node_modules/p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -9629,8 +7850,6 @@ }, "node_modules/pkg-up/node_modules/p-locate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "license": "MIT", "dependencies": { "p-limit": "^2.0.0" @@ -9641,8 +7860,6 @@ }, "node_modules/pkg-up/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "license": "MIT", "engines": { "node": ">=4" @@ -9650,8 +7867,6 @@ }, "node_modules/plist": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9665,8 +7880,6 @@ }, "node_modules/postject": { "version": "1.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", - "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", "dev": true, "license": "MIT", "dependencies": { @@ -9681,8 +7894,6 @@ }, "node_modules/postject/node_modules/commander": { "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "license": "MIT", "engines": { @@ -9691,8 +7902,6 @@ }, "node_modules/prebuild-install": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "license": "MIT", "dependencies": { "detect-libc": "^2.0.0", @@ -9715,10 +7924,24 @@ "node": ">=10" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proc-log": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", "dev": true, "license": "ISC", "engines": { @@ -9727,8 +7950,6 @@ }, "node_modules/progress": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", "engines": { @@ -9737,15 +7958,11 @@ }, "node_modules/promise-inflight": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, "license": "ISC" }, "node_modules/promise-retry": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "license": "MIT", "dependencies": { @@ -9758,8 +7975,6 @@ }, "node_modules/promise-retry/node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "license": "MIT", "engines": { @@ -9768,8 +7983,6 @@ }, "node_modules/proto3-json-serializer": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", - "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -9781,8 +7994,6 @@ }, "node_modules/protobufjs": { "version": "7.5.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", - "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -9805,8 +8016,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -9818,14 +8027,10 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/pump": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -9834,8 +8039,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -9844,8 +8047,6 @@ }, "node_modules/qs": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" @@ -9859,8 +8060,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -9880,8 +8079,6 @@ }, "node_modules/quick-lru": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "license": "MIT", "engines": { @@ -9893,8 +8090,6 @@ }, "node_modules/random-path": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/random-path/-/random-path-0.1.2.tgz", - "integrity": "sha512-4jY0yoEaQ5v9StCl5kZbNIQlg1QheIDBrdkDn53EynpPb9FgO6//p3X/tgMnrC45XN6QZCzU1Xz/+pSSsJBpRw==", "dev": true, "license": "MIT", "optional": true, @@ -9905,8 +8100,6 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -9914,8 +8107,6 @@ }, "node_modules/raw-body": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -9929,8 +8120,6 @@ }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -9941,8 +8130,6 @@ }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", @@ -9956,14 +8143,10 @@ }, "node_modules/rc/node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, "node_modules/react": { "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", "peer": true, "engines": { @@ -9972,8 +8155,6 @@ }, "node_modules/react-dom": { "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", "peer": true, "dependencies": { @@ -9985,8 +8166,6 @@ }, "node_modules/react-hot-toast": { "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", - "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", "license": "MIT", "dependencies": { "csstype": "^3.1.3", @@ -10002,8 +8181,6 @@ }, "node_modules/read-binary-file-arch": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", - "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", "dev": true, "license": "MIT", "dependencies": { @@ -10015,8 +8192,6 @@ }, "node_modules/read-pkg": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "dev": true, "license": "MIT", "dependencies": { @@ -10030,8 +8205,6 @@ }, "node_modules/read-pkg-up": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", "dev": true, "license": "MIT", "dependencies": { @@ -10044,8 +8217,6 @@ }, "node_modules/read-pkg-up/node_modules/find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10057,8 +8228,6 @@ }, "node_modules/read-pkg-up/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "license": "MIT", "dependencies": { @@ -10071,8 +8240,6 @@ }, "node_modules/read-pkg-up/node_modules/p-limit": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10084,8 +8251,6 @@ }, "node_modules/read-pkg-up/node_modules/p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "license": "MIT", "dependencies": { @@ -10097,8 +8262,6 @@ }, "node_modules/read-pkg-up/node_modules/p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "license": "MIT", "engines": { @@ -10107,8 +8270,6 @@ }, "node_modules/read-pkg-up/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "license": "MIT", "engines": { @@ -10117,8 +8278,6 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -10131,8 +8290,6 @@ }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { @@ -10144,8 +8301,6 @@ }, "node_modules/rechoir": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10157,8 +8312,6 @@ }, "node_modules/repeat-string": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, "license": "MIT", "optional": true, @@ -10168,8 +8321,6 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10177,8 +8328,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10186,8 +8335,6 @@ }, "node_modules/resedit": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", - "integrity": "sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==", "dev": true, "license": "MIT", "dependencies": { @@ -10204,8 +8351,6 @@ }, "node_modules/resolve": { "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { @@ -10225,15 +8370,11 @@ }, "node_modules/resolve-alpn": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, "license": "MIT" }, "node_modules/responselike": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "license": "MIT", "dependencies": { @@ -10245,8 +8386,6 @@ }, "node_modules/restore-cursor": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "license": "MIT", "dependencies": { @@ -10262,8 +8401,6 @@ }, "node_modules/retry": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", "optional": true, "engines": { @@ -10272,8 +8409,6 @@ }, "node_modules/retry-request": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", - "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", "license": "MIT", "optional": true, "dependencies": { @@ -10287,8 +8422,6 @@ }, "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -10298,16 +8431,11 @@ }, "node_modules/rfdc": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -10322,8 +8450,6 @@ }, "node_modules/roarr": { "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -10341,8 +8467,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -10365,8 +8489,6 @@ }, "node_modules/rxjs": { "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -10374,8 +8496,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -10394,14 +8514,10 @@ }, "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/sanitize-filename": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "dev": true, "license": "WTFPL OR ISC", "dependencies": { @@ -10410,21 +8526,15 @@ }, "node_modules/sax": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "license": "ISC" }, "node_modules/scheduler": { "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT", "peer": true }, "node_modules/semver": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10435,16 +8545,12 @@ }, "node_modules/semver-compare": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, "license": "MIT", "optional": true }, "node_modules/send": { "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -10467,8 +8573,6 @@ }, "node_modules/send/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -10476,14 +8580,10 @@ }, "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/send/node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -10491,8 +8591,6 @@ }, "node_modules/send/node_modules/mime": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "bin": { "mime": "cli.js" @@ -10503,8 +8601,6 @@ }, "node_modules/serialize-error": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, "license": "MIT", "optional": true, @@ -10520,8 +8616,6 @@ }, "node_modules/serialize-error/node_modules/type-fest": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, "license": "(MIT OR CC0-1.0)", "optional": true, @@ -10534,8 +8628,6 @@ }, "node_modules/serve-static": { "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", @@ -10549,14 +8641,10 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/sharp": { "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -10594,10 +8682,46 @@ "@img/sharp-win32-x64": "0.34.2" } }, + "node_modules/sharp/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -10609,8 +8733,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -10619,8 +8741,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10638,8 +8758,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10654,8 +8772,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10672,8 +8788,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10691,15 +8805,11 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "license": "ISC" }, "node_modules/simple-concat": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "funding": [ { "type": "github", @@ -10718,8 +8828,6 @@ }, "node_modules/simple-get": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "funding": [ { "type": "github", @@ -10743,8 +8851,6 @@ }, "node_modules/simple-swizzle": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" @@ -10752,14 +8858,10 @@ }, "node_modules/simple-swizzle/node_modules/is-arrayish": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, "node_modules/simple-update-notifier": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "license": "MIT", "dependencies": { @@ -10771,8 +8873,6 @@ }, "node_modules/slice-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "license": "MIT", "optional": true, @@ -10787,8 +8887,6 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", "engines": { @@ -10798,8 +8896,6 @@ }, "node_modules/socks": { "version": "2.8.5", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", - "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", "dev": true, "license": "MIT", "dependencies": { @@ -10813,8 +8909,6 @@ }, "node_modules/socks-proxy-agent": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "license": "MIT", "dependencies": { @@ -10828,8 +8922,6 @@ }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10841,8 +8933,6 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -10851,8 +8941,6 @@ }, "node_modules/source-map-support": { "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { @@ -10862,8 +8950,6 @@ }, "node_modules/spdx-correct": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10873,15 +8959,11 @@ }, "node_modules/spdx-exceptions": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10891,22 +8973,16 @@ }, "node_modules/spdx-license-ids": { "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true, "license": "CC0-1.0" }, "node_modules/sprintf-js": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssri": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "license": "ISC", "dependencies": { @@ -10918,8 +8994,6 @@ }, "node_modules/stat-mode": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, "license": "MIT", "engines": { @@ -10928,8 +9002,6 @@ }, "node_modules/statuses": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -10937,8 +9009,6 @@ }, "node_modules/stream-buffers": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "dev": true, "license": "Unlicense", "optional": true, @@ -10948,8 +9018,6 @@ }, "node_modules/stream-events": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "license": "MIT", "optional": true, "dependencies": { @@ -10958,15 +9026,11 @@ }, "node_modules/stream-shift": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "license": "MIT", "optional": true }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -10974,8 +9038,6 @@ }, "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", @@ -10989,8 +9051,6 @@ "node_modules/string-width-cjs": { "name": "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==", "dev": true, "license": "MIT", "dependencies": { @@ -11004,8 +9064,6 @@ }, "node_modules/string-width-cjs/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==", "dev": true, "license": "MIT", "engines": { @@ -11014,8 +9072,6 @@ }, "node_modules/string-width-cjs/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==", "dev": true, "license": "MIT", "dependencies": { @@ -11027,8 +9083,6 @@ }, "node_modules/string-width/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" @@ -11036,8 +9090,6 @@ }, "node_modules/string-width/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" @@ -11048,8 +9100,6 @@ }, "node_modules/strip-ansi": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11065,8 +9115,6 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -11078,8 +9126,6 @@ }, "node_modules/strip-ansi-cjs/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==", "dev": true, "license": "MIT", "engines": { @@ -11088,8 +9134,6 @@ }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -11098,8 +9142,6 @@ }, "node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "license": "MIT", "engines": { @@ -11108,8 +9150,6 @@ }, "node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11117,8 +9157,6 @@ }, "node_modules/strip-outer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "license": "MIT", "dependencies": { @@ -11130,8 +9168,6 @@ }, "node_modules/strip-outer/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { @@ -11140,8 +9176,6 @@ }, "node_modules/strnum": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", "funding": [ { "type": "github", @@ -11153,23 +9187,16 @@ }, "node_modules/stubs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", "license": "MIT", "optional": true }, "node_modules/sudo-prompt": { "version": "9.2.1", - "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT" }, "node_modules/sumchecker": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11181,8 +9208,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -11194,8 +9219,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { @@ -11207,8 +9230,6 @@ }, "node_modules/tar": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "license": "ISC", "dependencies": { @@ -11225,8 +9246,6 @@ }, "node_modules/tar-fs": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", "license": "MIT", "dependencies": { "chownr": "^1.1.1", @@ -11237,14 +9256,10 @@ }, "node_modules/tar-fs/node_modules/chownr": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, "node_modules/tar-stream": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -11259,8 +9274,6 @@ }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "license": "ISC", "engines": { @@ -11269,8 +9282,6 @@ }, "node_modules/teeny-request": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", - "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -11286,8 +9297,6 @@ }, "node_modules/teeny-request/node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", "optional": true, "dependencies": { @@ -11299,8 +9308,6 @@ }, "node_modules/teeny-request/node_modules/http-proxy-agent": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "license": "MIT", "optional": true, "dependencies": { @@ -11314,8 +9321,6 @@ }, "node_modules/teeny-request/node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "optional": true, "dependencies": { @@ -11328,8 +9333,6 @@ }, "node_modules/teeny-request/node_modules/uuid": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -11342,8 +9345,6 @@ }, "node_modules/temp": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", "dependencies": { @@ -11356,8 +9357,6 @@ }, "node_modules/temp-file": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, "license": "MIT", "dependencies": { @@ -11367,8 +9366,6 @@ }, "node_modules/temp/node_modules/mkdirp": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", "dependencies": { @@ -11380,9 +9377,6 @@ }, "node_modules/temp/node_modules/rimraf": { "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -11394,8 +9388,6 @@ }, "node_modules/time-zone": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", "dev": true, "license": "MIT", "engines": { @@ -11404,8 +9396,6 @@ }, "node_modules/tiny-async-pool": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", - "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", "dev": true, "license": "MIT", "dependencies": { @@ -11414,8 +9404,6 @@ }, "node_modules/tiny-async-pool/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -11424,22 +9412,16 @@ }, "node_modules/tiny-each-async": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", - "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", "dev": true, "license": "MIT", "optional": true }, "node_modules/tiny-typed-emitter": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", "license": "MIT" }, "node_modules/tmp": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, "license": "MIT", "engines": { @@ -11448,8 +9430,6 @@ }, "node_modules/tmp-promise": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11458,8 +9438,6 @@ }, "node_modules/tn1150": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tn1150/-/tn1150-0.1.0.tgz", - "integrity": "sha512-DbplOfQFkqG5IHcDyyrs/lkvSr3mPUVsFf/RbDppOshs22yTPnSJWEe6FkYd1txAwU/zcnR905ar2fi4kwF29w==", "dev": true, "license": "MIT", "optional": true, @@ -11472,16 +9450,12 @@ }, "node_modules/to-data-view": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-1.1.0.tgz", - "integrity": "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==", "dev": true, "license": "MIT", "optional": true }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11493,8 +9467,6 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -11502,14 +9474,10 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/trim-repeated": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", "dev": true, "license": "MIT", "dependencies": { @@ -11521,8 +9489,6 @@ }, "node_modules/trim-repeated/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { @@ -11531,8 +9497,6 @@ }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, "license": "WTFPL", "dependencies": { @@ -11541,14 +9505,10 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -11559,8 +9519,6 @@ }, "node_modules/type-fest": { "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" @@ -11571,8 +9529,6 @@ }, "node_modules/type-is": { "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { "media-typer": "0.3.0", @@ -11584,8 +9540,6 @@ }, "node_modules/typescript": { "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11598,14 +9552,10 @@ }, "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==", "license": "MIT" }, "node_modules/unique-filename": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "license": "ISC", "dependencies": { @@ -11617,8 +9567,6 @@ }, "node_modules/unique-slug": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11630,8 +9578,6 @@ }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -11639,8 +9585,6 @@ }, "node_modules/unorm": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", "dev": true, "license": "MIT or GPL-2.0", "optional": true, @@ -11650,8 +9594,6 @@ }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -11659,8 +9601,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -11669,8 +9609,6 @@ }, "node_modules/username": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", - "integrity": "sha512-PCKbdWw85JsYMvmCv5GH3kXmM66rCd9m1hBEDutPNv94b/pqCMT4NtcKyeWYvLFiE8b+ha1Jdl8XAaUdPn5QTg==", "dev": true, "license": "MIT", "dependencies": { @@ -11683,21 +9621,15 @@ }, "node_modules/utf8-byte-length": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", "dev": true, "license": "(WTFPL OR MIT)" }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -11705,8 +9637,6 @@ }, "node_modules/uuid": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -11718,8 +9648,6 @@ }, "node_modules/validate-npm-package-license": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -11729,8 +9657,6 @@ }, "node_modules/validator": { "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -11738,8 +9664,6 @@ }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -11747,8 +9671,6 @@ }, "node_modules/verror": { "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, "license": "MIT", "optional": true, @@ -11763,8 +9685,6 @@ }, "node_modules/wait-on": { "version": "8.0.3", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.3.tgz", - "integrity": "sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==", "license": "MIT", "dependencies": { "axios": "^1.8.2", @@ -11782,8 +9702,6 @@ }, "node_modules/wcwidth": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, "license": "MIT", "dependencies": { @@ -11792,8 +9710,6 @@ }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", "license": "MIT", "engines": { "node": ">= 14" @@ -11801,20 +9717,14 @@ }, "node_modules/web-vitals": { "version": "4.2.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", - "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", "license": "Apache-2.0" }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/websocket-driver": { "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", @@ -11827,8 +9737,6 @@ }, "node_modules/websocket-extensions": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "license": "Apache-2.0", "engines": { "node": ">=0.8.0" @@ -11836,8 +9744,6 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -11846,8 +9752,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -11862,8 +9766,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "optional": true, @@ -11873,8 +9775,6 @@ }, "node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11892,8 +9792,6 @@ "node_modules/wrap-ansi-cjs": { "name": "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==", "dev": true, "license": "MIT", "dependencies": { @@ -11910,8 +9808,6 @@ }, "node_modules/wrap-ansi-cjs/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==", "dev": true, "license": "MIT", "engines": { @@ -11920,8 +9816,6 @@ }, "node_modules/wrap-ansi-cjs/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==", "dev": true, "license": "MIT", "dependencies": { @@ -11933,8 +9827,6 @@ }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -11946,15 +9838,11 @@ }, "node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { @@ -11971,14 +9859,10 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -11998,8 +9882,6 @@ }, "node_modules/xmlbuilder": { "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, "license": "MIT", "engines": { @@ -12008,8 +9890,6 @@ }, "node_modules/xtend": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, "license": "MIT", "optional": true, @@ -12019,8 +9899,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "engines": { "node": ">=10" @@ -12028,14 +9906,10 @@ }, "node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -12052,8 +9926,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "engines": { "node": ">=12" @@ -12061,8 +9933,6 @@ }, "node_modules/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { @@ -12072,8 +9942,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "devOptional": true, "license": "MIT", "engines": { @@ -12085,8 +9953,6 @@ }, "node_modules/zod": { "version": "3.25.75", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", - "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -12094,8 +9960,6 @@ }, "node_modules/zod-to-json-schema": { "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", "license": "ISC", "peerDependencies": { "zod": "^3.24.1" diff --git a/package.json b/package.json index ea125a0..509b70b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pickle-glass", "productName": "Glass", - "version": "0.2.3", + "version": "0.2.4", "description": "Cl*ely for Free", "main": "src/index.js", @@ -48,6 +48,7 @@ "firebase": "^11.10.0", "firebase-admin": "^13.4.0", "jsonwebtoken": "^9.0.2", + "keytar": "^7.9.0", "node-fetch": "^2.7.0", "openai": "^4.70.0", "react-hot-toast": "^2.5.2", @@ -70,9 +71,10 @@ "electron": "^30.5.1", "electron-builder": "^26.0.12", "electron-reloader": "^1.2.3", - "esbuild": "^0.25.5" + "esbuild": "^0.25.5", + "prettier": "^3.6.2" }, "optionalDependencies": { "electron-liquid-glass": "^1.0.1" } -} +} \ No newline at end of file diff --git a/pickleglass_web/backend_node/ipcBridge.js b/pickleglass_web/backend_node/ipcBridge.js index 805403c..0e0d577 100644 --- a/pickleglass_web/backend_node/ipcBridge.js +++ b/pickleglass_web/backend_node/ipcBridge.js @@ -2,7 +2,7 @@ const crypto = require('crypto'); function ipcRequest(req, channel, payload) { return new Promise((resolve, reject) => { - // ์ฆ‰์‹œ ๋ธŒ๋ฆฌ์ง€ ์ƒํƒœ ํ™•์ธ - ๋ฌธ์ œ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ์‹คํŒจ + // Immediately check bridge status and fail if it's not available. if (!req.bridge || typeof req.bridge.emit !== 'function') { reject(new Error('IPC bridge is not available')); return; diff --git a/src/app/ApiKeyHeader.js b/src/app/ApiKeyHeader.js index 76abf21..8b6a12b 100644 --- a/src/app/ApiKeyHeader.js +++ b/src/app/ApiKeyHeader.js @@ -1,4 +1,5 @@ import { html, css, LitElement } from "../assets/lit-core-2.7.4.min.js" +import { getOllamaProgressTracker } from "../common/services/localProgressTracker.js" export class ApiKeyHeader extends LitElement { //////// after_modelStateService //////// @@ -9,7 +10,16 @@ export class ApiKeyHeader extends LitElement { sttProvider: { type: String }, isLoading: { type: Boolean }, errorMessage: { type: String }, + successMessage: { type: String }, providers: { type: Object, state: true }, + modelSuggestions: { type: Array, state: true }, + userModelHistory: { type: Array, state: true }, + selectedLlmModel: { type: String, state: true }, + selectedSttModel: { type: String, state: true }, + ollamaStatus: { type: Object, state: true }, + installingModel: { type: String, state: true }, + installProgress: { type: Number, state: true }, + whisperInstallingModels: { type: Object, state: true }, } //////// after_modelStateService //////// @@ -122,6 +132,29 @@ export class ApiKeyHeader extends LitElement { height: 14px; text-align: center; margin-bottom: 4px; + opacity: 1; + transition: opacity 0.3s ease; + } + + .success-message { + color: rgba(74, 222, 128, 0.9); + font-weight: 500; + font-size: 11px; + height: 14px; + text-align: center; + margin-bottom: 4px; + opacity: 1; + transition: opacity 0.3s ease; + } + + .message-fade-out { + animation: fadeOut 3s ease-in-out forwards; + } + + @keyframes fadeOut { + 0% { opacity: 1; } + 66% { opacity: 1; } + 100% { opacity: 0; } } .api-input { @@ -232,12 +265,62 @@ export class ApiKeyHeader extends LitElement { super() this.isLoading = false this.errorMessage = "" + this.successMessage = "" + this.messageTimestamp = 0 //////// after_modelStateService //////// this.llmApiKey = ""; this.sttApiKey = ""; this.llmProvider = "openai"; this.sttProvider = "openai"; this.providers = { llm: [], stt: [] }; // ์ดˆ๊ธฐํ™” + // Ollama related + this.modelSuggestions = []; + this.userModelHistory = []; + this.selectedLlmModel = ""; + this.selectedSttModel = ""; + this.ollamaStatus = { installed: false, running: false }; + this.installingModel = null; + this.installProgress = 0; + this.progressTracker = getOllamaProgressTracker(); + this.whisperInstallingModels = {}; + + // Professional operation management system + this.activeOperations = new Map(); + this.operationTimeouts = new Map(); + this.connectionState = 'idle'; // idle, connecting, connected, failed, disconnected + this.lastStateChange = Date.now(); + this.retryCount = 0; + this.maxRetries = 3; + this.baseRetryDelay = 1000; + + // Backpressure and resource management + this.operationQueue = []; + this.maxConcurrentOperations = 2; + this.maxQueueSize = 5; + this.operationMetrics = { + totalOperations: 0, + successfulOperations: 0, + failedOperations: 0, + timeouts: 0, + averageResponseTime: 0 + }; + + // Configuration + this.ipcTimeout = 10000; // 10s for IPC calls + this.operationTimeout = 15000; // 15s for complex operations + + // Health monitoring system + this.healthCheck = { + enabled: false, + intervalId: null, + intervalMs: 30000, // 30s + lastCheck: 0, + consecutiveFailures: 0, + maxFailures: 3 + }; + + // Load user model history from localStorage + this.loadUserModelHistory(); this.loadProviderConfig(); //////// after_modelStateService //////// @@ -247,6 +330,11 @@ export class ApiKeyHeader extends LitElement { this.handleAnimationEnd = this.handleAnimationEnd.bind(this) this.handleUsePicklesKey = this.handleUsePicklesKey.bind(this) this.handleProviderChange = this.handleProviderChange.bind(this) + this.handleLlmProviderChange = this.handleLlmProviderChange.bind(this) + this.handleSttProviderChange = this.handleSttProviderChange.bind(this) + this.handleMessageFadeEnd = this.handleMessageFadeEnd.bind(this) + this.handleModelKeyPress = this.handleModelKeyPress.bind(this) + this.handleSttModelChange = this.handleSttModelChange.bind(this) } reset() { @@ -261,35 +349,113 @@ export class ApiKeyHeader extends LitElement { async loadProviderConfig() { if (!window.require) return; const { ipcRenderer } = window.require('electron'); - const config = await ipcRenderer.invoke('model:get-provider-config'); - const llmProviders = []; - const sttProviders = []; + try { + const [config, ollamaStatus] = await Promise.all([ + ipcRenderer.invoke('model:get-provider-config'), + ipcRenderer.invoke('ollama:get-status') + ]); + + const llmProviders = []; + const sttProviders = []; - for (const id in config) { - // 'openai-glass' ๊ฐ™์€ ๊ฐ€์ƒ Provider๋Š” UI์— ํ‘œ์‹œํ•˜์ง€ ์•Š์Œ - if (id.includes('-glass')) continue; + for (const id in config) { + // 'openai-glass' ๊ฐ™์€ ๊ฐ€์ƒ Provider๋Š” UI์— ํ‘œ์‹œํ•˜์ง€ ์•Š์Œ + if (id.includes('-glass')) continue; + const hasLlmModels = config[id].llmModels.length > 0 || id === 'ollama'; + const hasSttModels = config[id].sttModels.length > 0 || id === 'whisper'; - if (config[id].llmModels.length > 0) { - llmProviders.push({ id, name: config[id].name }); + if (hasLlmModels) { + llmProviders.push({ id, name: config[id].name }); + } + if (hasSttModels) { + sttProviders.push({ id, name: config[id].name }); + } } - if (config[id].sttModels.length > 0) { - sttProviders.push({ id, name: config[id].name }); + + this.providers = { llm: llmProviders, stt: sttProviders }; + + // ๊ธฐ๋ณธ ์„ ํƒ ๊ฐ’ ์„ค์ • + if (llmProviders.length > 0) this.llmProvider = llmProviders[0].id; + if (sttProviders.length > 0) this.sttProvider = sttProviders[0].id; + + // Ollama ์ƒํƒœ ๋ฐ ๋ชจ๋ธ ์ œ์•ˆ ๋กœ๋“œ + if (ollamaStatus?.success) { + this.ollamaStatus = { + installed: ollamaStatus.installed, + running: ollamaStatus.running + }; + + // Load model suggestions if Ollama is running + if (ollamaStatus.running) { + await this.loadModelSuggestions(); + } } + + this.requestUpdate(); + } catch (error) { + console.error('[ApiKeyHeader] Failed to load provider config:', error); + } +} + + async handleMouseDown(e) { + if (e.target.tagName === "INPUT" || e.target.tagName === "BUTTON" || e.target.tagName === "SELECT") { + return + } + + e.preventDefault() + + const { ipcRenderer } = window.require("electron") + const initialPosition = await ipcRenderer.invoke("get-header-position") + + this.dragState = { + initialMouseX: e.screenX, + initialMouseY: e.screenY, + initialWindowX: initialPosition.x, + initialWindowY: initialPosition.y, + moved: false, + } + + window.addEventListener("mousemove", this.handleMouseMove) + window.addEventListener("mouseup", this.handleMouseUp, { once: true }) + } + + handleMouseMove(e) { + if (!this.dragState) return + + const deltaX = Math.abs(e.screenX - this.dragState.initialMouseX) + const deltaY = Math.abs(e.screenY - this.dragState.initialMouseY) + + if (deltaX > 3 || deltaY > 3) { + this.dragState.moved = true + } + + const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX) + const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY) + + const { ipcRenderer } = window.require("electron") + ipcRenderer.invoke("move-header-to", newWindowX, newWindowY) + } + + handleMouseUp(e) { + if (!this.dragState) return + + const wasDragged = this.dragState.moved + + window.removeEventListener("mousemove", this.handleMouseMove) + this.dragState = null + + if (wasDragged) { + this.wasJustDragged = true + setTimeout(() => { + this.wasJustDragged = false + }, 200) } - - this.providers = { llm: llmProviders, stt: sttProviders }; - - // ๊ธฐ๋ณธ ์„ ํƒ ๊ฐ’ ์„ค์ • - if (llmProviders.length > 0) this.llmProvider = llmProviders[0].id; - if (sttProviders.length > 0) this.sttProvider = sttProviders[0].id; - - this.requestUpdate(); } handleInput(e) { this.apiKey = e.target.value - this.errorMessage = "" + this.clearMessages() console.log("Input changed:", this.apiKey?.length || 0, "chars") this.requestUpdate() @@ -300,17 +466,878 @@ export class ApiKeyHeader extends LitElement { } }) } + + clearMessages() { + this.errorMessage = "" + this.successMessage = "" + this.messageTimestamp = 0 + } handleProviderChange(e) { this.selectedProvider = e.target.value - this.errorMessage = "" + this.clearMessages() console.log("Provider changed to:", this.selectedProvider) this.requestUpdate() } + async handleLlmProviderChange(e) { + // Cancel any active operations first + this._cancelAllActiveOperations(); + + this.llmProvider = e.target.value; + this.errorMessage = ""; + this.successMessage = ""; + + // Reset retry state + this.retryCount = 0; + + if (this.llmProvider === 'ollama') { + console.log('[ApiKeyHeader] Ollama selected, initiating connection...'); + await this._initializeOllamaConnection(); + // Start health monitoring for Ollama + this._startHealthMonitoring(); + } else { + this._updateConnectionState('idle', 'Non-Ollama provider selected'); + // Stop health monitoring for non-Ollama providers + this._stopHealthMonitoring(); + } + + this.requestUpdate(); + } + + async _initializeOllamaConnection() { + try { + // Progressive connection attempt with exponential backoff + await this._attemptOllamaConnection(); + } catch (error) { + console.error('[ApiKeyHeader] Initial Ollama connection failed:', error.message); + + if (this.retryCount < this.maxRetries) { + const delay = this.baseRetryDelay * Math.pow(2, this.retryCount); + console.log(`[ApiKeyHeader] Retrying Ollama connection in ${delay}ms (attempt ${this.retryCount + 1}/${this.maxRetries})`); + + this.retryCount++; + + // Use proper Promise-based delay instead of setTimeout + await new Promise(resolve => { + const retryTimeoutId = setTimeout(() => { + this._initializeOllamaConnection(); + resolve(); + }, delay); + + // Store timeout for cleanup + this.operationTimeouts.set(`retry_${this.retryCount}`, retryTimeoutId); + }); + } else { + this._updateConnectionState('failed', `Connection failed after ${this.maxRetries} attempts`); + } + } + } + + async _attemptOllamaConnection() { + await this.refreshOllamaStatus(); + } + + _cancelAllActiveOperations() { + console.log(`[ApiKeyHeader] Cancelling ${this.activeOperations.size} active operations and ${this.operationQueue.length} queued operations`); + + // Cancel active operations + for (const [operationType, operation] of this.activeOperations) { + this._cancelOperation(operationType); + } + + // Cancel queued operations + for (const queuedOp of this.operationQueue) { + queuedOp.reject(new Error(`Operation ${queuedOp.type} cancelled during cleanup`)); + } + this.operationQueue.length = 0; + + // Clean up all timeouts + for (const [timeoutId, timeout] of this.operationTimeouts) { + clearTimeout(timeout); + } + this.operationTimeouts.clear(); + } + + /** + * Get operation metrics for monitoring + */ + getOperationMetrics() { + return { + ...this.operationMetrics, + activeOperations: this.activeOperations.size, + queuedOperations: this.operationQueue.length, + successRate: this.operationMetrics.totalOperations > 0 ? + (this.operationMetrics.successfulOperations / this.operationMetrics.totalOperations) * 100 : 0 + }; + } + + /** + * Adaptive backpressure based on system performance + */ + _adjustBackpressureThresholds() { + const metrics = this.getOperationMetrics(); + + // Reduce concurrent operations if success rate is low + if (metrics.successRate < 70 && this.maxConcurrentOperations > 1) { + this.maxConcurrentOperations = Math.max(1, this.maxConcurrentOperations - 1); + console.log(`[ApiKeyHeader] Reduced max concurrent operations to ${this.maxConcurrentOperations} (success rate: ${metrics.successRate.toFixed(1)}%)`); + } + + // Increase if performance is good + if (metrics.successRate > 90 && metrics.averageResponseTime < 3000 && this.maxConcurrentOperations < 3) { + this.maxConcurrentOperations++; + console.log(`[ApiKeyHeader] Increased max concurrent operations to ${this.maxConcurrentOperations}`); + } + } + + /** + * Professional health monitoring system + */ + _startHealthMonitoring() { + if (this.healthCheck.enabled) return; + + this.healthCheck.enabled = true; + this.healthCheck.intervalId = setInterval(() => { + this._performHealthCheck(); + }, this.healthCheck.intervalMs); + + console.log(`[ApiKeyHeader] Health monitoring started (interval: ${this.healthCheck.intervalMs}ms)`); + } + + _stopHealthMonitoring() { + if (!this.healthCheck.enabled) return; + + this.healthCheck.enabled = false; + if (this.healthCheck.intervalId) { + clearInterval(this.healthCheck.intervalId); + this.healthCheck.intervalId = null; + } + + console.log('[ApiKeyHeader] Health monitoring stopped'); + } + + async _performHealthCheck() { + // Only perform health check if Ollama is selected and we're in a stable state + if (this.llmProvider !== 'ollama' || this.connectionState === 'connecting') { + return; + } + + const now = Date.now(); + this.healthCheck.lastCheck = now; + + try { + // Lightweight health check - just ping the service + const isHealthy = await this._executeOperation('health_check', async () => { + if (!window.require) return false; + const { ipcRenderer } = window.require('electron'); + const result = await ipcRenderer.invoke('ollama:get-status'); + return result?.success && result?.running; + }, { timeout: 5000, priority: 'low' }); + + if (isHealthy) { + this.healthCheck.consecutiveFailures = 0; + + // Update state if we were previously failed + if (this.connectionState === 'failed') { + this._updateConnectionState('connected', 'Health check recovered'); + } + } else { + this._handleHealthCheckFailure(); + } + + // Adjust thresholds based on performance + this._adjustBackpressureThresholds(); + + } catch (error) { + console.warn('[ApiKeyHeader] Health check failed:', error.message); + this._handleHealthCheckFailure(); + } + } + + _handleHealthCheckFailure() { + this.healthCheck.consecutiveFailures++; + + if (this.healthCheck.consecutiveFailures >= this.healthCheck.maxFailures) { + console.warn(`[ApiKeyHeader] Health check failed ${this.healthCheck.consecutiveFailures} times, marking as disconnected`); + this._updateConnectionState('failed', 'Service health check failed'); + + // Increase health check frequency when having issues + this.healthCheck.intervalMs = Math.max(10000, this.healthCheck.intervalMs / 2); + this._restartHealthMonitoring(); + } + } + + _restartHealthMonitoring() { + this._stopHealthMonitoring(); + this._startHealthMonitoring(); + } + + /** + * Get comprehensive health status + */ + getHealthStatus() { + return { + connection: { + state: this.connectionState, + lastStateChange: this.lastStateChange, + timeSinceLastChange: Date.now() - this.lastStateChange + }, + operations: this.getOperationMetrics(), + health: { + enabled: this.healthCheck.enabled, + lastCheck: this.healthCheck.lastCheck, + timeSinceLastCheck: this.healthCheck.lastCheck > 0 ? Date.now() - this.healthCheck.lastCheck : null, + consecutiveFailures: this.healthCheck.consecutiveFailures, + intervalMs: this.healthCheck.intervalMs + }, + ollama: { + provider: this.llmProvider, + status: this.ollamaStatus, + selectedModel: this.selectedLlmModel + } + }; + } + + async handleSttProviderChange(e) { + this.sttProvider = e.target.value; + this.errorMessage = ""; + this.successMessage = ""; + + if (this.sttProvider === 'ollama') { + console.warn('[ApiKeyHeader] Ollama does not support STT yet. Please select Whisper or another provider.'); + this.errorMessage = 'Ollama does not support STT yet. Please select Whisper or another STT provider.'; + this.messageTimestamp = Date.now(); + + // Auto-select Whisper if available + const whisperProvider = this.providers.stt.find(p => p.id === 'whisper'); + if (whisperProvider) { + this.sttProvider = 'whisper'; + console.log('[ApiKeyHeader] Auto-selected Whisper for STT'); + } + } + + this.requestUpdate(); + } + + /** + * Professional operation management with backpressure control + */ + async _executeOperation(operationType, operation, options = {}) { + const operationId = `${operationType}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + const timeout = options.timeout || this.ipcTimeout; + const priority = options.priority || 'normal'; // high, normal, low + + // Backpressure control + if (this.activeOperations.size >= this.maxConcurrentOperations) { + if (this.operationQueue.length >= this.maxQueueSize) { + throw new Error(`Operation queue full (${this.maxQueueSize}), rejecting ${operationType}`); + } + + console.log(`[ApiKeyHeader] Queuing operation ${operationType} (${this.activeOperations.size} active)`); + return this._queueOperation(operationId, operationType, operation, options); + } + + return this._executeImmediately(operationId, operationType, operation, timeout); + } + + async _queueOperation(operationId, operationType, operation, options) { + return new Promise((resolve, reject) => { + const queuedOperation = { + id: operationId, + type: operationType, + operation, + options, + resolve, + reject, + queuedAt: Date.now(), + priority: options.priority || 'normal' + }; + + // Insert based on priority (high priority first) + if (options.priority === 'high') { + this.operationQueue.unshift(queuedOperation); + } else { + this.operationQueue.push(queuedOperation); + } + + console.log(`[ApiKeyHeader] Queued ${operationType} (queue size: ${this.operationQueue.length})`); + }); + } + + async _executeImmediately(operationId, operationType, operation, timeout) { + const startTime = Date.now(); + this.operationMetrics.totalOperations++; + + // Check if similar operation is already running + if (this.activeOperations.has(operationType)) { + console.log(`[ApiKeyHeader] Operation ${operationType} already in progress, cancelling previous`); + this._cancelOperation(operationType); + } + + // Create cancellation mechanism + const cancellationPromise = new Promise((_, reject) => { + const timeoutId = setTimeout(() => { + this.operationMetrics.timeouts++; + reject(new Error(`Operation ${operationType} timeout after ${timeout}ms`)); + }, timeout); + + this.operationTimeouts.set(operationId, timeoutId); + }); + + const operationPromise = Promise.race([ + operation(), + cancellationPromise + ]); + + this.activeOperations.set(operationType, { + id: operationId, + promise: operationPromise, + startTime + }); + + try { + const result = await operationPromise; + this._recordOperationSuccess(startTime); + return result; + } catch (error) { + this._recordOperationFailure(error, operationType); + throw error; + } finally { + this._cleanupOperation(operationId, operationType); + this._processQueue(); + } + } + + _recordOperationSuccess(startTime) { + this.operationMetrics.successfulOperations++; + const responseTime = Date.now() - startTime; + this._updateAverageResponseTime(responseTime); + } + + _recordOperationFailure(error, operationType) { + this.operationMetrics.failedOperations++; + + if (error.message.includes('timeout')) { + console.error(`[ApiKeyHeader] Operation ${operationType} timed out`); + this._updateConnectionState('failed', `Timeout: ${error.message}`); + } + } + + _updateAverageResponseTime(responseTime) { + const totalOps = this.operationMetrics.successfulOperations; + this.operationMetrics.averageResponseTime = + ((this.operationMetrics.averageResponseTime * (totalOps - 1)) + responseTime) / totalOps; + } + + async _processQueue() { + if (this.operationQueue.length === 0 || this.activeOperations.size >= this.maxConcurrentOperations) { + return; + } + + const queuedOp = this.operationQueue.shift(); + if (!queuedOp) return; + + const queueTime = Date.now() - queuedOp.queuedAt; + console.log(`[ApiKeyHeader] Processing queued operation ${queuedOp.type} (waited ${queueTime}ms)`); + + try { + const result = await this._executeImmediately( + queuedOp.id, + queuedOp.type, + queuedOp.operation, + queuedOp.options.timeout || this.ipcTimeout + ); + queuedOp.resolve(result); + } catch (error) { + queuedOp.reject(error); + } + } + + _cancelOperation(operationType) { + const operation = this.activeOperations.get(operationType); + if (operation) { + this._cleanupOperation(operation.id, operationType); + console.log(`[ApiKeyHeader] Cancelled operation: ${operationType}`); + } + } + + _cleanupOperation(operationId, operationType) { + if (this.operationTimeouts.has(operationId)) { + clearTimeout(this.operationTimeouts.get(operationId)); + this.operationTimeouts.delete(operationId); + } + this.activeOperations.delete(operationType); + } + + _updateConnectionState(newState, reason = '') { + if (this.connectionState !== newState) { + console.log(`[ApiKeyHeader] Connection state: ${this.connectionState} -> ${newState} (${reason})`); + this.connectionState = newState; + this.lastStateChange = Date.now(); + + // Update UI based on state + this._handleStateChange(newState, reason); + } + } + + _handleStateChange(state, reason) { + switch (state) { + case 'connecting': + this.installingModel = 'Connecting to Ollama...'; + this.installProgress = 10; + break; + case 'failed': + this.errorMessage = reason || 'Connection failed'; + this.installingModel = null; + this.installProgress = 0; + this.messageTimestamp = Date.now(); + break; + case 'connected': + this.installingModel = null; + this.installProgress = 0; + break; + case 'disconnected': + this.ollamaStatus = { installed: false, running: false }; + break; + } + this.requestUpdate(); + } + + async refreshOllamaStatus() { + if (!window.require) return; + + try { + this._updateConnectionState('connecting', 'Checking Ollama status'); + + const result = await this._executeOperation('ollama_status', async () => { + const { ipcRenderer } = window.require('electron'); + return await ipcRenderer.invoke('ollama:get-status'); + }); + + if (result?.success) { + this.ollamaStatus = { + installed: result.installed, + running: result.running + }; + + this._updateConnectionState('connected', 'Status updated successfully'); + + // Load model suggestions if Ollama is running + if (result.running) { + await this.loadModelSuggestions(); + } + } else { + this._updateConnectionState('failed', result?.error || 'Status check failed'); + } + } catch (error) { + console.error('[ApiKeyHeader] Failed to refresh Ollama status:', error.message); + this._updateConnectionState('failed', error.message); + } + } + + async loadModelSuggestions() { + if (!window.require) return; + + try { + const result = await this._executeOperation('model_suggestions', async () => { + const { ipcRenderer } = window.require('electron'); + return await ipcRenderer.invoke('ollama:get-model-suggestions'); + }); + + if (result?.success) { + this.modelSuggestions = result.suggestions || []; + + // ๊ธฐ๋ณธ ๋ชจ๋ธ ์„ ํƒ (์„ค์น˜๋œ ๋ชจ๋ธ ์ค‘ ์ฒซ ๋ฒˆ์งธ) + if (!this.selectedLlmModel && this.modelSuggestions.length > 0) { + const installedModel = this.modelSuggestions.find(m => m.status === 'installed'); + if (installedModel) { + this.selectedLlmModel = installedModel.name; + } + } + this.requestUpdate(); + } else { + console.warn('[ApiKeyHeader] Model suggestions request unsuccessful:', result?.error); + } + } catch (error) { + console.error('[ApiKeyHeader] Failed to load model suggestions:', error.message); + } + } + + async ensureOllamaReady() { + if (!window.require) return false; + + try { + this._updateConnectionState('connecting', 'Ensuring Ollama is ready'); + + const result = await this._executeOperation('ollama_ensure_ready', async () => { + const { ipcRenderer } = window.require('electron'); + return await ipcRenderer.invoke('ollama:ensure-ready'); + }, { timeout: this.operationTimeout }); + + if (result?.success) { + await this.refreshOllamaStatus(); + this._updateConnectionState('connected', 'Ollama ready'); + return true; + } else { + const errorMsg = `Failed to setup Ollama: ${result?.error || 'Unknown error'}`; + this._updateConnectionState('failed', errorMsg); + return false; + } + } catch (error) { + console.error('[ApiKeyHeader] Failed to ensure Ollama ready:', error.message); + this._updateConnectionState('failed', `Error setting up Ollama: ${error.message}`); + return false; + } + } + + async ensureOllamaReadyWithUI() { + if (!window.require) return false; + const { ipcRenderer } = window.require("electron"); + + this.installingModel = "Setting up Ollama"; + this.installProgress = 0; + this.clearMessages(); + this.requestUpdate(); + + const progressHandler = (event, data) => { + let baseProgress = 0; + let stageTotal = 0; + + switch (data.stage) { + case "downloading": + baseProgress = 0; + stageTotal = 70; + break; + case "mounting": + baseProgress = 70; + stageTotal = 10; + break; + case "installing": + baseProgress = 80; + stageTotal = 10; + break; + case "linking": + baseProgress = 90; + stageTotal = 5; + break; + case "cleanup": + baseProgress = 95; + stageTotal = 3; + break; + case "starting": + baseProgress = 98; + stageTotal = 2; + break; + } + + const overallProgress = baseProgress + (data.progress / 100) * stageTotal; + + this.installingModel = data.message; + this.installProgress = Math.round(overallProgress); + this.requestUpdate(); + }; + + let operationCompleted = false; + const completionTimeout = setTimeout(async () => { + if (!operationCompleted) { + console.log("[ApiKeyHeader] Operation timeout, checking status manually..."); + await this._handleOllamaSetupCompletion(true); + } + }, 15000); // 15 second timeout + + const completionHandler = async (event, result) => { + if (operationCompleted) return; + operationCompleted = true; + clearTimeout(completionTimeout); + + ipcRenderer.removeListener("ollama:install-progress", progressHandler); + await this._handleOllamaSetupCompletion(result.success, result.error); + }; + + ipcRenderer.once("ollama:install-complete", completionHandler); + ipcRenderer.on("ollama:install-progress", progressHandler); + + try { + let result; + if (!this.ollamaStatus.installed) { + console.log("[ApiKeyHeader] Ollama not installed. Starting installation."); + result = await ipcRenderer.invoke("ollama:install"); + } else { + console.log("[ApiKeyHeader] Ollama installed. Starting service."); + result = await ipcRenderer.invoke("ollama:start-service"); + } + + // If IPC call succeeds but no event received, handle completion manually + if (result?.success && !operationCompleted) { + setTimeout(async () => { + if (!operationCompleted) { + operationCompleted = true; + clearTimeout(completionTimeout); + await this._handleOllamaSetupCompletion(true); + } + }, 2000); + } + + } catch (error) { + operationCompleted = true; + clearTimeout(completionTimeout); + console.error("[ApiKeyHeader] Ollama setup failed:", error); + ipcRenderer.removeListener("ollama:install-progress", progressHandler); + ipcRenderer.removeListener("ollama:install-complete", completionHandler); + await this._handleOllamaSetupCompletion(false, error.message); + } + } + + async _handleOllamaSetupCompletion(success, errorMessage = null) { + this.installingModel = null; + this.installProgress = 0; + + if (success) { + await this.refreshOllamaStatus(); + this.successMessage = "โœ“ Ollama is ready!"; + } else { + this.errorMessage = `Setup failed: ${errorMessage || "Unknown error"}`; + } + this.messageTimestamp = Date.now(); + this.requestUpdate(); + } + + async handleModelInput(e) { + const modelName = e.target.value.trim(); + this.selectedLlmModel = modelName; + this.clearMessages(); + + // Save to user history if it's a valid model name + if (modelName && modelName.length > 2) { + this.saveToUserHistory(modelName); + } + + this.requestUpdate(); + } + + async handleModelKeyPress(e) { + if (e.key === 'Enter' && this.selectedLlmModel?.trim()) { + e.preventDefault(); + console.log(`[ApiKeyHeader] Enter pressed, installing model: ${this.selectedLlmModel}`); + + // Check if Ollama is ready first + const ollamaReady = await this.ensureOllamaReady(); + if (!ollamaReady) { + this.errorMessage = 'Failed to setup Ollama'; + this.messageTimestamp = Date.now(); + this.requestUpdate(); + return; + } + + // Install the model + await this.installModel(this.selectedLlmModel); + } + } + + loadUserModelHistory() { + try { + const saved = localStorage.getItem('ollama-model-history'); + if (saved) { + this.userModelHistory = JSON.parse(saved); + } + } catch (error) { + console.error('[ApiKeyHeader] Failed to load model history:', error); + this.userModelHistory = []; + } + } + + saveToUserHistory(modelName) { + if (!modelName || !modelName.trim()) return; + + // Remove if already exists (to move to front) + this.userModelHistory = this.userModelHistory.filter(m => m !== modelName); + + // Add to front + this.userModelHistory.unshift(modelName); + + // Keep only last 20 entries + this.userModelHistory = this.userModelHistory.slice(0, 20); + + // Save to localStorage + try { + localStorage.setItem('ollama-model-history', JSON.stringify(this.userModelHistory)); + } catch (error) { + console.error('[ApiKeyHeader] Failed to save model history:', error); + } + } + + getCombinedModelSuggestions() { + const combined = []; + + // Add installed models first (from Ollama CLI) + for (const model of this.modelSuggestions) { + combined.push({ + name: model.name, + status: 'installed', + size: model.size || 'Unknown', + source: 'installed' + }); + } + + // Add user history models that aren't already installed + const installedNames = this.modelSuggestions.map(m => m.name); + for (const modelName of this.userModelHistory) { + if (!installedNames.includes(modelName)) { + combined.push({ + name: modelName, + status: 'history', + size: 'Unknown', + source: 'history' + }); + } + } + + return combined; + } + + async installModel(modelName) { + if (!modelName?.trim()) { + throw new Error('Invalid model name'); + } + + this.installingModel = modelName; + this.installProgress = 0; + this.clearMessages(); + this.requestUpdate(); + + const { ipcRenderer } = window.require('electron'); + let progressHandler = null; + + try { + console.log(`[ApiKeyHeader] Installing model via Ollama REST API: ${modelName}`); + + // Create robust progress handler with timeout protection + progressHandler = (event, data) => { + if (data.model === modelName && !this._isOperationCancelled(modelName)) { + const progress = Math.round(Math.max(0, Math.min(100, data.progress || 0))); + + if (progress !== this.installProgress) { + this.installProgress = progress; + console.log(`[ApiKeyHeader] API Progress: ${progress}% for ${modelName} (${data.status || 'downloading'})`); + this.requestUpdate(); + } + } + }; + + // Set up progress tracking + ipcRenderer.on('ollama:pull-progress', progressHandler); + + // Execute the model pull with timeout + const installPromise = ipcRenderer.invoke('ollama:pull-model', modelName); + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Installation timeout after 10 minutes')), 600000) + ); + + const result = await Promise.race([installPromise, timeoutPromise]); + + if (result.success) { + console.log(`[ApiKeyHeader] Model ${modelName} installed successfully via API`); + this.installProgress = 100; + this.requestUpdate(); + + // Brief pause to show completion + await new Promise(resolve => setTimeout(resolve, 300)); + + // Refresh status and show success + await this.refreshOllamaStatus(); + this.successMessage = `โœ“ ${modelName} ready`; + this.messageTimestamp = Date.now(); + } else { + throw new Error(result.error || 'Installation failed'); + } + } catch (error) { + console.error(`[ApiKeyHeader] Model installation failed:`, error); + this.errorMessage = `Failed: ${error.message}`; + this.messageTimestamp = Date.now(); + } finally { + // Comprehensive cleanup + if (progressHandler) { + ipcRenderer.removeListener('ollama:pull-progress', progressHandler); + } + + this.installingModel = null; + this.installProgress = 0; + this.requestUpdate(); + } + } + + _isOperationCancelled(modelName) { + return !this.installingModel || this.installingModel !== modelName; + } + + async downloadWhisperModel(modelId) { + if (!modelId?.trim()) { + console.warn('[ApiKeyHeader] Invalid Whisper model ID'); + return; + } + + console.log(`[ApiKeyHeader] Starting Whisper model download: ${modelId}`); + + // Mark as installing + this.whisperInstallingModels = { ...this.whisperInstallingModels, [modelId]: 0 }; + this.clearMessages(); + this.requestUpdate(); + + const { ipcRenderer } = window.require('electron'); + let progressHandler = null; + + try { + // Set up robust progress listener + progressHandler = (event, { modelId: id, progress }) => { + if (id === modelId) { + const cleanProgress = Math.round(Math.max(0, Math.min(100, progress || 0))); + this.whisperInstallingModels = { ...this.whisperInstallingModels, [modelId]: cleanProgress }; + console.log(`[ApiKeyHeader] Whisper download progress: ${cleanProgress}% for ${modelId}`); + this.requestUpdate(); + } + }; + + ipcRenderer.on('whisper:download-progress', progressHandler); + + // Start download with timeout protection + const downloadPromise = ipcRenderer.invoke('whisper:download-model', modelId); + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Download timeout after 10 minutes')), 600000) + ); + + const result = await Promise.race([downloadPromise, timeoutPromise]); + + if (result?.success) { + this.successMessage = `โœ“ ${modelId} downloaded successfully`; + this.messageTimestamp = Date.now(); + console.log(`[ApiKeyHeader] Whisper model ${modelId} downloaded successfully`); + + // Auto-select the downloaded model + this.selectedSttModel = modelId; + } else { + this.errorMessage = `Failed to download ${modelId}: ${result?.error || 'Unknown error'}`; + this.messageTimestamp = Date.now(); + console.error(`[ApiKeyHeader] Whisper download failed:`, result?.error); + } + + } catch (error) { + console.error(`[ApiKeyHeader] Error downloading Whisper model ${modelId}:`, error); + this.errorMessage = `Error downloading ${modelId}: ${error.message}`; + this.messageTimestamp = Date.now(); + } finally { + // Cleanup + if (progressHandler) { + ipcRenderer.removeListener('whisper:download-progress', progressHandler); + } + delete this.whisperInstallingModels[modelId]; + this.requestUpdate(); + } + } + handlePaste(e) { e.preventDefault() - this.errorMessage = "" + this.clearMessages() const clipboardText = (e.clipboardData || window.clipboardData).getData("text") console.log("Paste event detected:", clipboardText?.substring(0, 10) + "...") @@ -339,39 +1366,127 @@ export class ApiKeyHeader extends LitElement { } //////// after_modelStateService //////// - async handleSubmit() { - console.log('[ApiKeyHeader] handleSubmit: Submitting API keys...'); - if (this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim()) { - this.errorMessage = "Please enter keys for both LLM and STT."; - return; + async handleSttModelChange(e) { + const modelId = e.target.value; + this.selectedSttModel = modelId; + + if (modelId && this.sttProvider === 'whisper') { + // Check if model needs to be downloaded + const isInstalling = this.whisperInstallingModels[modelId] !== undefined; + if (!isInstalling) { + console.log(`[ApiKeyHeader] Auto-installing Whisper model: ${modelId}`); + await this.downloadWhisperModel(modelId); + } } + + this.requestUpdate(); + } + async handleSubmit() { + console.log('[ApiKeyHeader] handleSubmit: Submitting...'); + this.isLoading = true; - this.errorMessage = ""; + this.clearMessages(); this.requestUpdate(); const { ipcRenderer } = window.require('electron'); + + try { + // Handle LLM provider + let llmResult; + if (this.llmProvider === 'ollama') { + // For Ollama ensure it's ready and validate model selection + if (!this.selectedLlmModel?.trim()) { + throw new Error('Please enter an Ollama model name'); + } + + const ollamaReady = await this.ensureOllamaReady(); + if (!ollamaReady) { + throw new Error('Failed to setup Ollama'); + } + + // Check if model is installed, if not install it + const selectedModel = this.getCombinedModelSuggestions().find(m => m.name === this.selectedLlmModel); + if (!selectedModel || selectedModel.status !== 'installed') { + console.log(`[ApiKeyHeader] Installing model ${this.selectedLlmModel}...`); + await this.installModel(this.selectedLlmModel); + } + + // Validate Ollama is working + llmResult = await ipcRenderer.invoke('model:validate-key', { + provider: 'ollama', + key: 'local' + }); + + if (llmResult.success) { + // Set the selected model + await ipcRenderer.invoke('model:set-selected-model', { + type: 'llm', + modelId: this.selectedLlmModel + }); + } + } else { + // For other providers, validate API key + if (!this.llmApiKey.trim()) { + throw new Error('Please enter LLM API key'); + } + + llmResult = await ipcRenderer.invoke('model:validate-key', { + provider: this.llmProvider, + key: this.llmApiKey.trim() + }); + } - console.log('[ApiKeyHeader] handleSubmit: Validating LLM key...'); - const llmValidation = ipcRenderer.invoke('model:validate-key', { provider: this.llmProvider, key: this.llmApiKey.trim() }); - const sttValidation = ipcRenderer.invoke('model:validate-key', { provider: this.sttProvider, key: this.sttApiKey.trim() }); + // Handle STT provider + let sttResult; + if (this.sttProvider === 'ollama') { + // Ollama doesn't support STT yet, so skip or use same as LLM validation + sttResult = { success: true }; + } else if (this.sttProvider === 'whisper') { + // For Whisper, just validate it's enabled (model download already handled in handleSttModelChange) + sttResult = await ipcRenderer.invoke('model:validate-key', { + provider: 'whisper', + key: 'local' + }); + + if (sttResult.success && this.selectedSttModel) { + // Set the selected model + await ipcRenderer.invoke('model:set-selected-model', { + type: 'stt', + modelId: this.selectedSttModel + }); + } + } else { + // For other providers, validate API key + if (!this.sttApiKey.trim()) { + throw new Error('Please enter STT API key'); + } + + sttResult = await ipcRenderer.invoke('model:validate-key', { + provider: this.sttProvider, + key: this.sttApiKey.trim() + }); + } - const [llmResult, sttResult] = await Promise.all([llmValidation, sttValidation]); - - if (llmResult.success && sttResult.success) { - console.log('[ApiKeyHeader] handleSubmit: Both LLM and STT keys are valid.'); - this.startSlideOutAnimation(); - } else { - console.log('[ApiKeyHeader] handleSubmit: Validation failed.'); - let errorParts = []; - if (!llmResult.success) errorParts.push(`LLM Key: ${llmResult.error || 'Invalid'}`); - if (!sttResult.success) errorParts.push(`STT Key: ${sttResult.error || 'Invalid'}`); - this.errorMessage = errorParts.join(' | '); + if (llmResult.success && sttResult.success) { + console.log('[ApiKeyHeader] handleSubmit: Validation successful.'); + this.startSlideOutAnimation(); + } else { + let errorParts = []; + if (!llmResult.success) errorParts.push(`LLM: ${llmResult.error || 'Invalid'}`); + if (!sttResult.success) errorParts.push(`STT: ${sttResult.error || 'Invalid'}`); + this.errorMessage = errorParts.join(' | '); + this.messageTimestamp = Date.now(); + } + } catch (error) { + console.error('[ApiKeyHeader] handleSubmit: Error:', error); + this.errorMessage = error.message; + this.messageTimestamp = Date.now(); } this.isLoading = false; this.requestUpdate(); -} + } //////// after_modelStateService //////// @@ -402,10 +1517,46 @@ export class ApiKeyHeader extends LitElement { if (e.target !== this || !this.classList.contains('sliding-out')) return; this.classList.remove("sliding-out"); this.classList.add("hidden"); - window.require('electron').ipcRenderer.invoke('get-current-user').then(userState => { - console.log('[ApiKeyHeader] handleAnimationEnd: User state updated:', userState); - this.stateUpdateCallback?.(userState); - }); + + console.log('[ApiKeyHeader] handleAnimationEnd: Animation completed, transitioning to next state...'); + + if (!window.require) { + console.error('[ApiKeyHeader] handleAnimationEnd: window.require not available'); + return; + } + + if (!this.stateUpdateCallback) { + console.error('[ApiKeyHeader] handleAnimationEnd: stateUpdateCallback not set! This will prevent transition to main window.'); + return; + } + + const { ipcRenderer } = window.require('electron'); + + ipcRenderer.invoke('get-current-user') + .then(userState => { + console.log('[ApiKeyHeader] handleAnimationEnd: User state retrieved:', userState); + + // Additional validation for local providers + return ipcRenderer.invoke('model:are-providers-configured').then(isConfigured => { + console.log('[ApiKeyHeader] handleAnimationEnd: Providers configured check:', isConfigured); + + if (!isConfigured) { + console.warn('[ApiKeyHeader] handleAnimationEnd: Providers still not configured, may return to ApiKey screen'); + } + + // Call the state update callback + this.stateUpdateCallback(userState); + }); + }) + .catch(error => { + console.error('[ApiKeyHeader] handleAnimationEnd: Error during state transition:', error); + + // Fallback: try to call callback with minimal state + if (this.stateUpdateCallback) { + console.log('[ApiKeyHeader] handleAnimationEnd: Attempting fallback state transition...'); + this.stateUpdateCallback({ isLoggedIn: false }); + } + }); } //////// after_modelStateService //////// @@ -413,41 +1564,346 @@ export class ApiKeyHeader extends LitElement { super.connectedCallback() this.addEventListener("animationend", this.handleAnimationEnd) } + + handleMessageFadeEnd(e) { + if (e.animationName === 'fadeOut') { + // Clear the message that finished fading + if (e.target.classList.contains('error-message')) { + this.errorMessage = ''; + } else if (e.target.classList.contains('success-message')) { + this.successMessage = ''; + } + this.messageTimestamp = 0; + this.requestUpdate(); + } + } disconnectedCallback() { super.disconnectedCallback() this.removeEventListener("animationend", this.handleAnimationEnd) + + // Professional cleanup of all resources + this._performCompleteCleanup(); + } + + _performCompleteCleanup() { + console.log('[ApiKeyHeader] Performing complete cleanup'); + + // Stop health monitoring + this._stopHealthMonitoring(); + + // Cancel all active operations + this._cancelAllActiveOperations(); + + // Cancel any ongoing installations when component is destroyed + if (this.installingModel) { + this.progressTracker.cancelInstallation(this.installingModel); + } + + // Cleanup event listeners + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.removeAllListeners('whisper:download-progress'); + ipcRenderer.removeAllListeners('ollama:install-progress'); + ipcRenderer.removeAllListeners('ollama:pull-progress'); + ipcRenderer.removeAllListeners('ollama:install-complete'); + } + + // Cancel any ongoing downloads + const downloadingModels = Object.keys(this.whisperInstallingModels); + if (downloadingModels.length > 0) { + console.log(`[ApiKeyHeader] Cancelling ${downloadingModels.length} ongoing Whisper downloads`); + downloadingModels.forEach(modelId => { + delete this.whisperInstallingModels[modelId]; + }); + } + + // Reset state + this.connectionState = 'disconnected'; + this.retryCount = 0; + + console.log('[ApiKeyHeader] Cleanup completed'); + } + + /** + * State machine-based Ollama UI rendering + */ + _renderOllamaStateUI() { + const state = this._getOllamaUIState(); + + switch (state.type) { + case 'connecting': + return this._renderConnectingState(state); + case 'install_required': + return this._renderInstallRequiredState(); + case 'start_required': + return this._renderStartRequiredState(); + case 'ready': + return this._renderReadyState(); + case 'failed': + return this._renderFailedState(state); + case 'installing': + return this._renderInstallingState(state); + default: + return this._renderUnknownState(); + } + } + + _getOllamaUIState() { + // State determination logic + if (this.connectionState === 'connecting') { + return { type: 'connecting', message: this.installingModel || 'Connecting to Ollama...' }; + } + + if (this.connectionState === 'failed') { + return { type: 'failed', message: this.errorMessage }; + } + + if (this.installingModel && this.installingModel.includes('Ollama')) { + return { type: 'installing', progress: this.installProgress }; + } + + if (!this.ollamaStatus.installed) { + return { type: 'install_required' }; + } + + if (!this.ollamaStatus.running) { + return { type: 'start_required' }; + } + + return { type: 'ready' }; + } + + _renderConnectingState(state) { + return html` +
+
+
+
+
+ ${this.installProgress}% +
+
+ `; + } + + _renderInstallRequiredState() { + return html` + + `; + } + + _renderStartRequiredState() { + return html` + + `; + } + + _renderReadyState() { + return html` + + + + ${this.getCombinedModelSuggestions().map(model => html` + + `)} + + + + ${this.renderModelStatus()} + + ${this.installingModel && !this.installingModel.includes('Ollama') ? html` +
+
+
+
+
+ ${this.installProgress}% +
+
+ ` : ''} + `; + } + + _renderFailedState(state) { + return html` +
+
+ Connection failed +
+
+ ${state.message || 'Unknown error'} +
+ +
+ `; + } + + _renderInstallingState(state) { + return html` +
+
+
+
+
+ ${state.progress}% +
+
+ `; + } + + _renderUnknownState() { + return html` +
+
+ Unknown state - Please refresh +
+
+ `; + } + + renderModelStatus() { + return ''; + } + + shouldFadeMessage(type) { + const hasMessage = type === 'error' ? this.errorMessage : this.successMessage; + return hasMessage && this.messageTimestamp > 0 && (Date.now() - this.messageTimestamp) > 100; } render() { - const isButtonDisabled = this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim(); + // Check if providers are selected and determine validation requirements + const llmNeedsApiKey = this.llmProvider !== 'ollama' && this.llmProvider !== 'whisper'; + const sttNeedsApiKey = this.sttProvider !== 'ollama' && this.sttProvider !== 'whisper'; + const llmNeedsModel = this.llmProvider === 'ollama'; + const sttNeedsModel = this.sttProvider === 'whisper'; + + // Simplified button disabled logic + const isButtonDisabled = this.isLoading || + this.installingModel || + Object.keys(this.whisperInstallingModels).length > 0 || + (llmNeedsApiKey && !this.llmApiKey.trim()) || + (sttNeedsApiKey && !this.sttApiKey.trim()) || + (llmNeedsModel && !this.selectedLlmModel?.trim()) || + (sttNeedsModel && !this.selectedSttModel); return html` -
-

Enter Your API Keys

+
+ +

Configure AI Models

-
- ${this.providers.llm.map(p => html``)} - this.llmApiKey = e.target.value} ?disabled=${this.isLoading}> + + ${this.llmProvider === 'ollama' ? this._renderOllamaStateUI() : html` + + this.llmApiKey = e.target.value} ?disabled=${this.isLoading}> + `}
-
- ${this.providers.stt.map(p => html``)} - this.sttApiKey = e.target.value} ?disabled=${this.isLoading}> + + ${this.sttProvider === 'ollama' ? html` + +
+ STT not supported by Ollama +
+ ` : this.sttProvider === 'whisper' ? html` + + + + ${Object.entries(this.whisperInstallingModels).map(([modelId, progress]) => { + if (progress !== undefined) { + return html` +
+
+
+
+
+ ${progress}% +
+
+ `; + } + return ''; + })} + ` : html` + + this.sttApiKey = e.target.value} ?disabled=${this.isLoading}> + `}
-
${this.errorMessage}
+
+ ${this.errorMessage} +
+
+ ${this.successMessage} +
or
diff --git a/src/common/ai/factory.js b/src/common/ai/factory.js index ea86ff0..520ba06 100644 --- a/src/common/ai/factory.js +++ b/src/common/ai/factory.js @@ -57,6 +57,34 @@ const PROVIDERS = { ], sttModels: [], }, + 'ollama': { + name: 'Ollama (Local)', + handler: () => require("./providers/ollama"), + llmModels: [], // Dynamic models populated from installed Ollama models + sttModels: [], // Ollama doesn't support STT yet + }, + 'whisper': { + name: 'Whisper (Local)', + handler: () => { + // Only load in main process + if (typeof window === 'undefined') { + return require("./providers/whisper"); + } + // Return dummy for renderer + return { + createSTT: () => { throw new Error('Whisper STT is only available in main process'); }, + createLLM: () => { throw new Error('Whisper does not support LLM'); }, + createStreamingLLM: () => { throw new Error('Whisper does not support LLM'); } + }; + }, + llmModels: [], + sttModels: [ + { id: 'whisper-tiny', name: 'Whisper Tiny (39M)' }, + { id: 'whisper-base', name: 'Whisper Base (74M)' }, + { id: 'whisper-small', name: 'Whisper Small (244M)' }, + { id: 'whisper-medium', name: 'Whisper Medium (769M)' }, + ], + }, }; function sanitizeModelId(model) { diff --git a/src/common/ai/providers/ollama.js b/src/common/ai/providers/ollama.js new file mode 100644 index 0000000..c25e10c --- /dev/null +++ b/src/common/ai/providers/ollama.js @@ -0,0 +1,242 @@ +const http = require('http'); +const fetch = require('node-fetch'); + +function convertMessagesToOllamaFormat(messages) { + return messages.map(msg => { + if (Array.isArray(msg.content)) { + let textContent = ''; + const images = []; + + for (const part of msg.content) { + if (part.type === 'text') { + textContent += part.text; + } else if (part.type === 'image_url') { + const base64 = part.image_url.url.replace(/^data:image\/[^;]+;base64,/, ''); + images.push(base64); + } + } + + return { + role: msg.role, + content: textContent, + ...(images.length > 0 && { images }) + }; + } else { + return msg; + } + }); +} + +function createLLM({ + model, + temperature = 0.7, + maxTokens = 2048, + baseUrl = 'http://localhost:11434', + ...config +}) { + if (!model) { + throw new Error('Model parameter is required for Ollama LLM. Please specify a model name (e.g., "llama3.2:latest", "gemma3:4b")'); + } + return { + generateContent: async (parts) => { + let systemPrompt = ''; + const userContent = []; + + for (const part of parts) { + if (typeof part === 'string') { + if (systemPrompt === '' && part.includes('You are')) { + systemPrompt = part; + } else { + userContent.push(part); + } + } else if (part.inlineData) { + userContent.push({ + type: 'image', + image: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}` + }); + } + } + + const messages = []; + if (systemPrompt) { + messages.push({ role: 'system', content: systemPrompt }); + } + messages.push({ role: 'user', content: userContent.join('\n') }); + + try { + const response = await fetch(`${baseUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + messages, + stream: false, + options: { + temperature, + num_predict: maxTokens, + } + }) + }); + + if (!response.ok) { + throw new Error(`Ollama API error: ${response.status} ${response.statusText}`); + } + + const result = await response.json(); + + return { + response: { + text: () => result.message.content + }, + raw: result + }; + } catch (error) { + console.error('Ollama LLM error:', error); + throw error; + } + }, + + chat: async (messages) => { + const ollamaMessages = convertMessagesToOllamaFormat(messages); + + try { + const response = await fetch(`${baseUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + messages: ollamaMessages, + stream: false, + options: { + temperature, + num_predict: maxTokens, + } + }) + }); + + if (!response.ok) { + throw new Error(`Ollama API error: ${response.status} ${response.statusText}`); + } + + const result = await response.json(); + + return { + content: result.message.content, + raw: result + }; + } catch (error) { + console.error('Ollama chat error:', error); + throw error; + } + } + }; +} + +function createStreamingLLM({ + model, + temperature = 0.7, + maxTokens = 2048, + baseUrl = 'http://localhost:11434', + ...config +}) { + if (!model) { + throw new Error('Model parameter is required for Ollama streaming LLM. Please specify a model name (e.g., "llama3.2:latest", "gemma3:4b")'); + } + return { + streamChat: async (messages) => { + console.log('[Ollama Provider] Starting streaming request'); + + const ollamaMessages = convertMessagesToOllamaFormat(messages); + console.log('[Ollama Provider] Converted messages for Ollama:', ollamaMessages); + + try { + const response = await fetch(`${baseUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model, + messages: ollamaMessages, + stream: true, + options: { + temperature, + num_predict: maxTokens, + } + }) + }); + + if (!response.ok) { + throw new Error(`Ollama API error: ${response.status} ${response.statusText}`); + } + + console.log('[Ollama Provider] Got streaming response'); + + const stream = new ReadableStream({ + async start(controller) { + let buffer = ''; + + try { + response.body.on('data', (chunk) => { + buffer += chunk.toString(); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (line.trim() === '') continue; + + try { + const data = JSON.parse(line); + + if (data.message?.content) { + const sseData = JSON.stringify({ + choices: [{ + delta: { + content: data.message.content + } + }] + }); + controller.enqueue(new TextEncoder().encode(`data: ${sseData}\n\n`)); + } + + if (data.done) { + controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n')); + } + } catch (e) { + console.error('[Ollama Provider] Failed to parse chunk:', e); + } + } + }); + + response.body.on('end', () => { + controller.close(); + console.log('[Ollama Provider] Streaming completed'); + }); + + response.body.on('error', (error) => { + console.error('[Ollama Provider] Streaming error:', error); + controller.error(error); + }); + + } catch (error) { + console.error('[Ollama Provider] Streaming setup error:', error); + controller.error(error); + } + } + }); + + return { + ok: true, + body: stream + }; + + } catch (error) { + console.error('[Ollama Provider] Request error:', error); + throw error; + } + } + }; +} + +module.exports = { + createLLM, + createStreamingLLM +}; \ No newline at end of file diff --git a/src/common/ai/providers/whisper.js b/src/common/ai/providers/whisper.js new file mode 100644 index 0000000..abeaa06 --- /dev/null +++ b/src/common/ai/providers/whisper.js @@ -0,0 +1,231 @@ +let spawn, path, EventEmitter; + +if (typeof window === 'undefined') { + spawn = require('child_process').spawn; + path = require('path'); + EventEmitter = require('events').EventEmitter; +} else { + class DummyEventEmitter { + on() {} + emit() {} + removeAllListeners() {} + } + EventEmitter = DummyEventEmitter; +} + +class WhisperSTTSession extends EventEmitter { + constructor(model, whisperService, sessionId) { + super(); + this.model = model; + this.whisperService = whisperService; + this.sessionId = sessionId || `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + this.process = null; + this.isRunning = false; + this.audioBuffer = Buffer.alloc(0); + this.processingInterval = null; + this.lastTranscription = ''; + } + + async initialize() { + try { + await this.whisperService.ensureModelAvailable(this.model); + this.isRunning = true; + this.startProcessingLoop(); + return true; + } catch (error) { + console.error('[WhisperSTT] Initialization error:', error); + this.emit('error', error); + return false; + } + } + + startProcessingLoop() { + this.processingInterval = setInterval(async () => { + const minBufferSize = 24000 * 2 * 0.15; + if (this.audioBuffer.length >= minBufferSize && !this.process) { + console.log(`[WhisperSTT-${this.sessionId}] Processing audio chunk, buffer size: ${this.audioBuffer.length}`); + await this.processAudioChunk(); + } + }, 1500); + } + + async processAudioChunk() { + if (!this.isRunning || this.audioBuffer.length === 0) return; + + const audioData = this.audioBuffer; + this.audioBuffer = Buffer.alloc(0); + + try { + const tempFile = await this.whisperService.saveAudioToTemp(audioData, this.sessionId); + + if (!tempFile || typeof tempFile !== 'string') { + console.error('[WhisperSTT] Invalid temp file path:', tempFile); + return; + } + + const whisperPath = await this.whisperService.getWhisperPath(); + const modelPath = await this.whisperService.getModelPath(this.model); + + if (!whisperPath || !modelPath) { + console.error('[WhisperSTT] Invalid whisper or model path:', { whisperPath, modelPath }); + return; + } + + this.process = spawn(whisperPath, [ + '-m', modelPath, + '-f', tempFile, + '--no-timestamps', + '--output-txt', + '--output-json', + '--language', 'auto', + '--threads', '4', + '--print-progress', 'false' + ]); + + let output = ''; + let errorOutput = ''; + + this.process.stdout.on('data', (data) => { + output += data.toString(); + }); + + this.process.stderr.on('data', (data) => { + errorOutput += data.toString(); + }); + + this.process.on('close', async (code) => { + this.process = null; + + if (code === 0 && output.trim()) { + const transcription = output.trim(); + if (transcription && transcription !== this.lastTranscription) { + this.lastTranscription = transcription; + console.log(`[WhisperSTT-${this.sessionId}] Transcription: "${transcription}"`); + this.emit('transcription', { + text: transcription, + timestamp: Date.now(), + confidence: 1.0, + sessionId: this.sessionId + }); + } + } else if (errorOutput) { + console.error(`[WhisperSTT-${this.sessionId}] Process error:`, errorOutput); + } + + await this.whisperService.cleanupTempFile(tempFile); + }); + + } catch (error) { + console.error('[WhisperSTT] Processing error:', error); + this.emit('error', error); + } + } + + sendRealtimeInput(audioData) { + if (!this.isRunning) { + console.warn(`[WhisperSTT-${this.sessionId}] Session not running, cannot accept audio`); + return; + } + + if (typeof audioData === 'string') { + try { + audioData = Buffer.from(audioData, 'base64'); + } catch (error) { + console.error('[WhisperSTT] Failed to decode base64 audio data:', error); + return; + } + } else if (audioData instanceof ArrayBuffer) { + audioData = Buffer.from(audioData); + } else if (!Buffer.isBuffer(audioData) && !(audioData instanceof Uint8Array)) { + console.error('[WhisperSTT] Invalid audio data type:', typeof audioData); + return; + } + + if (!Buffer.isBuffer(audioData)) { + audioData = Buffer.from(audioData); + } + + if (audioData.length > 0) { + this.audioBuffer = Buffer.concat([this.audioBuffer, audioData]); + // Log every 10th audio chunk to avoid spam + if (Math.random() < 0.1) { + console.log(`[WhisperSTT-${this.sessionId}] Received audio chunk: ${audioData.length} bytes, total buffer: ${this.audioBuffer.length} bytes`); + } + } + } + + async close() { + console.log(`[WhisperSTT-${this.sessionId}] Closing session`); + this.isRunning = false; + + if (this.processingInterval) { + clearInterval(this.processingInterval); + this.processingInterval = null; + } + + if (this.process) { + this.process.kill('SIGTERM'); + this.process = null; + } + + this.removeAllListeners(); + } +} + +class WhisperProvider { + constructor() { + this.whisperService = null; + } + + async initialize() { + if (!this.whisperService) { + const { WhisperService } = require('../../services/whisperService'); + this.whisperService = new WhisperService(); + await this.whisperService.initialize(); + } + } + + async createSTT(config) { + await this.initialize(); + + const model = config.model || 'whisper-tiny'; + const sessionType = config.sessionType || 'unknown'; + console.log(`[WhisperProvider] Creating ${sessionType} STT session with model: ${model}`); + + // Create unique session ID based on type + const sessionId = `${sessionType}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; + const session = new WhisperSTTSession(model, this.whisperService, sessionId); + + // Log session creation + console.log(`[WhisperProvider] Created session: ${sessionId}`); + + const initialized = await session.initialize(); + if (!initialized) { + throw new Error('Failed to initialize Whisper STT session'); + } + + if (config.callbacks) { + if (config.callbacks.onmessage) { + session.on('transcription', config.callbacks.onmessage); + } + if (config.callbacks.onerror) { + session.on('error', config.callbacks.onerror); + } + if (config.callbacks.onclose) { + session.on('close', config.callbacks.onclose); + } + } + + return session; + } + + async createLLM() { + throw new Error('Whisper provider does not support LLM functionality'); + } + + async createStreamingLLM() { + throw new Error('Whisper provider does not support streaming LLM functionality'); + } +} + +module.exports = new WhisperProvider(); \ No newline at end of file diff --git a/src/common/config/checksums.js b/src/common/config/checksums.js new file mode 100644 index 0000000..ff903cc --- /dev/null +++ b/src/common/config/checksums.js @@ -0,0 +1,46 @@ +const DOWNLOAD_CHECKSUMS = { + ollama: { + dmg: { + url: 'https://ollama.com/download/Ollama.dmg', + sha256: null // To be updated with actual checksum + }, + exe: { + url: 'https://ollama.com/download/OllamaSetup.exe', + sha256: null // To be updated with actual checksum + } + }, + whisper: { + models: { + 'whisper-tiny': { + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin', + sha256: 'be07e048e1e599ad46341c8d2a135645097a538221678b7acdd1b1919c6e1b21' + }, + 'whisper-base': { + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin', + sha256: '60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe' + }, + 'whisper-small': { + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin', + sha256: '1be3a9b2063867b937e64e2ec7483364a79917e157fa98c5d94b5c1fffea987b' + }, + 'whisper-medium': { + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin', + sha256: '6c14d5adee5f86394037b4e4e8b59f1673b6cee10e3cf0b11bbdbee79c156208' + } + }, + binaries: { + 'v1.7.6': { + windows: { + url: 'https://github.com/ggerganov/whisper.cpp/releases/download/v1.7.6/whisper-cpp-v1.7.6-win-x64.zip', + sha256: null // To be updated with actual checksum + }, + linux: { + url: 'https://github.com/ggerganov/whisper.cpp/releases/download/v1.7.6/whisper-cpp-v1.7.6-linux-x64.tar.gz', + sha256: null // To be updated with actual checksum + } + } + } + } +}; + +module.exports = { DOWNLOAD_CHECKSUMS }; \ No newline at end of file diff --git a/src/common/config/schema.js b/src/common/config/schema.js index 00b58f7..4dfc4ba 100644 --- a/src/common/config/schema.js +++ b/src/common/config/schema.js @@ -7,7 +7,8 @@ const LATEST_SCHEMA = { { name: 'created_at', type: 'INTEGER' }, { name: 'api_key', type: 'TEXT' }, { name: 'provider', type: 'TEXT DEFAULT \'openai\'' }, - { name: 'auto_update_enabled', type: 'INTEGER DEFAULT 1' } + { name: 'auto_update_enabled', type: 'INTEGER DEFAULT 1' }, + { name: 'has_migrated_to_firebase', type: 'INTEGER DEFAULT 0' } ] }, sessions: { @@ -72,6 +73,23 @@ const LATEST_SCHEMA = { { name: 'created_at', type: 'INTEGER' }, { name: 'sync_state', type: 'TEXT DEFAULT \'clean\'' } ] + }, + ollama_models: { + columns: [ + { name: 'name', type: 'TEXT PRIMARY KEY' }, + { name: 'size', type: 'TEXT NOT NULL' }, + { name: 'installed', type: 'INTEGER DEFAULT 0' }, + { name: 'installing', type: 'INTEGER DEFAULT 0' } + ] + }, + whisper_models: { + columns: [ + { name: 'id', type: 'TEXT PRIMARY KEY' }, + { name: 'name', type: 'TEXT NOT NULL' }, + { name: 'size', type: 'TEXT NOT NULL' }, + { name: 'installed', type: 'INTEGER DEFAULT 0' }, + { name: 'installing', type: 'INTEGER DEFAULT 0' } + ] } }; diff --git a/src/common/repositories/firestoreConverter.js b/src/common/repositories/firestoreConverter.js new file mode 100644 index 0000000..695f039 --- /dev/null +++ b/src/common/repositories/firestoreConverter.js @@ -0,0 +1,54 @@ +const encryptionService = require('../services/encryptionService'); +const { Timestamp } = require('firebase/firestore'); + +/** + * Creates a Firestore converter that automatically encrypts and decrypts specified fields. + * @param {string[]} fieldsToEncrypt - An array of field names to encrypt. + * @returns {import('@firebase/firestore').FirestoreDataConverter} A Firestore converter. + * @template T + */ +function createEncryptedConverter(fieldsToEncrypt = []) { + return { + /** + * @param {import('@firebase/firestore').DocumentData} appObject + */ + toFirestore: (appObject) => { + const firestoreData = { ...appObject }; + for (const field of fieldsToEncrypt) { + if (Object.prototype.hasOwnProperty.call(firestoreData, field) && firestoreData[field] != null) { + firestoreData[field] = encryptionService.encrypt(firestoreData[field]); + } + } + // Ensure there's a timestamp for the last modification + firestoreData.updated_at = Timestamp.now(); + return firestoreData; + }, + /** + * @param {import('@firebase/firestore').QueryDocumentSnapshot} snapshot + * @param {import('@firebase/firestore').SnapshotOptions} options + */ + fromFirestore: (snapshot, options) => { + const firestoreData = snapshot.data(options); + const appObject = { ...firestoreData, id: snapshot.id }; // include the document ID + + for (const field of fieldsToEncrypt) { + if (Object.prototype.hasOwnProperty.call(appObject, field) && appObject[field] != null) { + appObject[field] = encryptionService.decrypt(appObject[field]); + } + } + + // Convert Firestore Timestamps back to Unix timestamps (seconds) for app-wide consistency + for (const key in appObject) { + if (appObject[key] instanceof Timestamp) { + appObject[key] = appObject[key].seconds; + } + } + + return appObject; + } + }; +} + +module.exports = { + createEncryptedConverter, +}; \ No newline at end of file diff --git a/src/common/repositories/ollamaModel/index.js b/src/common/repositories/ollamaModel/index.js new file mode 100644 index 0000000..bb924b5 --- /dev/null +++ b/src/common/repositories/ollamaModel/index.js @@ -0,0 +1,20 @@ +const sqliteRepository = require('./sqlite.repository'); + +// For now, we only use SQLite repository +// In the future, we could add cloud sync support + +function getRepository() { + return sqliteRepository; +} + +// Export all repository methods +module.exports = { + getAllModels: (...args) => getRepository().getAllModels(...args), + getModel: (...args) => getRepository().getModel(...args), + upsertModel: (...args) => getRepository().upsertModel(...args), + updateInstallStatus: (...args) => getRepository().updateInstallStatus(...args), + initializeDefaultModels: (...args) => getRepository().initializeDefaultModels(...args), + deleteModel: (...args) => getRepository().deleteModel(...args), + getInstalledModels: (...args) => getRepository().getInstalledModels(...args), + getInstallingModels: (...args) => getRepository().getInstallingModels(...args) +}; \ No newline at end of file diff --git a/src/common/repositories/ollamaModel/sqlite.repository.js b/src/common/repositories/ollamaModel/sqlite.repository.js new file mode 100644 index 0000000..a7a3361 --- /dev/null +++ b/src/common/repositories/ollamaModel/sqlite.repository.js @@ -0,0 +1,137 @@ +const sqliteClient = require('../../services/sqliteClient'); + +/** + * Get all Ollama models + */ +function getAllModels() { + const db = sqliteClient.getDb(); + const query = 'SELECT * FROM ollama_models ORDER BY name'; + + try { + return db.prepare(query).all() || []; + } catch (err) { + console.error('[OllamaModel Repository] Failed to get models:', err); + throw err; + } +} + +/** + * Get a specific model by name + */ +function getModel(name) { + const db = sqliteClient.getDb(); + const query = 'SELECT * FROM ollama_models WHERE name = ?'; + + try { + return db.prepare(query).get(name); + } catch (err) { + console.error('[OllamaModel Repository] Failed to get model:', err); + throw err; + } +} + +/** + * Create or update a model entry + */ +function upsertModel({ name, size, installed = false, installing = false }) { + const db = sqliteClient.getDb(); + const query = ` + INSERT INTO ollama_models (name, size, installed, installing) + VALUES (?, ?, ?, ?) + ON CONFLICT(name) DO UPDATE SET + size = excluded.size, + installed = excluded.installed, + installing = excluded.installing + `; + + try { + db.prepare(query).run(name, size, installed ? 1 : 0, installing ? 1 : 0); + return { success: true }; + } catch (err) { + console.error('[OllamaModel Repository] Failed to upsert model:', err); + throw err; + } +} + +/** + * Update installation status for a model + */ +function updateInstallStatus(name, installed, installing = false) { + const db = sqliteClient.getDb(); + const query = 'UPDATE ollama_models SET installed = ?, installing = ? WHERE name = ?'; + + try { + const result = db.prepare(query).run(installed ? 1 : 0, installing ? 1 : 0, name); + return { success: true, changes: result.changes }; + } catch (err) { + console.error('[OllamaModel Repository] Failed to update install status:', err); + throw err; + } +} + +/** + * Initialize default models - now done dynamically based on installed models + */ +function initializeDefaultModels() { + // Default models are now detected dynamically from Ollama installation + // This function maintains compatibility but doesn't hardcode any models + console.log('[OllamaModel Repository] Default models initialization skipped - using dynamic detection'); + return { success: true }; +} + +/** + * Delete a model entry + */ +function deleteModel(name) { + const db = sqliteClient.getDb(); + const query = 'DELETE FROM ollama_models WHERE name = ?'; + + try { + const result = db.prepare(query).run(name); + return { success: true, changes: result.changes }; + } catch (err) { + console.error('[OllamaModel Repository] Failed to delete model:', err); + throw err; + } +} + +/** + * Get installed models + */ +function getInstalledModels() { + const db = sqliteClient.getDb(); + const query = 'SELECT * FROM ollama_models WHERE installed = 1 ORDER BY name'; + + try { + return db.prepare(query).all() || []; + } catch (err) { + console.error('[OllamaModel Repository] Failed to get installed models:', err); + throw err; + } +} + +/** + * Get models currently being installed + */ +function getInstallingModels() { + const db = sqliteClient.getDb(); + const query = 'SELECT * FROM ollama_models WHERE installing = 1 ORDER BY name'; + + try { + return db.prepare(query).all() || []; + } catch (err) { + console.error('[OllamaModel Repository] Failed to get installing models:', err); + throw err; + } +} + +module.exports = { + getAllModels, + getModel, + upsertModel, + updateInstallStatus, + initializeDefaultModels, + deleteModel, + getInstalledModels, + getInstallingModels +}; \ No newline at end of file diff --git a/src/common/repositories/preset/firebase.repository.js b/src/common/repositories/preset/firebase.repository.js new file mode 100644 index 0000000..c9c2f6f --- /dev/null +++ b/src/common/repositories/preset/firebase.repository.js @@ -0,0 +1,107 @@ +const { collection, doc, addDoc, getDoc, getDocs, updateDoc, deleteDoc, query, where, orderBy, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../services/firebaseClient'); +const { createEncryptedConverter } = require('../firestoreConverter'); +const encryptionService = require('../../services/encryptionService'); + +const userPresetConverter = createEncryptedConverter(['prompt', 'title']); + +const defaultPresetConverter = { + toFirestore: (data) => data, + fromFirestore: (snapshot, options) => { + const data = snapshot.data(options); + return { ...data, id: snapshot.id }; + } +}; + +function userPresetsCol() { + const db = getFirestoreInstance(); + return collection(db, 'prompt_presets').withConverter(userPresetConverter); +} + +function defaultPresetsCol() { + const db = getFirestoreInstance(); + // Path must have an odd number of segments. 'v1' is a placeholder document. + return collection(db, 'defaults/v1/prompt_presets').withConverter(defaultPresetConverter); +} + +async function getPresets(uid) { + const userPresetsQuery = query(userPresetsCol(), where('uid', '==', uid)); + const defaultPresetsQuery = query(defaultPresetsCol()); // Defaults have no owner + + const [userSnapshot, defaultSnapshot] = await Promise.all([ + getDocs(userPresetsQuery), + getDocs(defaultPresetsQuery) + ]); + + const presets = [ + ...defaultSnapshot.docs.map(d => d.data()), + ...userSnapshot.docs.map(d => d.data()) + ]; + + return presets.sort((a, b) => { + if (a.is_default && !b.is_default) return -1; + if (!a.is_default && b.is_default) return 1; + return a.title.localeCompare(b.title); + }); +} + +async function getPresetTemplates() { + const q = query(defaultPresetsCol(), orderBy('title', 'asc')); + const snapshot = await getDocs(q); + return snapshot.docs.map(doc => doc.data()); +} + +async function create({ uid, title, prompt }) { + const now = Timestamp.now(); + const newPreset = { + uid: uid, + title, + prompt, + is_default: 0, + created_at: now, + }; + const docRef = await addDoc(userPresetsCol(), newPreset); + return { id: docRef.id }; +} + +async function update(id, { title, prompt }, uid) { + const docRef = doc(userPresetsCol(), id); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists() || docSnap.data().uid !== uid || docSnap.data().is_default) { + throw new Error("Preset not found or permission denied to update."); + } + + // Encrypt sensitive fields before sending to Firestore because `updateDoc` bypasses converters. + const updates = {}; + if (title !== undefined) { + updates.title = encryptionService.encrypt(title); + } + if (prompt !== undefined) { + updates.prompt = encryptionService.encrypt(prompt); + } + updates.updated_at = Timestamp.now(); + + await updateDoc(docRef, updates); + return { changes: 1 }; +} + +async function del(id, uid) { + const docRef = doc(userPresetsCol(), id); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists() || docSnap.data().uid !== uid || docSnap.data().is_default) { + throw new Error("Preset not found or permission denied to delete."); + } + + await deleteDoc(docRef); + return { changes: 1 }; +} + +module.exports = { + getPresets, + getPresetTemplates, + create, + update, + delete: del, +}; \ No newline at end of file diff --git a/src/common/repositories/preset/index.js b/src/common/repositories/preset/index.js index ebf7f77..ac61784 100644 --- a/src/common/repositories/preset/index.js +++ b/src/common/repositories/preset/index.js @@ -1,19 +1,39 @@ const sqliteRepository = require('./sqlite.repository'); -// const firebaseRepository = require('./firebase.repository'); +const firebaseRepository = require('./firebase.repository'); const authService = require('../../../common/services/authService'); -function getRepository() { - // const user = authService.getCurrentUser(); - // if (user.isLoggedIn) { - // return firebaseRepository; - // } +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } return sqliteRepository; } -module.exports = { - getPresets: (...args) => getRepository().getPresets(...args), - getPresetTemplates: (...args) => getRepository().getPresetTemplates(...args), - create: (...args) => getRepository().create(...args), - update: (...args) => getRepository().update(...args), - delete: (...args) => getRepository().delete(...args), -}; \ No newline at end of file +const presetRepositoryAdapter = { + getPresets: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getPresets(uid); + }, + + getPresetTemplates: () => { + return getBaseRepository().getPresetTemplates(); + }, + + create: (options) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().create({ uid, ...options }); + }, + + update: (id, options) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().update(id, options, uid); + }, + + delete: (id) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().delete(id, uid); + }, +}; + +module.exports = presetRepositoryAdapter; \ No newline at end of file diff --git a/src/common/repositories/session/firebase.repository.js b/src/common/repositories/session/firebase.repository.js new file mode 100644 index 0000000..632d3ad --- /dev/null +++ b/src/common/repositories/session/firebase.repository.js @@ -0,0 +1,161 @@ +const { doc, getDoc, collection, addDoc, query, where, getDocs, writeBatch, orderBy, limit, updateDoc, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../services/firebaseClient'); +const { createEncryptedConverter } = require('../firestoreConverter'); +const encryptionService = require('../../services/encryptionService'); + +const sessionConverter = createEncryptedConverter(['title']); + +function sessionsCol() { + const db = getFirestoreInstance(); + return collection(db, 'sessions').withConverter(sessionConverter); +} + +// Sub-collection references are now built from the top-level +function subCollections(sessionId) { + const db = getFirestoreInstance(); + const sessionPath = `sessions/${sessionId}`; + return { + transcripts: collection(db, `${sessionPath}/transcripts`), + ai_messages: collection(db, `${sessionPath}/ai_messages`), + summary: collection(db, `${sessionPath}/summary`), + } +} + +async function getById(id) { + const docRef = doc(sessionsCol(), id); + const docSnap = await getDoc(docRef); + return docSnap.exists() ? docSnap.data() : null; +} + +async function create(uid, type = 'ask') { + const now = Timestamp.now(); + const newSession = { + uid: uid, + members: [uid], // For future sharing functionality + title: `Session @ ${new Date().toLocaleTimeString()}`, + session_type: type, + started_at: now, + updated_at: now, + ended_at: null, + }; + const docRef = await addDoc(sessionsCol(), newSession); + console.log(`Firebase: Created session ${docRef.id} for user ${uid}`); + return docRef.id; +} + +async function getAllByUserId(uid) { + const q = query(sessionsCol(), where('members', 'array-contains', uid), orderBy('started_at', 'desc')); + const querySnapshot = await getDocs(q); + return querySnapshot.docs.map(doc => doc.data()); +} + +async function updateTitle(id, title) { + const docRef = doc(sessionsCol(), id); + await updateDoc(docRef, { + title: encryptionService.encrypt(title), + updated_at: Timestamp.now() + }); + return { changes: 1 }; +} + +async function deleteWithRelatedData(id) { + const db = getFirestoreInstance(); + const batch = writeBatch(db); + + const { transcripts, ai_messages, summary } = subCollections(id); + const [transcriptsSnap, aiMessagesSnap, summarySnap] = await Promise.all([ + getDocs(query(transcripts)), + getDocs(query(ai_messages)), + getDocs(query(summary)), + ]); + + transcriptsSnap.forEach(d => batch.delete(d.ref)); + aiMessagesSnap.forEach(d => batch.delete(d.ref)); + summarySnap.forEach(d => batch.delete(d.ref)); + + const sessionRef = doc(sessionsCol(), id); + batch.delete(sessionRef); + + await batch.commit(); + return { success: true }; +} + +async function end(id) { + const docRef = doc(sessionsCol(), id); + await updateDoc(docRef, { ended_at: Timestamp.now() }); + return { changes: 1 }; +} + +async function updateType(id, type) { + const docRef = doc(sessionsCol(), id); + await updateDoc(docRef, { session_type: type }); + return { changes: 1 }; +} + +async function touch(id) { + const docRef = doc(sessionsCol(), id); + await updateDoc(docRef, { updated_at: Timestamp.now() }); + return { changes: 1 }; +} + +async function getOrCreateActive(uid, requestedType = 'ask') { + const findQuery = query( + sessionsCol(), + where('uid', '==', uid), + where('ended_at', '==', null), + orderBy('session_type', 'desc'), + limit(1) + ); + + const activeSessionSnap = await getDocs(findQuery); + + if (!activeSessionSnap.empty) { + const activeSessionDoc = activeSessionSnap.docs[0]; + const sessionRef = doc(sessionsCol(), activeSessionDoc.id); + const activeSession = activeSessionDoc.data(); + + console.log(`[Repo] Found active Firebase session ${activeSession.id}`); + + const updates = { updated_at: Timestamp.now() }; + if (activeSession.session_type === 'ask' && requestedType === 'listen') { + updates.session_type = 'listen'; + console.log(`[Repo] Promoted Firebase session ${activeSession.id} to 'listen' type.`); + } + + await updateDoc(sessionRef, updates); + return activeSessionDoc.id; + } else { + console.log(`[Repo] No active Firebase session for user ${uid}. Creating new.`); + return create(uid, requestedType); + } +} + +async function endAllActiveSessions(uid) { + const q = query(sessionsCol(), where('uid', '==', uid), where('ended_at', '==', null)); + const snapshot = await getDocs(q); + + if (snapshot.empty) return { changes: 0 }; + + const batch = writeBatch(getFirestoreInstance()); + const now = Timestamp.now(); + snapshot.forEach(d => { + batch.update(d.ref, { ended_at: now }); + }); + await batch.commit(); + + console.log(`[Repo] Ended ${snapshot.size} active session(s) for user ${uid}.`); + return { changes: snapshot.size }; +} + +module.exports = { + getById, + create, + getAllByUserId, + updateTitle, + deleteWithRelatedData, + end, + updateType, + touch, + getOrCreateActive, + endAllActiveSessions, +}; \ No newline at end of file diff --git a/src/common/repositories/session/index.js b/src/common/repositories/session/index.js index 42e7ec0..da91036 100644 --- a/src/common/repositories/session/index.js +++ b/src/common/repositories/session/index.js @@ -1,26 +1,60 @@ const sqliteRepository = require('./sqlite.repository'); -// const firebaseRepository = require('./firebase.repository'); // Future implementation -const authService = require('../../../common/services/authService'); +const firebaseRepository = require('./firebase.repository'); -function getRepository() { - // In the future, we can check the user's login status from authService - // const user = authService.getCurrentUser(); - // if (user.isLoggedIn) { - // return firebaseRepository; - // } +let authService = null; + +function setAuthService(service) { + authService = service; +} + +function getBaseRepository() { + if (!authService) { + // Fallback or error if authService is not set, to prevent crashes. + // During initial load, it might not be set, so we default to sqlite. + return sqliteRepository; + } + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } return sqliteRepository; } -// Directly export functions for ease of use, decided by the strategy -module.exports = { - getById: (...args) => getRepository().getById(...args), - create: (...args) => getRepository().create(...args), - getAllByUserId: (...args) => getRepository().getAllByUserId(...args), - updateTitle: (...args) => getRepository().updateTitle(...args), - deleteWithRelatedData: (...args) => getRepository().deleteWithRelatedData(...args), - end: (...args) => getRepository().end(...args), - updateType: (...args) => getRepository().updateType(...args), - touch: (...args) => getRepository().touch(...args), - getOrCreateActive: (...args) => getRepository().getOrCreateActive(...args), - endAllActiveSessions: (...args) => getRepository().endAllActiveSessions(...args), -}; \ No newline at end of file +// The adapter layer that injects the UID +const sessionRepositoryAdapter = { + setAuthService, // Expose the setter + + getById: (id) => getBaseRepository().getById(id), + + create: (type = 'ask') => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().create(uid, type); + }, + + getAllByUserId: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getAllByUserId(uid); + }, + + updateTitle: (id, title) => getBaseRepository().updateTitle(id, title), + + deleteWithRelatedData: (id) => getBaseRepository().deleteWithRelatedData(id), + + end: (id) => getBaseRepository().end(id), + + updateType: (id, type) => getBaseRepository().updateType(id, type), + + touch: (id) => getBaseRepository().touch(id), + + getOrCreateActive: (requestedType = 'ask') => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getOrCreateActive(uid, requestedType); + }, + + endAllActiveSessions: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().endAllActiveSessions(uid); + }, +}; + +module.exports = sessionRepositoryAdapter; \ No newline at end of file diff --git a/src/common/repositories/session/sqlite.repository.js b/src/common/repositories/session/sqlite.repository.js index 509ba98..c3c9f9c 100644 --- a/src/common/repositories/session/sqlite.repository.js +++ b/src/common/repositories/session/sqlite.repository.js @@ -108,14 +108,15 @@ function getOrCreateActive(uid, requestedType = 'ask') { } } -function endAllActiveSessions() { +function endAllActiveSessions(uid) { const db = sqliteClient.getDb(); const now = Math.floor(Date.now() / 1000); - const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL`; + // Filter by uid to match the Firebase repository's behavior. + const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL AND uid = ?`; try { - const result = db.prepare(query).run(now, now); - console.log(`[Repo] Ended ${result.changes} active session(s).`); + const result = db.prepare(query).run(now, now, uid); + console.log(`[Repo] Ended ${result.changes} active SQLite session(s) for user ${uid}.`); return { changes: result.changes }; } catch (err) { console.error('SQLite: Failed to end all active sessions:', err); diff --git a/src/common/repositories/user/firebase.repository.js b/src/common/repositories/user/firebase.repository.js new file mode 100644 index 0000000..3867108 --- /dev/null +++ b/src/common/repositories/user/firebase.repository.js @@ -0,0 +1,91 @@ +const { doc, getDoc, setDoc, deleteDoc, writeBatch, query, where, getDocs, collection, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../services/firebaseClient'); +const { createEncryptedConverter } = require('../firestoreConverter'); +const encryptionService = require('../../services/encryptionService'); + +const userConverter = createEncryptedConverter(['api_key']); + +function usersCol() { + const db = getFirestoreInstance(); + return collection(db, 'users').withConverter(userConverter); +} + +// These functions are mostly correct as they already operate on a top-level collection. +// We just need to ensure the signatures are consistent. + +async function findOrCreate(user) { + if (!user || !user.uid) throw new Error('User object and uid are required'); + const { uid, displayName, email } = user; + const now = Timestamp.now(); + const docRef = doc(usersCol(), uid); + const docSnap = await getDoc(docRef); + + if (docSnap.exists()) { + await setDoc(docRef, { + display_name: displayName || docSnap.data().display_name || 'User', + email: email || docSnap.data().email || 'no-email@example.com' + }, { merge: true }); + } else { + await setDoc(docRef, { uid, display_name: displayName || 'User', email: email || 'no-email@example.com', created_at: now }); + } + const finalDoc = await getDoc(docRef); + return finalDoc.data(); +} + +async function getById(uid) { + const docRef = doc(usersCol(), uid); + const docSnap = await getDoc(docRef); + return docSnap.exists() ? docSnap.data() : null; +} + +async function saveApiKey(uid, apiKey, provider = 'openai') { + const docRef = doc(usersCol(), uid); + await setDoc(docRef, { api_key: apiKey, provider }, { merge: true }); + return { changes: 1 }; +} + +async function update({ uid, displayName }) { + const docRef = doc(usersCol(), uid); + await setDoc(docRef, { display_name: displayName }, { merge: true }); + return { changes: 1 }; +} + +async function deleteById(uid) { + const db = getFirestoreInstance(); + const batch = writeBatch(db); + + // 1. Delete all sessions owned by the user + const sessionsQuery = query(collection(db, 'sessions'), where('uid', '==', uid)); + const sessionsSnapshot = await getDocs(sessionsQuery); + + for (const sessionDoc of sessionsSnapshot.docs) { + // Recursively delete sub-collections + const subcollectionsToDelete = ['transcripts', 'ai_messages', 'summary']; + for (const sub of subcollectionsToDelete) { + const subColPath = `sessions/${sessionDoc.id}/${sub}`; + const subSnapshot = await getDocs(query(collection(db, subColPath))); + subSnapshot.forEach(d => batch.delete(d.ref)); + } + batch.delete(sessionDoc.ref); + } + + // 2. Delete all presets owned by the user + const presetsQuery = query(collection(db, 'prompt_presets'), where('uid', '==', uid)); + const presetsSnapshot = await getDocs(presetsQuery); + presetsSnapshot.forEach(doc => batch.delete(doc.ref)); + + // 3. Delete the user document itself + const userRef = doc(usersCol(), uid); + batch.delete(userRef); + + await batch.commit(); + return { success: true }; +} + +module.exports = { + findOrCreate, + getById, + saveApiKey, + update, + deleteById, +}; \ No newline at end of file diff --git a/src/common/repositories/user/index.js b/src/common/repositories/user/index.js index 9455ef8..6c90a87 100644 --- a/src/common/repositories/user/index.js +++ b/src/common/repositories/user/index.js @@ -1,19 +1,40 @@ const sqliteRepository = require('./sqlite.repository'); -// const firebaseRepository = require('./firebase.repository'); -const authService = require('../../../common/services/authService'); +const firebaseRepository = require('./firebase.repository'); +const authService = require('../../services/authService'); -function getRepository() { - // const user = authService.getCurrentUser(); - // if (user.isLoggedIn) { - // return firebaseRepository; - // } +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } return sqliteRepository; } -module.exports = { - findOrCreate: (...args) => getRepository().findOrCreate(...args), - getById: (...args) => getRepository().getById(...args), - saveApiKey: (...args) => getRepository().saveApiKey(...args), - update: (...args) => getRepository().update(...args), - deleteById: (...args) => getRepository().deleteById(...args), -}; \ No newline at end of file +const userRepositoryAdapter = { + findOrCreate: (user) => { + // This function receives the full user object, which includes the uid. No need to inject. + return getBaseRepository().findOrCreate(user); + }, + + getById: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getById(uid); + }, + + saveApiKey: (apiKey, provider) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().saveApiKey(uid, apiKey, provider); + }, + + update: (updateData) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().update({ uid, ...updateData }); + }, + + deleteById: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().deleteById(uid); + } +}; + +module.exports = userRepositoryAdapter; \ No newline at end of file diff --git a/src/common/repositories/user/sqlite.repository.js b/src/common/repositories/user/sqlite.repository.js index 7185c81..d443f47 100644 --- a/src/common/repositories/user/sqlite.repository.js +++ b/src/common/repositories/user/sqlite.repository.js @@ -40,7 +40,7 @@ function getById(uid) { return db.prepare('SELECT * FROM users WHERE uid = ?').get(uid); } -function saveApiKey(apiKey, uid, provider = 'openai') { +function saveApiKey(uid, apiKey, provider = 'openai') { const db = sqliteClient.getDb(); try { const result = db.prepare('UPDATE users SET api_key = ?, provider = ? WHERE uid = ?').run(apiKey, provider, uid); @@ -58,6 +58,16 @@ function update({ uid, displayName }) { return { changes: result.changes }; } +function setMigrationComplete(uid) { + const db = sqliteClient.getDb(); + const stmt = db.prepare('UPDATE users SET has_migrated_to_firebase = 1 WHERE uid = ?'); + const result = stmt.run(uid); + if (result.changes > 0) { + console.log(`[Repo] Marked migration as complete for user ${uid}.`); + } + return result; +} + function deleteById(uid) { const db = sqliteClient.getDb(); const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(uid); @@ -88,5 +98,6 @@ module.exports = { getById, saveApiKey, update, + setMigrationComplete, deleteById }; \ No newline at end of file diff --git a/src/common/repositories/whisperModel/index.js b/src/common/repositories/whisperModel/index.js new file mode 100644 index 0000000..d796e24 --- /dev/null +++ b/src/common/repositories/whisperModel/index.js @@ -0,0 +1,53 @@ +const BaseModelRepository = require('../baseModel'); + +class WhisperModelRepository extends BaseModelRepository { + constructor(db, tableName = 'whisper_models') { + super(db, tableName); + } + + async initializeModels(availableModels) { + const existingModels = await this.getAll(); + const existingIds = new Set(existingModels.map(m => m.id)); + + for (const [modelId, modelInfo] of Object.entries(availableModels)) { + if (!existingIds.has(modelId)) { + await this.create({ + id: modelId, + name: modelInfo.name, + size: modelInfo.size, + installed: 0, + installing: 0 + }); + } + } + } + + async getInstalledModels() { + return this.findAll({ installed: 1 }); + } + + async setInstalled(modelId, installed = true) { + return this.update({ id: modelId }, { + installed: installed ? 1 : 0, + installing: 0 + }); + } + + async setInstalling(modelId, installing = true) { + return this.update({ id: modelId }, { + installing: installing ? 1 : 0 + }); + } + + async isInstalled(modelId) { + const model = await this.findOne({ id: modelId }); + return model && model.installed === 1; + } + + async isInstalling(modelId) { + const model = await this.findOne({ id: modelId }); + return model && model.installing === 1; + } +} + +module.exports = WhisperModelRepository; \ No newline at end of file diff --git a/src/common/services/authService.js b/src/common/services/authService.js index fd4b9de..55862ff 100644 --- a/src/common/services/authService.js +++ b/src/common/services/authService.js @@ -1,8 +1,10 @@ const { onAuthStateChanged, signInWithCustomToken, signOut } = require('firebase/auth'); const { BrowserWindow } = require('electron'); const { getFirebaseAuth } = require('./firebaseClient'); -const userRepository = require('../repositories/user'); const fetch = require('node-fetch'); +const encryptionService = require('./encryptionService'); +const migrationService = require('./migrationService'); +const sessionRepository = require('../repositories/session'); async function getVirtualKeyByEmail(email, idToken) { if (!idToken) { @@ -37,12 +39,22 @@ class AuthService { this.currentUserMode = 'local'; // 'local' or 'firebase' this.currentUser = null; this.isInitialized = false; + + // Initialize immediately for the default local user on startup. + // This ensures the key is ready before any login/logout state change. + encryptionService.initializeKey(this.currentUserId); this.initializationPromise = null; } initialize() { if (this.isInitialized) return this.initializationPromise; + // --- Break the circular dependency --- + // Inject this authService instance into the session repository so it can be used + // without a direct `require` cycle. + sessionRepository.setAuthService(this); + // --- End of dependency injection --- + this.initializationPromise = new Promise((resolve) => { const auth = getFirebaseAuth(); onAuthStateChanged(auth, async (user) => { @@ -55,6 +67,17 @@ class AuthService { this.currentUserId = user.uid; this.currentUserMode = 'firebase'; + // Clean up any zombie sessions from a previous run for this user. + await sessionRepository.endAllActiveSessions(); + + // ** Initialize encryption key for the logged-in user ** + await encryptionService.initializeKey(user.uid); + + // ** Check for and run data migration for the user ** + // No 'await' here, so it runs in the background without blocking startup. + migrationService.checkAndRunMigration(user); + + // Start background task to fetch and save virtual key (async () => { try { @@ -83,6 +106,12 @@ class AuthService { this.currentUser = null; this.currentUserId = 'default_user'; this.currentUserMode = 'local'; + + // End active sessions for the local/default user as well. + await sessionRepository.endAllActiveSessions(); + + // ** Initialize encryption key for the default/local user ** + await encryptionService.initializeKey(this.currentUserId); } this.broadcastUserState(); @@ -112,6 +141,9 @@ class AuthService { async signOut() { const auth = getFirebaseAuth(); try { + // End all active sessions for the current user BEFORE signing out. + await sessionRepository.endAllActiveSessions(); + await signOut(auth); console.log('[AuthService] User sign-out initiated successfully.'); // onAuthStateChanged will handle the state update and broadcast, diff --git a/src/common/services/cryptoService.js b/src/common/services/cryptoService.js new file mode 100644 index 0000000..79935e4 --- /dev/null +++ b/src/common/services/cryptoService.js @@ -0,0 +1,89 @@ +const crypto = require('crypto'); +const { app } = require('electron'); +const os = require('os'); + +class CryptoService { + constructor() { + this.algorithm = 'aes-256-gcm'; + this.saltLength = 32; + this.tagLength = 16; + this.ivLength = 16; + this.iterations = 100000; + this.keyLength = 32; + this._derivedKey = null; + } + + _getMachineId() { + const machineInfo = `${os.hostname()}-${os.platform()}-${os.arch()}`; + const appPath = app.getPath('userData'); + return crypto.createHash('sha256').update(machineInfo + appPath).digest('hex'); + } + + _deriveKey() { + if (this._derivedKey) return this._derivedKey; + + const machineId = this._getMachineId(); + const salt = crypto.createHash('sha256').update('pickle-glass-salt').digest(); + this._derivedKey = crypto.pbkdf2Sync(machineId, salt, this.iterations, this.keyLength, 'sha256'); + return this._derivedKey; + } + + encrypt(text) { + if (!text) return null; + + try { + const iv = crypto.randomBytes(this.ivLength); + const salt = crypto.randomBytes(this.saltLength); + const key = this._deriveKey(); + + const cipher = crypto.createCipheriv(this.algorithm, key, iv); + + const encrypted = Buffer.concat([ + cipher.update(text, 'utf8'), + cipher.final() + ]); + + const tag = cipher.getAuthTag(); + + const combined = Buffer.concat([salt, iv, tag, encrypted]); + return combined.toString('base64'); + } catch (error) { + console.error('[CryptoService] Encryption failed:', error.message); + throw new Error('Encryption failed'); + } + } + + decrypt(encryptedData) { + if (!encryptedData) return null; + + try { + const combined = Buffer.from(encryptedData, 'base64'); + + const salt = combined.slice(0, this.saltLength); + const iv = combined.slice(this.saltLength, this.saltLength + this.ivLength); + const tag = combined.slice(this.saltLength + this.ivLength, this.saltLength + this.ivLength + this.tagLength); + const encrypted = combined.slice(this.saltLength + this.ivLength + this.tagLength); + + const key = this._deriveKey(); + + const decipher = crypto.createDecipheriv(this.algorithm, key, iv); + decipher.setAuthTag(tag); + + const decrypted = Buffer.concat([ + decipher.update(encrypted), + decipher.final() + ]); + + return decrypted.toString('utf8'); + } catch (error) { + console.error('[CryptoService] Decryption failed:', error.message); + throw new Error('Decryption failed'); + } + } + + clearCache() { + this._derivedKey = null; + } +} + +module.exports = new CryptoService(); \ No newline at end of file diff --git a/src/common/services/encryptionService.js b/src/common/services/encryptionService.js new file mode 100644 index 0000000..7b3d419 --- /dev/null +++ b/src/common/services/encryptionService.js @@ -0,0 +1,140 @@ +const crypto = require('crypto'); +let keytar; + +// Dynamically import keytar, as it's an optional dependency. +try { + keytar = require('keytar'); +} catch (error) { + console.warn('[EncryptionService] keytar is not available. Will use in-memory key for this session. Restarting the app might be required for data persistence after login.'); + keytar = null; +} + +const SERVICE_NAME = 'com.pickle.glass'; // A unique identifier for the app in the keychain +let sessionKey = null; // In-memory fallback key + +const ALGORITHM = 'aes-256-gcm'; +const IV_LENGTH = 16; // For AES, this is always 16 +const AUTH_TAG_LENGTH = 16; + + +/** + * Initializes the encryption key for a given user. + * It first tries to get the key from the OS keychain. + * If that fails, it generates a new key. + * If keytar is available, it saves the new key. + * Otherwise, it uses an in-memory key for the session. + * + * @param {string} userId - The unique identifier for the user (e.g., Firebase UID). + */ +async function initializeKey(userId) { + if (!userId) { + throw new Error('A user ID must be provided to initialize the encryption key.'); + } + + if (keytar) { + try { + let key = await keytar.getPassword(SERVICE_NAME, userId); + if (!key) { + console.log(`[EncryptionService] No key found for ${userId}. Creating a new one.`); + key = crypto.randomBytes(32).toString('hex'); + await keytar.setPassword(SERVICE_NAME, userId, key); + console.log(`[EncryptionService] New key securely stored in keychain for ${userId}.`); + } else { + console.log(`[EncryptionService] Encryption key successfully retrieved from keychain for ${userId}.`); + } + sessionKey = key; + } catch (error) { + console.error('[EncryptionService] keytar failed. Falling back to in-memory key for this session.', error); + keytar = null; // Disable keytar for the rest of the session to avoid repeated errors + sessionKey = crypto.randomBytes(32).toString('hex'); + } + } else { + // keytar is not available + if (!sessionKey) { + console.warn('[EncryptionService] Using in-memory session key. Data will not persist across restarts without keytar.'); + sessionKey = crypto.randomBytes(32).toString('hex'); + } + } + + if (!sessionKey) { + throw new Error('Failed to initialize encryption key.'); + } +} + +/** + * Encrypts a given text using AES-256-GCM. + * @param {string} text The text to encrypt. + * @returns {string | null} The encrypted data, as a base64 string containing iv, authTag, and content, or the original value if it cannot be encrypted. + */ +function encrypt(text) { + if (!sessionKey) { + console.error('[EncryptionService] Encryption key is not initialized. Cannot encrypt.'); + return text; // Return original if key is missing + } + if (text == null) { // checks for null or undefined + return text; + } + + try { + const key = Buffer.from(sessionKey, 'hex'); + const iv = crypto.randomBytes(IV_LENGTH); + const cipher = crypto.createCipheriv(ALGORITHM, key, iv); + + let encrypted = cipher.update(String(text), 'utf8', 'hex'); + encrypted += cipher.final('hex'); + + const authTag = cipher.getAuthTag(); + + // Prepend IV and AuthTag to the encrypted content, then encode as base64. + return Buffer.concat([iv, authTag, Buffer.from(encrypted, 'hex')]).toString('base64'); + } catch (error) { + console.error('[EncryptionService] Encryption failed:', error); + return text; // Return original on error + } +} + +/** + * Decrypts a given encrypted string. + * @param {string} encryptedText The base64 encrypted text. + * @returns {string | null} The decrypted text, or the original value if it cannot be decrypted. + */ +function decrypt(encryptedText) { + if (!sessionKey) { + console.error('[EncryptionService] Encryption key is not initialized. Cannot decrypt.'); + return encryptedText; // Return original if key is missing + } + if (encryptedText == null || typeof encryptedText !== 'string') { + return encryptedText; + } + + try { + const data = Buffer.from(encryptedText, 'base64'); + if (data.length < IV_LENGTH + AUTH_TAG_LENGTH) { + // This is not a valid encrypted string, likely plain text. + return encryptedText; + } + + const key = Buffer.from(sessionKey, 'hex'); + const iv = data.slice(0, IV_LENGTH); + const authTag = data.slice(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH); + const encryptedContent = data.slice(IV_LENGTH + AUTH_TAG_LENGTH); + + const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); + decipher.setAuthTag(authTag); + + let decrypted = decipher.update(encryptedContent, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + + return decrypted; + } catch (error) { + // It's common for this to fail if the data is not encrypted (e.g., legacy data). + // In that case, we return the original value. + return encryptedText; + } +} + +module.exports = { + initializeKey, + encrypt, + decrypt, +}; \ No newline at end of file diff --git a/src/common/services/firebaseClient.js b/src/common/services/firebaseClient.js index 5ccb6d5..99ca063 100644 --- a/src/common/services/firebaseClient.js +++ b/src/common/services/firebaseClient.js @@ -1,6 +1,9 @@ const { initializeApp } = require('firebase/app'); const { initializeAuth } = require('firebase/auth'); const Store = require('electron-store'); +const { getFirestore, setLogLevel } = require('firebase/firestore'); + +// setLogLevel('debug'); /** * Firebase Auth expects the `persistence` option passed to `initializeAuth()` to be *classes*, @@ -66,6 +69,7 @@ const firebaseConfig = { let firebaseApp = null; let firebaseAuth = null; +let firestoreInstance = null; // To hold the specific DB instance function initializeFirebase() { if (firebaseApp) { @@ -84,7 +88,11 @@ function initializeFirebase() { persistence: [ElectronStorePersistence], }); + // Initialize Firestore with the specific database ID + firestoreInstance = getFirestore(firebaseApp, 'pickle-glass'); + console.log('[FirebaseClient] Firebase initialized successfully with class-based electron-store persistence.'); + console.log('[FirebaseClient] Firestore instance is targeting the "pickle-glass" database.'); } catch (error) { console.error('[FirebaseClient] Firebase initialization failed:', error); } @@ -97,7 +105,15 @@ function getFirebaseAuth() { return firebaseAuth; } +function getFirestoreInstance() { + if (!firestoreInstance) { + throw new Error("Firestore has not been initialized. Call initializeFirebase() first."); + } + return firestoreInstance; +} + module.exports = { initializeFirebase, getFirebaseAuth, + getFirestoreInstance, }; \ No newline at end of file diff --git a/src/common/services/localAIServiceBase.js b/src/common/services/localAIServiceBase.js new file mode 100644 index 0000000..45db41b --- /dev/null +++ b/src/common/services/localAIServiceBase.js @@ -0,0 +1,277 @@ +const { exec } = require('child_process'); +const { promisify } = require('util'); +const { EventEmitter } = require('events'); +const path = require('path'); +const os = require('os'); +const https = require('https'); +const fs = require('fs'); +const crypto = require('crypto'); + +const execAsync = promisify(exec); + +class LocalAIServiceBase extends EventEmitter { + constructor(serviceName) { + super(); + this.serviceName = serviceName; + this.baseUrl = null; + this.installationProgress = new Map(); + } + + getPlatform() { + return process.platform; + } + + async checkCommand(command) { + try { + const platform = this.getPlatform(); + const checkCmd = platform === 'win32' ? 'where' : 'which'; + const { stdout } = await execAsync(`${checkCmd} ${command}`); + return stdout.trim(); + } catch (error) { + return null; + } + } + + async isInstalled() { + throw new Error('isInstalled() must be implemented by subclass'); + } + + async isServiceRunning() { + throw new Error('isServiceRunning() must be implemented by subclass'); + } + + async startService() { + throw new Error('startService() must be implemented by subclass'); + } + + async stopService() { + throw new Error('stopService() must be implemented by subclass'); + } + + async waitForService(checkFn, maxAttempts = 30, delayMs = 1000) { + for (let i = 0; i < maxAttempts; i++) { + if (await checkFn()) { + console.log(`[${this.serviceName}] Service is ready`); + return true; + } + await new Promise(resolve => setTimeout(resolve, delayMs)); + } + throw new Error(`${this.serviceName} service failed to start within timeout`); + } + + getInstallProgress(modelName) { + return this.installationProgress.get(modelName) || 0; + } + + setInstallProgress(modelName, progress) { + this.installationProgress.set(modelName, progress); + this.emit('install-progress', { model: modelName, progress }); + } + + clearInstallProgress(modelName) { + this.installationProgress.delete(modelName); + } + + async autoInstall(onProgress) { + const platform = this.getPlatform(); + console.log(`[${this.serviceName}] Starting auto-installation for ${platform}`); + + try { + switch(platform) { + case 'darwin': + return await this.installMacOS(onProgress); + case 'win32': + return await this.installWindows(onProgress); + case 'linux': + return await this.installLinux(); + default: + throw new Error(`Unsupported platform: ${platform}`); + } + } catch (error) { + console.error(`[${this.serviceName}] Auto-installation failed:`, error); + throw error; + } + } + + async installMacOS() { + throw new Error('installMacOS() must be implemented by subclass'); + } + + async installWindows() { + throw new Error('installWindows() must be implemented by subclass'); + } + + async installLinux() { + throw new Error('installLinux() must be implemented by subclass'); + } + + // parseProgress method removed - using proper REST API now + + async shutdown(force = false) { + console.log(`[${this.serviceName}] Starting ${force ? 'forced' : 'graceful'} shutdown...`); + + const isRunning = await this.isServiceRunning(); + if (!isRunning) { + console.log(`[${this.serviceName}] Service not running, nothing to shutdown`); + return true; + } + + const platform = this.getPlatform(); + + try { + switch(platform) { + case 'darwin': + return await this.shutdownMacOS(force); + case 'win32': + return await this.shutdownWindows(force); + case 'linux': + return await this.shutdownLinux(force); + default: + console.warn(`[${this.serviceName}] Unsupported platform for shutdown: ${platform}`); + return false; + } + } catch (error) { + console.error(`[${this.serviceName}] Error during shutdown:`, error); + return false; + } + } + + async shutdownMacOS(force) { + throw new Error('shutdownMacOS() must be implemented by subclass'); + } + + async shutdownWindows(force) { + throw new Error('shutdownWindows() must be implemented by subclass'); + } + + async shutdownLinux(force) { + throw new Error('shutdownLinux() must be implemented by subclass'); + } + + async downloadFile(url, destination, options = {}) { + const { + onProgress = null, + headers = { 'User-Agent': 'Glass-App' }, + timeout = 300000 // 5 minutes default + } = options; + + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(destination); + let downloadedSize = 0; + let totalSize = 0; + + const request = https.get(url, { headers }, (response) => { + // Handle redirects (301, 302, 307, 308) + if ([301, 302, 307, 308].includes(response.statusCode)) { + file.close(); + fs.unlink(destination, () => {}); + + if (!response.headers.location) { + reject(new Error('Redirect without location header')); + return; + } + + console.log(`[${this.serviceName}] Following redirect from ${url} to ${response.headers.location}`); + this.downloadFile(response.headers.location, destination, options) + .then(resolve) + .catch(reject); + return; + } + + if (response.statusCode !== 200) { + file.close(); + fs.unlink(destination, () => {}); + reject(new Error(`Download failed: ${response.statusCode} ${response.statusMessage}`)); + return; + } + + totalSize = parseInt(response.headers['content-length'], 10) || 0; + + response.on('data', (chunk) => { + downloadedSize += chunk.length; + + if (onProgress && totalSize > 0) { + const progress = Math.round((downloadedSize / totalSize) * 100); + onProgress(progress, downloadedSize, totalSize); + } + }); + + response.pipe(file); + + file.on('finish', () => { + file.close(() => { + this.emit('download-complete', { url, destination, size: downloadedSize }); + resolve({ success: true, size: downloadedSize }); + }); + }); + }); + + request.on('timeout', () => { + request.destroy(); + file.close(); + fs.unlink(destination, () => {}); + reject(new Error('Download timeout')); + }); + + request.on('error', (err) => { + file.close(); + fs.unlink(destination, () => {}); + this.emit('download-error', { url, error: err }); + reject(err); + }); + + request.setTimeout(timeout); + + file.on('error', (err) => { + fs.unlink(destination, () => {}); + reject(err); + }); + }); + } + + async downloadWithRetry(url, destination, options = {}) { + const { maxRetries = 3, retryDelay = 1000, expectedChecksum = null, ...downloadOptions } = options; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const result = await this.downloadFile(url, destination, downloadOptions); + + if (expectedChecksum) { + const isValid = await this.verifyChecksum(destination, expectedChecksum); + if (!isValid) { + fs.unlinkSync(destination); + throw new Error('Checksum verification failed'); + } + console.log(`[${this.serviceName}] Checksum verified successfully`); + } + + return result; + } catch (error) { + if (attempt === maxRetries) { + throw error; + } + + console.log(`Download attempt ${attempt} failed, retrying in ${retryDelay}ms...`); + await new Promise(resolve => setTimeout(resolve, retryDelay * attempt)); + } + } + } + + async verifyChecksum(filePath, expectedChecksum) { + return new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + const stream = fs.createReadStream(filePath); + + stream.on('data', (data) => hash.update(data)); + stream.on('end', () => { + const fileChecksum = hash.digest('hex'); + console.log(`[${this.serviceName}] File checksum: ${fileChecksum}`); + console.log(`[${this.serviceName}] Expected checksum: ${expectedChecksum}`); + resolve(fileChecksum === expectedChecksum); + }); + stream.on('error', reject); + }); + } +} + +module.exports = LocalAIServiceBase; \ No newline at end of file diff --git a/src/common/services/localProgressTracker.js b/src/common/services/localProgressTracker.js new file mode 100644 index 0000000..48f5fcb --- /dev/null +++ b/src/common/services/localProgressTracker.js @@ -0,0 +1,133 @@ +export class LocalProgressTracker { + constructor(serviceName) { + this.serviceName = serviceName; + this.activeOperations = new Map(); // operationId -> { controller, onProgress } + this.ipcRenderer = window.require?.('electron')?.ipcRenderer; + + if (!this.ipcRenderer) { + throw new Error(`${serviceName} requires Electron environment`); + } + + this.globalProgressHandler = (event, data) => { + const operation = this.activeOperations.get(data.model || data.modelId); + if (operation && !operation.controller.signal.aborted) { + operation.onProgress(data.progress); + } + }; + + const progressEvents = { + 'ollama': 'ollama:pull-progress', + 'whisper': 'whisper:download-progress' + }; + + const eventName = progressEvents[serviceName.toLowerCase()] || `${serviceName}:progress`; + this.progressEvent = eventName; + this.ipcRenderer.on(eventName, this.globalProgressHandler); + } + + async trackOperation(operationId, operationType, onProgress) { + if (this.activeOperations.has(operationId)) { + throw new Error(`${operationType} ${operationId} is already in progress`); + } + + const controller = new AbortController(); + const operation = { controller, onProgress }; + this.activeOperations.set(operationId, operation); + + try { + const ipcChannels = { + 'ollama': { install: 'ollama:pull-model' }, + 'whisper': { download: 'whisper:download-model' } + }; + + const channel = ipcChannels[this.serviceName.toLowerCase()]?.[operationType] || + `${this.serviceName}:${operationType}`; + + const result = await this.ipcRenderer.invoke(channel, operationId); + + if (!result.success) { + throw new Error(result.error || `${operationType} failed`); + } + + return true; + } catch (error) { + if (!controller.signal.aborted) { + throw error; + } + return false; + } finally { + this.activeOperations.delete(operationId); + } + } + + async installModel(modelName, onProgress) { + return this.trackOperation(modelName, 'install', onProgress); + } + + async downloadModel(modelId, onProgress) { + return this.trackOperation(modelId, 'download', onProgress); + } + + cancelOperation(operationId) { + const operation = this.activeOperations.get(operationId); + if (operation) { + operation.controller.abort(); + this.activeOperations.delete(operationId); + } + } + + cancelAllOperations() { + for (const [operationId, operation] of this.activeOperations) { + operation.controller.abort(); + } + this.activeOperations.clear(); + } + + isOperationActive(operationId) { + return this.activeOperations.has(operationId); + } + + getActiveOperations() { + return Array.from(this.activeOperations.keys()); + } + + destroy() { + this.cancelAllOperations(); + if (this.ipcRenderer) { + this.ipcRenderer.removeListener(this.progressEvent, this.globalProgressHandler); + } + } +} + +let trackers = new Map(); + +export function getLocalProgressTracker(serviceName) { + if (!trackers.has(serviceName)) { + trackers.set(serviceName, new LocalProgressTracker(serviceName)); + } + return trackers.get(serviceName); +} + +export function destroyLocalProgressTracker(serviceName) { + const tracker = trackers.get(serviceName); + if (tracker) { + tracker.destroy(); + trackers.delete(serviceName); + } +} + +export function destroyAllProgressTrackers() { + for (const [name, tracker] of trackers) { + tracker.destroy(); + } + trackers.clear(); +} + +// Legacy compatibility exports +export function getOllamaProgressTracker() { + return getLocalProgressTracker('ollama'); +} + +export function destroyOllamaProgressTracker() { + destroyLocalProgressTracker('ollama'); +} \ No newline at end of file diff --git a/src/common/services/migrationService.js b/src/common/services/migrationService.js new file mode 100644 index 0000000..8fe13c3 --- /dev/null +++ b/src/common/services/migrationService.js @@ -0,0 +1,192 @@ +const { doc, writeBatch, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../services/firebaseClient'); +const encryptionService = require('../services/encryptionService'); + +const sqliteSessionRepo = require('../repositories/session/sqlite.repository'); +const sqlitePresetRepo = require('../repositories/preset/sqlite.repository'); +const sqliteUserRepo = require('../repositories/user/sqlite.repository'); +const sqliteSttRepo = require('../../features/listen/stt/repositories/sqlite.repository'); +const sqliteSummaryRepo = require('../../features/listen/summary/repositories/sqlite.repository'); +const sqliteAiMessageRepo = require('../../features/ask/repositories/sqlite.repository'); + +const MAX_BATCH_OPERATIONS = 500; + +async function checkAndRunMigration(firebaseUser) { + if (!firebaseUser || !firebaseUser.uid) { + console.log('[Migration] No user, skipping migration check.'); + return; + } + + console.log(`[Migration] Checking for user ${firebaseUser.uid}...`); + + const localUser = sqliteUserRepo.getById(firebaseUser.uid); + if (!localUser || localUser.has_migrated_to_firebase) { + console.log(`[Migration] User ${firebaseUser.uid} is not eligible or already migrated.`); + return; + } + + console.log(`[Migration] Starting data migration for user ${firebaseUser.uid}...`); + + try { + const db = getFirestoreInstance(); + + // --- Phase 1: Migrate Parent Documents (Presets & Sessions) --- + console.log('[Migration Phase 1] Migrating parent documents...'); + let phase1Batch = writeBatch(db); + let phase1OpCount = 0; + const phase1Promises = []; + + const localPresets = (await sqlitePresetRepo.getPresets(firebaseUser.uid)).filter(p => !p.is_default); + console.log(`[Migration Phase 1] Found ${localPresets.length} custom presets.`); + for (const preset of localPresets) { + const presetRef = doc(db, 'prompt_presets', preset.id); + const cleanPreset = { + uid: preset.uid, + title: encryptionService.encrypt(preset.title ?? ''), + prompt: encryptionService.encrypt(preset.prompt ?? ''), + is_default: preset.is_default ?? 0, + created_at: preset.created_at ? Timestamp.fromMillis(preset.created_at * 1000) : null, + updated_at: preset.updated_at ? Timestamp.fromMillis(preset.updated_at * 1000) : null + }; + phase1Batch.set(presetRef, cleanPreset); + phase1OpCount++; + if (phase1OpCount >= MAX_BATCH_OPERATIONS) { + phase1Promises.push(phase1Batch.commit()); + phase1Batch = writeBatch(db); + phase1OpCount = 0; + } + } + + const localSessions = await sqliteSessionRepo.getAllByUserId(firebaseUser.uid); + console.log(`[Migration Phase 1] Found ${localSessions.length} sessions.`); + for (const session of localSessions) { + const sessionRef = doc(db, 'sessions', session.id); + const cleanSession = { + uid: session.uid, + members: session.members ?? [session.uid], + title: encryptionService.encrypt(session.title ?? ''), + session_type: session.session_type ?? 'ask', + started_at: session.started_at ? Timestamp.fromMillis(session.started_at * 1000) : null, + ended_at: session.ended_at ? Timestamp.fromMillis(session.ended_at * 1000) : null, + updated_at: session.updated_at ? Timestamp.fromMillis(session.updated_at * 1000) : null + }; + phase1Batch.set(sessionRef, cleanSession); + phase1OpCount++; + if (phase1OpCount >= MAX_BATCH_OPERATIONS) { + phase1Promises.push(phase1Batch.commit()); + phase1Batch = writeBatch(db); + phase1OpCount = 0; + } + } + + if (phase1OpCount > 0) { + phase1Promises.push(phase1Batch.commit()); + } + + if (phase1Promises.length > 0) { + await Promise.all(phase1Promises); + console.log(`[Migration Phase 1] Successfully committed ${phase1Promises.length} batches of parent documents.`); + } else { + console.log('[Migration Phase 1] No parent documents to migrate.'); + } + + // --- Phase 2: Migrate Child Documents (sub-collections) --- + console.log('[Migration Phase 2] Migrating child documents for all sessions...'); + let phase2Batch = writeBatch(db); + let phase2OpCount = 0; + const phase2Promises = []; + + for (const session of localSessions) { + const transcripts = await sqliteSttRepo.getAllTranscriptsBySessionId(session.id); + for (const t of transcripts) { + const transcriptRef = doc(db, `sessions/${session.id}/transcripts`, t.id); + const cleanTranscript = { + uid: firebaseUser.uid, + session_id: t.session_id, + start_at: t.start_at ? Timestamp.fromMillis(t.start_at * 1000) : null, + end_at: t.end_at ? Timestamp.fromMillis(t.end_at * 1000) : null, + speaker: t.speaker ?? null, + text: encryptionService.encrypt(t.text ?? ''), + lang: t.lang ?? 'en', + created_at: t.created_at ? Timestamp.fromMillis(t.created_at * 1000) : null + }; + phase2Batch.set(transcriptRef, cleanTranscript); + phase2OpCount++; + if (phase2OpCount >= MAX_BATCH_OPERATIONS) { + phase2Promises.push(phase2Batch.commit()); + phase2Batch = writeBatch(db); + phase2OpCount = 0; + } + } + + const messages = await sqliteAiMessageRepo.getAllAiMessagesBySessionId(session.id); + for (const m of messages) { + const msgRef = doc(db, `sessions/${session.id}/ai_messages`, m.id); + const cleanMessage = { + uid: firebaseUser.uid, + session_id: m.session_id, + sent_at: m.sent_at ? Timestamp.fromMillis(m.sent_at * 1000) : null, + role: m.role ?? 'user', + content: encryptionService.encrypt(m.content ?? ''), + tokens: m.tokens ?? null, + model: m.model ?? 'unknown', + created_at: m.created_at ? Timestamp.fromMillis(m.created_at * 1000) : null + }; + phase2Batch.set(msgRef, cleanMessage); + phase2OpCount++; + if (phase2OpCount >= MAX_BATCH_OPERATIONS) { + phase2Promises.push(phase2Batch.commit()); + phase2Batch = writeBatch(db); + phase2OpCount = 0; + } + } + + const summary = await sqliteSummaryRepo.getSummaryBySessionId(session.id); + if (summary) { + // Reverting to use 'data' as the document ID for summary. + const summaryRef = doc(db, `sessions/${session.id}/summary`, 'data'); + const cleanSummary = { + uid: firebaseUser.uid, + session_id: summary.session_id, + generated_at: summary.generated_at ? Timestamp.fromMillis(summary.generated_at * 1000) : null, + model: summary.model ?? 'unknown', + tldr: encryptionService.encrypt(summary.tldr ?? ''), + text: encryptionService.encrypt(summary.text ?? ''), + bullet_json: encryptionService.encrypt(summary.bullet_json ?? '[]'), + action_json: encryptionService.encrypt(summary.action_json ?? '[]'), + tokens_used: summary.tokens_used ?? null, + updated_at: summary.updated_at ? Timestamp.fromMillis(summary.updated_at * 1000) : null + }; + phase2Batch.set(summaryRef, cleanSummary); + phase2OpCount++; + if (phase2OpCount >= MAX_BATCH_OPERATIONS) { + phase2Promises.push(phase2Batch.commit()); + phase2Batch = writeBatch(db); + phase2OpCount = 0; + } + } + } + + if (phase2OpCount > 0) { + phase2Promises.push(phase2Batch.commit()); + } + + if (phase2Promises.length > 0) { + await Promise.all(phase2Promises); + console.log(`[Migration Phase 2] Successfully committed ${phase2Promises.length} batches of child documents.`); + } else { + console.log('[Migration Phase 2] No child documents to migrate.'); + } + + // --- 4. Mark migration as complete --- + sqliteUserRepo.setMigrationComplete(firebaseUser.uid); + console.log(`[Migration] โœ… Successfully marked migration as complete for ${firebaseUser.uid}.`); + + } catch (error) { + console.error(`[Migration] ๐Ÿ”ฅ An error occurred during migration for user ${firebaseUser.uid}:`, error); + } +} + +module.exports = { + checkAndRunMigration, +}; \ No newline at end of file diff --git a/src/common/services/modelStateService.js b/src/common/services/modelStateService.js index 7218ce7..62a093b 100644 --- a/src/common/services/modelStateService.js +++ b/src/common/services/modelStateService.js @@ -2,6 +2,7 @@ const Store = require('electron-store'); const fetch = require('node-fetch'); const { ipcMain, webContents } = require('electron'); const { PROVIDERS } = require('../ai/factory'); +const cryptoService = require('./cryptoService'); class ModelStateService { constructor(authService) { @@ -23,7 +24,7 @@ class ModelStateService { const llmProvider = this.getProviderForModel('llm', llmModel) || 'None'; const sttProvider = this.getProviderForModel('stt', sttModel) || 'None'; - console.log(`[ModelStateService] ๐ŸŒŸ Current Selection -> LLM: ${llmModel || 'None'} (Provider: ${llmProvider}), STT: ${sttModel || 'None'} (Provider: ${sttProvider})`); + console.log(`[ModelStateService] Current Selection -> LLM: ${llmModel || 'None'} (Provider: ${llmProvider}), STT: ${sttModel || 'None'} (Provider: ${sttProvider})`); } _autoSelectAvailableModels() { @@ -36,7 +37,9 @@ class ModelStateService { if (currentModelId) { const provider = this.getProviderForModel(type, currentModelId); - if (provider && this.getApiKey(provider)) { + const apiKey = this.getApiKey(provider); + // For Ollama, 'local' is a valid API key + if (provider && (apiKey || (provider === 'ollama' && apiKey === 'local'))) { isCurrentModelValid = true; } } @@ -45,8 +48,15 @@ class ModelStateService { console.log(`[ModelStateService] No valid ${type.toUpperCase()} model selected. Finding an alternative...`); const availableModels = this.getAvailableModels(type); if (availableModels.length > 0) { - this.state.selectedModels[type] = availableModels[0].id; - console.log(`[ModelStateService] Auto-selected ${type.toUpperCase()} model: ${availableModels[0].id}`); + // Prefer API providers over local providers for auto-selection + const apiModel = availableModels.find(model => { + const provider = this.getProviderForModel(type, model.id); + return provider && provider !== 'ollama' && provider !== 'whisper'; + }); + + const selectedModel = apiModel || availableModels[0]; + this.state.selectedModels[type] = selectedModel.id; + console.log(`[ModelStateService] Auto-selected ${type.toUpperCase()} model: ${selectedModel.id} (preferred: ${apiModel ? 'API' : 'local'})`); } else { this.state.selectedModels[type] = null; } @@ -67,11 +77,20 @@ class ModelStateService { }; this.state = this.store.get(`users.${userId}`, defaultState); console.log(`[ModelStateService] State loaded for user: ${userId}`); + for (const p of Object.keys(PROVIDERS)) { - if (!(p in this.state.apiKeys)) { + if (!(p in this.state.apiKeys)) { + this.state.apiKeys[p] = null; + } else if (this.state.apiKeys[p] && p !== 'ollama' && p !== 'whisper') { + try { + this.state.apiKeys[p] = cryptoService.decrypt(this.state.apiKeys[p]); + } catch (error) { + console.error(`[ModelStateService] Failed to decrypt API key for ${p}, resetting`); this.state.apiKeys[p] = null; } } + } + this._autoSelectAvailableModels(); this._saveState(); this._logCurrentSelection(); @@ -80,7 +99,23 @@ class ModelStateService { _saveState() { const userId = this.authService.getCurrentUserId(); - this.store.set(`users.${userId}`, this.state); + const stateToSave = { + ...this.state, + apiKeys: { ...this.state.apiKeys } + }; + + for (const [provider, key] of Object.entries(stateToSave.apiKeys)) { + if (key && provider !== 'ollama' && provider !== 'whisper') { + try { + stateToSave.apiKeys[provider] = cryptoService.encrypt(key); + } catch (error) { + console.error(`[ModelStateService] Failed to encrypt API key for ${provider}`); + stateToSave.apiKeys[provider] = null; + } + } + } + + this.store.set(`users.${userId}`, stateToSave); console.log(`[ModelStateService] State saved for user: ${userId}`); this._logCurrentSelection(); } @@ -94,6 +129,26 @@ class ModelStateService { const body = undefined; switch (provider) { + case 'ollama': + // Ollama doesn't need API key validation + // Just check if the service is running + try { + const response = await fetch('http://localhost:11434/api/tags'); + if (response.ok) { + console.log(`[ModelStateService] Ollama service is accessible.`); + this.setApiKey(provider, 'local'); // Use 'local' as a placeholder + return { success: true }; + } else { + return { success: false, error: 'Ollama service is not running. Please start Ollama first.' }; + } + } catch (error) { + return { success: false, error: 'Cannot connect to Ollama. Please ensure Ollama is installed and running.' }; + } + case 'whisper': + // Whisper is a local service, no API key validation needed + console.log(`[ModelStateService] Whisper is a local service.`); + this.setApiKey(provider, 'local'); // Use 'local' as a placeholder + return { success: true }; case 'openai': validationUrl = 'https://api.openai.com/v1/models'; headers = { 'Authorization': `Bearer ${key}` }; @@ -158,13 +213,21 @@ class ModelStateService { const llmModels = PROVIDERS['openai-glass']?.llmModels; const sttModels = PROVIDERS['openai-glass']?.sttModels; - if (!this.state.selectedModels.llm && llmModels?.length > 0) { + // When logging in with Pickle, prioritize Pickle's models over existing selections + if (virtualKey && llmModels?.length > 0) { this.state.selectedModels.llm = llmModels[0].id; + console.log(`[ModelStateService] Prioritized Pickle LLM model: ${llmModels[0].id}`); } - if (!this.state.selectedModels.stt && sttModels?.length > 0) { + if (virtualKey && sttModels?.length > 0) { this.state.selectedModels.stt = sttModels[0].id; + console.log(`[ModelStateService] Prioritized Pickle STT model: ${sttModels[0].id}`); } - this._autoSelectAvailableModels(); + + // If logging out (virtualKey is null), run auto-selection to find alternatives + if (!virtualKey) { + this._autoSelectAvailableModels(); + } + this._saveState(); this._logCurrentSelection(); } @@ -176,11 +239,19 @@ class ModelStateService { const llmModels = PROVIDERS[provider]?.llmModels; const sttModels = PROVIDERS[provider]?.sttModels; - if (!this.state.selectedModels.llm && llmModels?.length > 0) { - this.state.selectedModels.llm = llmModels[0].id; + // Prioritize newly set API key provider over existing selections + // Only for non-local providers or if no model is currently selected + if (llmModels?.length > 0) { + if (!this.state.selectedModels.llm || provider !== 'ollama') { + this.state.selectedModels.llm = llmModels[0].id; + console.log(`[ModelStateService] Selected LLM model from newly configured provider ${provider}: ${llmModels[0].id}`); + } } - if (!this.state.selectedModels.stt && sttModels?.length > 0) { - this.state.selectedModels.stt = sttModels[0].id; + if (sttModels?.length > 0) { + if (!this.state.selectedModels.stt || provider !== 'whisper') { + this.state.selectedModels.stt = sttModels[0].id; + console.log(`[ModelStateService] Selected STT model from newly configured provider ${provider}: ${sttModels[0].id}`); + } } this._saveState(); this._logCurrentSelection(); @@ -223,6 +294,14 @@ class ModelStateService { return providerId; } } + + // If no provider was found, assume it could be a custom Ollama model + // if Ollama provider is configured (has a key). + if (type === 'llm' && this.state.apiKeys['ollama']) { + console.log(`[ModelStateService] Model '${modelId}' not found in PROVIDERS list, assuming it's a custom Ollama model.`); + return 'ollama'; + } + return null; } @@ -239,10 +318,33 @@ class ModelStateService { if (this.isLoggedInWithFirebase()) return true; // LLM๊ณผ STT ๋ชจ๋ธ์„ ์ œ๊ณตํ•˜๋Š” Provider ์ค‘ ํ•˜๋‚˜๋ผ๋„ API ํ‚ค๊ฐ€ ์„ค์ •๋˜์—ˆ๋Š”์ง€ ํ™•์ธ - const hasLlmKey = Object.entries(this.state.apiKeys).some(([provider, key]) => key && PROVIDERS[provider]?.llmModels.length > 0); - const hasSttKey = Object.entries(this.state.apiKeys).some(([provider, key]) => key && PROVIDERS[provider]?.sttModels.length > 0); + const hasLlmKey = Object.entries(this.state.apiKeys).some(([provider, key]) => { + if (provider === 'ollama') { + // Ollama uses dynamic models, so just check if configured (has 'local' key) + return key === 'local'; + } + if (provider === 'whisper') { + // Whisper doesn't support LLM + return false; + } + return key && PROVIDERS[provider]?.llmModels.length > 0; + }); - return hasLlmKey && hasSttKey; + const hasSttKey = Object.entries(this.state.apiKeys).some(([provider, key]) => { + if (provider === 'whisper') { + // Whisper has static model list and supports STT + return key === 'local' && PROVIDERS[provider]?.sttModels.length > 0; + } + if (provider === 'ollama') { + // Ollama doesn't support STT yet + return false; + } + return key && PROVIDERS[provider]?.sttModels.length > 0; + }); + + const result = hasLlmKey && hasSttKey; + console.log(`[ModelStateService] areProvidersConfigured: LLM=${hasLlmKey}, STT=${hasSttKey}, result=${result}`); + return result; } @@ -265,13 +367,58 @@ class ModelStateService { setSelectedModel(type, modelId) { const provider = this.getProviderForModel(type, modelId); if (provider && this.state.apiKeys[provider]) { + const previousModel = this.state.selectedModels[type]; this.state.selectedModels[type] = modelId; this._saveState(); + + // Auto warm-up for Ollama LLM models when changed + if (type === 'llm' && provider === 'ollama' && modelId !== previousModel) { + this._autoWarmUpOllamaModel(modelId, previousModel); + } + return true; } return false; } + /** + * Auto warm-up Ollama model when LLM selection changes + * @private + * @param {string} newModelId - The newly selected model + * @param {string} previousModelId - The previously selected model + */ + async _autoWarmUpOllamaModel(newModelId, previousModelId) { + try { + console.log(`[ModelStateService] ๐Ÿ”ฅ LLM model changed: ${previousModelId || 'None'} โ†’ ${newModelId}, triggering warm-up`); + + // Get Ollama service if available + const ollamaService = require('./ollamaService'); + if (!ollamaService) { + console.log('[ModelStateService] OllamaService not available for auto warm-up'); + return; + } + + // Delay warm-up slightly to allow UI to update first + setTimeout(async () => { + try { + console.log(`[ModelStateService] Starting background warm-up for: ${newModelId}`); + const success = await ollamaService.warmUpModel(newModelId); + + if (success) { + console.log(`[ModelStateService] โœ… Successfully warmed up model: ${newModelId}`); + } else { + console.log(`[ModelStateService] โš ๏ธ Failed to warm up model: ${newModelId}`); + } + } catch (error) { + console.log(`[ModelStateService] ๐Ÿšซ Error during auto warm-up for ${newModelId}:`, error.message); + } + }, 500); // 500ms delay + + } catch (error) { + console.error('[ModelStateService] Error in auto warm-up setup:', error); + } + } + /** * * @param {('llm' | 'stt')} type diff --git a/src/common/services/ollamaService.js b/src/common/services/ollamaService.js new file mode 100644 index 0000000..13186d5 --- /dev/null +++ b/src/common/services/ollamaService.js @@ -0,0 +1,809 @@ +const { spawn } = require('child_process'); +const { promisify } = require('util'); +const fetch = require('node-fetch'); +const path = require('path'); +const fs = require('fs').promises; +const { app } = require('electron'); +const LocalAIServiceBase = require('./localAIServiceBase'); +const { spawnAsync } = require('../utils/spawnHelper'); +const { DOWNLOAD_CHECKSUMS } = require('../config/checksums'); + +class OllamaService extends LocalAIServiceBase { + constructor() { + super('OllamaService'); + this.baseUrl = 'http://localhost:11434'; + this.warmingModels = new Map(); + this.warmedModels = new Set(); + this.lastWarmUpAttempt = new Map(); + + // Request management system + this.activeRequests = new Map(); + this.requestTimeouts = new Map(); + this.healthStatus = { + lastHealthCheck: 0, + consecutive_failures: 0, + is_circuit_open: false + }; + + // Configuration + this.requestTimeout = 8000; // 8s for health checks + this.warmupTimeout = 15000; // 15s for model warmup + this.healthCheckInterval = 60000; // 1min between health checks + this.circuitBreakerThreshold = 3; + this.circuitBreakerCooldown = 30000; // 30s + + // Supported models are determined dynamically from installed models + this.supportedModels = {}; + + // Start health monitoring + this._startHealthMonitoring(); + } + + getOllamaCliPath() { + if (this.getPlatform() === 'darwin') { + return '/Applications/Ollama.app/Contents/Resources/ollama'; + } + return 'ollama'; + } + + /** + * Professional request management with AbortController-based cancellation + */ + async _makeRequest(url, options = {}, operationType = 'default') { + const requestId = `${operationType}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + // Circuit breaker check + if (this._isCircuitOpen()) { + throw new Error('Service temporarily unavailable (circuit breaker open)'); + } + + // Request deduplication for health checks + if (operationType === 'health' && this.activeRequests.has('health')) { + console.log('[OllamaService] Health check already in progress, returning existing promise'); + return this.activeRequests.get('health'); + } + + const controller = new AbortController(); + const timeout = options.timeout || this.requestTimeout; + + // Set up timeout mechanism + const timeoutId = setTimeout(() => { + controller.abort(); + this.activeRequests.delete(requestId); + this._recordFailure(); + }, timeout); + + this.requestTimeouts.set(requestId, timeoutId); + + const requestPromise = this._executeRequest(url, { + ...options, + signal: controller.signal + }, requestId); + + // Store active request for deduplication and cleanup + this.activeRequests.set(operationType === 'health' ? 'health' : requestId, requestPromise); + + try { + const result = await requestPromise; + this._recordSuccess(); + return result; + } catch (error) { + this._recordFailure(); + if (error.name === 'AbortError') { + throw new Error(`Request timeout after ${timeout}ms`); + } + throw error; + } finally { + clearTimeout(timeoutId); + this.requestTimeouts.delete(requestId); + this.activeRequests.delete(operationType === 'health' ? 'health' : requestId); + } + } + + async _executeRequest(url, options, requestId) { + try { + console.log(`[OllamaService] Executing request ${requestId} to ${url}`); + const response = await fetch(url, options); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return response; + } catch (error) { + console.error(`[OllamaService] Request ${requestId} failed:`, error.message); + throw error; + } + } + + _isCircuitOpen() { + if (!this.healthStatus.is_circuit_open) return false; + + // Check if cooldown period has passed + const now = Date.now(); + if (now - this.healthStatus.lastHealthCheck > this.circuitBreakerCooldown) { + console.log('[OllamaService] Circuit breaker cooldown expired, attempting recovery'); + this.healthStatus.is_circuit_open = false; + this.healthStatus.consecutive_failures = 0; + return false; + } + + return true; + } + + _recordSuccess() { + this.healthStatus.consecutive_failures = 0; + this.healthStatus.is_circuit_open = false; + this.healthStatus.lastHealthCheck = Date.now(); + } + + _recordFailure() { + this.healthStatus.consecutive_failures++; + this.healthStatus.lastHealthCheck = Date.now(); + + if (this.healthStatus.consecutive_failures >= this.circuitBreakerThreshold) { + console.warn(`[OllamaService] Circuit breaker opened after ${this.healthStatus.consecutive_failures} failures`); + this.healthStatus.is_circuit_open = true; + } + } + + _startHealthMonitoring() { + // Passive health monitoring - only when requests are made + console.log('[OllamaService] Health monitoring system initialized'); + } + + /** + * Cleanup all active requests and resources + */ + _cleanup() { + console.log(`[OllamaService] Cleaning up ${this.activeRequests.size} active requests`); + + // Cancel all active requests + for (const [requestId, promise] of this.activeRequests) { + if (this.requestTimeouts.has(requestId)) { + clearTimeout(this.requestTimeouts.get(requestId)); + this.requestTimeouts.delete(requestId); + } + } + + this.activeRequests.clear(); + this.requestTimeouts.clear(); + } + + async isInstalled() { + try { + const platform = this.getPlatform(); + + if (platform === 'darwin') { + try { + await fs.access('/Applications/Ollama.app'); + return true; + } catch { + const ollamaPath = await this.checkCommand(this.getOllamaCliPath()); + return !!ollamaPath; + } + } else { + const ollamaPath = await this.checkCommand(this.getOllamaCliPath()); + return !!ollamaPath; + } + } catch (error) { + console.log('[OllamaService] Ollama not found:', error.message); + return false; + } + } + + async isServiceRunning() { + try { + const response = await this._makeRequest(`${this.baseUrl}/api/tags`, { + method: 'GET', + timeout: this.requestTimeout + }, 'health'); + + return response.ok; + } catch (error) { + console.log(`[OllamaService] Service health check failed: ${error.message}`); + return false; + } + } + + async startService() { + const platform = this.getPlatform(); + + try { + if (platform === 'darwin') { + try { + await spawnAsync('open', ['-a', 'Ollama']); + await this.waitForService(() => this.isServiceRunning()); + return true; + } catch { + spawn(this.getOllamaCliPath(), ['serve'], { + detached: true, + stdio: 'ignore' + }).unref(); + await this.waitForService(() => this.isServiceRunning()); + return true; + } + } else { + spawn(this.getOllamaCliPath(), ['serve'], { + detached: true, + stdio: 'ignore', + shell: platform === 'win32' + }).unref(); + await this.waitForService(() => this.isServiceRunning()); + return true; + } + } catch (error) { + console.error('[OllamaService] Failed to start service:', error); + throw error; + } + } + + async stopService() { + return await this.shutdown(); + } + + async getInstalledModels() { + try { + const response = await this._makeRequest(`${this.baseUrl}/api/tags`, { + method: 'GET', + timeout: this.requestTimeout + }, 'models'); + + const data = await response.json(); + return data.models || []; + } catch (error) { + console.error('[OllamaService] Failed to get installed models:', error.message); + return []; + } + } + + async getInstalledModelsList() { + try { + const { stdout } = await spawnAsync(this.getOllamaCliPath(), ['list']); + const lines = stdout.split('\n').filter(line => line.trim()); + + // Skip header line (NAME, ID, SIZE, MODIFIED) + const modelLines = lines.slice(1); + + const models = []; + for (const line of modelLines) { + if (!line.trim()) continue; + + // Parse line: "model:tag model_id size modified_time" + const parts = line.split(/\s+/); + if (parts.length >= 3) { + models.push({ + name: parts[0], + id: parts[1], + size: parts[2] + (parts[3] === 'GB' || parts[3] === 'MB' ? ' ' + parts[3] : ''), + status: 'installed' + }); + } + } + + return models; + } catch (error) { + console.log('[OllamaService] Failed to get installed models via CLI, falling back to API'); + // Fallback to API if CLI fails + const apiModels = await this.getInstalledModels(); + return apiModels.map(model => ({ + name: model.name, + id: model.digest || 'unknown', + size: model.size || 'Unknown', + status: 'installed' + })); + } + } + + async getModelSuggestions() { + try { + // Get actually installed models + const installedModels = await this.getInstalledModelsList(); + + // Get user input history from storage (we'll implement this in the frontend) + // For now, just return installed models + return installedModels; + } catch (error) { + console.error('[OllamaService] Failed to get model suggestions:', error); + return []; + } + } + + async isModelInstalled(modelName) { + const models = await this.getInstalledModels(); + return models.some(model => model.name === modelName); + } + + async pullModel(modelName) { + if (!modelName?.trim()) { + throw new Error(`Invalid model name: ${modelName}`); + } + + console.log(`[OllamaService] Starting to pull model: ${modelName} via API`); + + try { + const response = await fetch(`${this.baseUrl}/api/pull`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: modelName, + stream: true + }) + }); + + if (!response.ok) { + throw new Error(`Pull API failed: ${response.status} ${response.statusText}`); + } + + // Handle Node.js streaming response + return new Promise((resolve, reject) => { + let buffer = ''; + + response.body.on('data', (chunk) => { + buffer += chunk.toString(); + const lines = buffer.split('\n'); + + // Keep incomplete line in buffer + buffer = lines.pop() || ''; + + // Process complete lines + for (const line of lines) { + if (!line.trim()) continue; + + try { + const data = JSON.parse(line); + const progress = this._parseOllamaPullProgress(data, modelName); + + if (progress !== null) { + this.setInstallProgress(modelName, progress); + this.emit('pull-progress', { + model: modelName, + progress, + status: data.status || 'downloading' + }); + console.log(`[OllamaService] API Progress: ${progress}% for ${modelName} (${data.status || 'downloading'})`); + } + + // Handle completion + if (data.status === 'success') { + console.log(`[OllamaService] Successfully pulled model: ${modelName}`); + this.emit('pull-complete', { model: modelName }); + this.clearInstallProgress(modelName); + resolve(); + return; + } + } catch (parseError) { + console.warn('[OllamaService] Failed to parse response line:', line); + } + } + }); + + response.body.on('end', () => { + // Process any remaining data in buffer + if (buffer.trim()) { + try { + const data = JSON.parse(buffer); + if (data.status === 'success') { + console.log(`[OllamaService] Successfully pulled model: ${modelName}`); + this.emit('pull-complete', { model: modelName }); + } + } catch (parseError) { + console.warn('[OllamaService] Failed to parse final buffer:', buffer); + } + } + this.clearInstallProgress(modelName); + resolve(); + }); + + response.body.on('error', (error) => { + console.error(`[OllamaService] Stream error for ${modelName}:`, error); + this.clearInstallProgress(modelName); + reject(error); + }); + }); + } catch (error) { + this.clearInstallProgress(modelName); + console.error(`[OllamaService] Pull model failed:`, error); + throw error; + } + } + + _parseOllamaPullProgress(data, modelName) { + // Handle Ollama API response format + if (data.status === 'success') { + return 100; + } + + // Handle downloading progress + if (data.total && data.completed !== undefined) { + const progress = Math.round((data.completed / data.total) * 100); + return Math.min(progress, 99); // Don't show 100% until success + } + + // Handle status-based progress + const statusProgress = { + 'pulling manifest': 5, + 'downloading': 10, + 'verifying sha256 digest': 90, + 'writing manifest': 95, + 'removing any unused layers': 98 + }; + + if (data.status && statusProgress[data.status] !== undefined) { + return statusProgress[data.status]; + } + + return null; + } + + + + async installMacOS(onProgress) { + console.log('[OllamaService] Installing Ollama on macOS using DMG...'); + + try { + const dmgUrl = 'https://ollama.com/download/Ollama.dmg'; + const tempDir = app.getPath('temp'); + const dmgPath = path.join(tempDir, 'Ollama.dmg'); + const mountPoint = path.join(tempDir, 'OllamaMount'); + + console.log('[OllamaService] Step 1: Downloading Ollama DMG...'); + onProgress?.({ stage: 'downloading', message: 'Downloading Ollama installer...', progress: 0 }); + const checksumInfo = DOWNLOAD_CHECKSUMS.ollama.dmg; + await this.downloadWithRetry(dmgUrl, dmgPath, { + expectedChecksum: checksumInfo?.sha256, + onProgress: (progress) => { + onProgress?.({ stage: 'downloading', message: `Downloading... ${progress}%`, progress }); + } + }); + + console.log('[OllamaService] Step 2: Mounting DMG...'); + onProgress?.({ stage: 'mounting', message: 'Mounting disk image...', progress: 0 }); + await fs.mkdir(mountPoint, { recursive: true }); + await spawnAsync('hdiutil', ['attach', dmgPath, '-mountpoint', mountPoint]); + onProgress?.({ stage: 'mounting', message: 'Disk image mounted.', progress: 100 }); + + console.log('[OllamaService] Step 3: Installing Ollama.app...'); + onProgress?.({ stage: 'installing', message: 'Installing Ollama application...', progress: 0 }); + await spawnAsync('cp', ['-R', `${mountPoint}/Ollama.app`, '/Applications/']); + onProgress?.({ stage: 'installing', message: 'Application installed.', progress: 100 }); + + console.log('[OllamaService] Step 4: Setting up CLI path...'); + onProgress?.({ stage: 'linking', message: 'Creating command-line shortcut...', progress: 0 }); + try { + const script = `do shell script "mkdir -p /usr/local/bin && ln -sf '${this.getOllamaCliPath()}' '/usr/local/bin/ollama'" with administrator privileges`; + await spawnAsync('osascript', ['-e', script]); + onProgress?.({ stage: 'linking', message: 'Shortcut created.', progress: 100 }); + } catch (linkError) { + console.error('[OllamaService] CLI symlink creation failed:', linkError.message); + onProgress?.({ stage: 'linking', message: 'Shortcut creation failed (permissions?).', progress: 100 }); + // Not throwing an error, as the app might still work + } + + console.log('[OllamaService] Step 5: Cleanup...'); + onProgress?.({ stage: 'cleanup', message: 'Cleaning up installation files...', progress: 0 }); + await spawnAsync('hdiutil', ['detach', mountPoint]); + await fs.unlink(dmgPath).catch(() => {}); + await fs.rmdir(mountPoint).catch(() => {}); + onProgress?.({ stage: 'cleanup', message: 'Cleanup complete.', progress: 100 }); + + console.log('[OllamaService] Ollama installed successfully on macOS'); + + await new Promise(resolve => setTimeout(resolve, 2000)); + + return true; + } catch (error) { + console.error('[OllamaService] macOS installation failed:', error); + throw new Error(`Failed to install Ollama on macOS: ${error.message}`); + } + } + + async installWindows(onProgress) { + console.log('[OllamaService] Installing Ollama on Windows...'); + + try { + const exeUrl = 'https://ollama.com/download/OllamaSetup.exe'; + const tempDir = app.getPath('temp'); + const exePath = path.join(tempDir, 'OllamaSetup.exe'); + + console.log('[OllamaService] Step 1: Downloading Ollama installer...'); + onProgress?.({ stage: 'downloading', message: 'Downloading Ollama installer...', progress: 0 }); + const checksumInfo = DOWNLOAD_CHECKSUMS.ollama.exe; + await this.downloadWithRetry(exeUrl, exePath, { + expectedChecksum: checksumInfo?.sha256, + onProgress: (progress) => { + onProgress?.({ stage: 'downloading', message: `Downloading... ${progress}%`, progress }); + } + }); + + console.log('[OllamaService] Step 2: Running silent installation...'); + onProgress?.({ stage: 'installing', message: 'Installing Ollama...', progress: 0 }); + await spawnAsync(exePath, ['/VERYSILENT', '/NORESTART']); + onProgress?.({ stage: 'installing', message: 'Installation complete.', progress: 100 }); + + console.log('[OllamaService] Step 3: Cleanup...'); + onProgress?.({ stage: 'cleanup', message: 'Cleaning up installation files...', progress: 0 }); + await fs.unlink(exePath).catch(() => {}); + onProgress?.({ stage: 'cleanup', message: 'Cleanup complete.', progress: 100 }); + + console.log('[OllamaService] Ollama installed successfully on Windows'); + + await new Promise(resolve => setTimeout(resolve, 3000)); + + return true; + } catch (error) { + console.error('[OllamaService] Windows installation failed:', error); + throw new Error(`Failed to install Ollama on Windows: ${error.message}`); + } + } + + async installLinux() { + console.log('[OllamaService] Installing Ollama on Linux...'); + console.log('[OllamaService] Automatic installation on Linux is not supported for security reasons.'); + console.log('[OllamaService] Please install Ollama manually:'); + console.log('[OllamaService] 1. Visit https://ollama.com/download/linux'); + console.log('[OllamaService] 2. Follow the official installation instructions'); + console.log('[OllamaService] 3. Or use your package manager if available'); + throw new Error('Manual installation required on Linux. Please visit https://ollama.com/download/linux'); + } + + + + async warmUpModel(modelName, forceRefresh = false) { + if (!modelName?.trim()) { + console.warn(`[OllamaService] Invalid model name for warm-up`); + return false; + } + + // Check if already warmed (and not forcing refresh) + if (!forceRefresh && this.warmedModels.has(modelName)) { + console.log(`[OllamaService] Model ${modelName} already warmed up, skipping`); + return true; + } + + // Check if currently warming - return existing Promise + if (this.warmingModels.has(modelName)) { + console.log(`[OllamaService] Model ${modelName} is already warming up, joining existing operation`); + return await this.warmingModels.get(modelName); + } + + // Check rate limiting (prevent too frequent attempts) + const lastAttempt = this.lastWarmUpAttempt.get(modelName); + const now = Date.now(); + if (lastAttempt && (now - lastAttempt) < 5000) { // 5 second cooldown + console.log(`[OllamaService] Rate limiting warm-up for ${modelName}, try again in ${5 - Math.floor((now - lastAttempt) / 1000)}s`); + return false; + } + + // Create and store the warming Promise + const warmingPromise = this._performWarmUp(modelName); + this.warmingModels.set(modelName, warmingPromise); + this.lastWarmUpAttempt.set(modelName, now); + + try { + const result = await warmingPromise; + + if (result) { + this.warmedModels.add(modelName); + console.log(`[OllamaService] Model ${modelName} successfully warmed up`); + } + + return result; + } finally { + // Always clean up the warming Promise + this.warmingModels.delete(modelName); + } + } + + async _performWarmUp(modelName) { + console.log(`[OllamaService] Starting warm-up for model: ${modelName}`); + + try { + const response = await this._makeRequest(`${this.baseUrl}/api/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + model: modelName, + messages: [ + { role: 'user', content: 'Hi' } + ], + stream: false, + options: { + num_predict: 1, // Minimal response + temperature: 0 + } + }), + timeout: this.warmupTimeout + }, `warmup_${modelName}`); + + return true; + } catch (error) { + console.error(`[OllamaService] Failed to warm up model ${modelName}:`, error.message); + return false; + } + } + + async autoWarmUpSelectedModel() { + try { + // Get selected model from ModelStateService + const modelStateService = global.modelStateService; + if (!modelStateService) { + console.log('[OllamaService] ModelStateService not available for auto warm-up'); + return false; + } + + const selectedModels = modelStateService.getSelectedModels(); + const llmModelId = selectedModels.llm; + + // Check if it's an Ollama model + const provider = modelStateService.getProviderForModel('llm', llmModelId); + if (provider !== 'ollama') { + console.log('[OllamaService] Selected LLM is not Ollama, skipping warm-up'); + return false; + } + + // Check if Ollama service is running + const isRunning = await this.isServiceRunning(); + if (!isRunning) { + console.log('[OllamaService] Ollama service not running, clearing warm-up cache'); + this._clearWarmUpCache(); + return false; + } + + // Check if model is installed + const isInstalled = await this.isModelInstalled(llmModelId); + if (!isInstalled) { + console.log(`[OllamaService] Model ${llmModelId} not installed, skipping warm-up`); + return false; + } + + console.log(`[OllamaService] Auto-warming up selected model: ${llmModelId}`); + return await this.warmUpModel(llmModelId); + + } catch (error) { + console.error('[OllamaService] Auto warm-up failed:', error); + return false; + } + } + + _clearWarmUpCache() { + this.warmedModels.clear(); + this.warmingModels.clear(); + this.lastWarmUpAttempt.clear(); + console.log('[OllamaService] Warm-up cache cleared'); + } + + getWarmUpStatus() { + return { + warmedModels: Array.from(this.warmedModels), + warmingModels: Array.from(this.warmingModels.keys()), + lastAttempts: Object.fromEntries(this.lastWarmUpAttempt) + }; + } + + async shutdown(force = false) { + console.log(`[OllamaService] Shutdown initiated (force: ${force})`); + + if (!force && this.warmingModels.size > 0) { + const warmingList = Array.from(this.warmingModels.keys()); + console.log(`[OllamaService] Waiting for ${warmingList.length} models to finish warming: ${warmingList.join(', ')}`); + + const warmingPromises = Array.from(this.warmingModels.values()); + try { + // Use Promise.allSettled instead of race with setTimeout + const results = await Promise.allSettled(warmingPromises); + const completed = results.filter(r => r.status === 'fulfilled').length; + console.log(`[OllamaService] ${completed}/${results.length} warming operations completed`); + } catch (error) { + console.log('[OllamaService] Error waiting for warm-up completion, proceeding with shutdown'); + } + } + + // Clean up all resources + this._cleanup(); + this._clearWarmUpCache(); + + return super.shutdown(force); + } + + async shutdownMacOS(force) { + try { + // Try to quit Ollama.app gracefully + await spawnAsync('osascript', ['-e', 'tell application "Ollama" to quit']); + console.log('[OllamaService] Ollama.app quit successfully'); + + // Wait a moment for graceful shutdown + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Check if still running + const stillRunning = await this.isServiceRunning(); + if (stillRunning) { + console.log('[OllamaService] Ollama still running, forcing shutdown'); + // Force kill if necessary + await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]); + } + + return true; + } catch (error) { + console.log('[OllamaService] Graceful quit failed, trying force kill'); + try { + await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]); + return true; + } catch (killError) { + console.error('[OllamaService] Failed to force kill Ollama:', killError); + return false; + } + } + } + + async shutdownWindows(force) { + try { + // Try to stop the service gracefully + await spawnAsync('taskkill', ['/IM', 'ollama.exe', '/T']); + console.log('[OllamaService] Ollama process terminated on Windows'); + return true; + } catch (error) { + console.log('[OllamaService] Standard termination failed, trying force kill'); + try { + await spawnAsync('taskkill', ['/IM', 'ollama.exe', '/F', '/T']); + return true; + } catch (killError) { + console.error('[OllamaService] Failed to force kill Ollama on Windows:', killError); + return false; + } + } + } + + async shutdownLinux(force) { + try { + await spawnAsync('pkill', ['-f', this.getOllamaCliPath()]); + console.log('[OllamaService] Ollama process terminated on Linux'); + return true; + } catch (error) { + if (force) { + await spawnAsync('pkill', ['-9', '-f', this.getOllamaCliPath()]).catch(() => {}); + } + console.error('[OllamaService] Failed to shutdown Ollama on Linux:', error); + return false; + } + } + + async getAllModelsWithStatus() { + // Get all installed models directly from Ollama + const installedModels = await this.getInstalledModels(); + + const models = []; + for (const model of installedModels) { + models.push({ + name: model.name, + displayName: model.name, // Use model name as display name + size: model.size || 'Unknown', + description: `Ollama model: ${model.name}`, + installed: true, + installing: this.installationProgress.has(model.name), + progress: this.getInstallProgress(model.name) + }); + } + + // Also add any models currently being installed + for (const [modelName, progress] of this.installationProgress) { + if (!models.find(m => m.name === modelName)) { + models.push({ + name: modelName, + displayName: modelName, + size: 'Unknown', + description: `Ollama model: ${modelName}`, + installed: false, + installing: true, + progress: progress + }); + } + } + + return models; + } +} + +// Export singleton instance +const ollamaService = new OllamaService(); +module.exports = ollamaService; \ No newline at end of file diff --git a/src/common/services/whisperService.js b/src/common/services/whisperService.js new file mode 100644 index 0000000..684c055 --- /dev/null +++ b/src/common/services/whisperService.js @@ -0,0 +1,451 @@ +const { spawn } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const LocalAIServiceBase = require('./localAIServiceBase'); +const { spawnAsync } = require('../utils/spawnHelper'); +const { DOWNLOAD_CHECKSUMS } = require('../config/checksums'); + +const fsPromises = fs.promises; + +class WhisperService extends LocalAIServiceBase { + constructor() { + super('WhisperService'); + this.isInitialized = false; + this.whisperPath = null; + this.modelsDir = null; + this.tempDir = null; + this.availableModels = { + 'whisper-tiny': { + name: 'Tiny', + size: '39M', + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin' + }, + 'whisper-base': { + name: 'Base', + size: '74M', + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin' + }, + 'whisper-small': { + name: 'Small', + size: '244M', + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin' + }, + 'whisper-medium': { + name: 'Medium', + size: '769M', + url: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin' + } + }; + } + + async initialize() { + if (this.isInitialized) return; + + try { + const homeDir = os.homedir(); + const whisperDir = path.join(homeDir, '.glass', 'whisper'); + + this.modelsDir = path.join(whisperDir, 'models'); + this.tempDir = path.join(whisperDir, 'temp'); + + // Windows์—์„œ๋Š” .exe ํ™•์žฅ์ž ํ•„์š” + const platform = this.getPlatform(); + const whisperExecutable = platform === 'win32' ? 'whisper.exe' : 'whisper'; + this.whisperPath = path.join(whisperDir, 'bin', whisperExecutable); + + await this.ensureDirectories(); + await this.ensureWhisperBinary(); + + this.isInitialized = true; + console.log('[WhisperService] Initialized successfully'); + } catch (error) { + console.error('[WhisperService] Initialization failed:', error); + throw error; + } + } + + async ensureDirectories() { + await fsPromises.mkdir(this.modelsDir, { recursive: true }); + await fsPromises.mkdir(this.tempDir, { recursive: true }); + await fsPromises.mkdir(path.dirname(this.whisperPath), { recursive: true }); + } + + async ensureWhisperBinary() { + const whisperCliPath = await this.checkCommand('whisper-cli'); + if (whisperCliPath) { + this.whisperPath = whisperCliPath; + console.log(`[WhisperService] Found whisper-cli at: ${this.whisperPath}`); + return; + } + + const whisperPath = await this.checkCommand('whisper'); + if (whisperPath) { + this.whisperPath = whisperPath; + console.log(`[WhisperService] Found whisper at: ${this.whisperPath}`); + return; + } + + try { + await fsPromises.access(this.whisperPath, fs.constants.X_OK); + console.log('[WhisperService] Custom whisper binary found'); + return; + } catch (error) { + // Continue to installation + } + + const platform = this.getPlatform(); + if (platform === 'darwin') { + console.log('[WhisperService] Whisper not found, trying Homebrew installation...'); + try { + await this.installViaHomebrew(); + return; + } catch (error) { + console.log('[WhisperService] Homebrew installation failed:', error.message); + } + } + + await this.autoInstall(); + } + + async installViaHomebrew() { + const brewPath = await this.checkCommand('brew'); + if (!brewPath) { + throw new Error('Homebrew not found. Please install Homebrew first.'); + } + + console.log('[WhisperService] Installing whisper-cpp via Homebrew...'); + await spawnAsync('brew', ['install', 'whisper-cpp']); + + const whisperCliPath = await this.checkCommand('whisper-cli'); + if (whisperCliPath) { + this.whisperPath = whisperCliPath; + console.log(`[WhisperService] Whisper-cli installed via Homebrew at: ${this.whisperPath}`); + } else { + const whisperPath = await this.checkCommand('whisper'); + if (whisperPath) { + this.whisperPath = whisperPath; + console.log(`[WhisperService] Whisper installed via Homebrew at: ${this.whisperPath}`); + } + } + } + + + async ensureModelAvailable(modelId) { + if (!this.isInitialized) { + console.log('[WhisperService] Service not initialized, initializing now...'); + await this.initialize(); + } + + const modelInfo = this.availableModels[modelId]; + if (!modelInfo) { + throw new Error(`Unknown model: ${modelId}. Available models: ${Object.keys(this.availableModels).join(', ')}`); + } + + const modelPath = await this.getModelPath(modelId); + try { + await fsPromises.access(modelPath, fs.constants.R_OK); + console.log(`[WhisperService] Model ${modelId} already available at: ${modelPath}`); + } catch (error) { + console.log(`[WhisperService] Model ${modelId} not found, downloading...`); + await this.downloadModel(modelId); + } + } + + async downloadModel(modelId) { + const modelInfo = this.availableModels[modelId]; + const modelPath = await this.getModelPath(modelId); + const checksumInfo = DOWNLOAD_CHECKSUMS.whisper.models[modelId]; + + this.emit('downloadProgress', { modelId, progress: 0 }); + + await this.downloadWithRetry(modelInfo.url, modelPath, { + expectedChecksum: checksumInfo?.sha256, + onProgress: (progress) => { + this.emit('downloadProgress', { modelId, progress }); + } + }); + + console.log(`[WhisperService] Model ${modelId} downloaded successfully`); + } + + + async getModelPath(modelId) { + if (!this.isInitialized || !this.modelsDir) { + throw new Error('WhisperService is not initialized. Call initialize() first.'); + } + return path.join(this.modelsDir, `${modelId}.bin`); + } + + async getWhisperPath() { + return this.whisperPath; + } + + async saveAudioToTemp(audioBuffer, sessionId = '') { + const timestamp = Date.now(); + const random = Math.random().toString(36).substr(2, 6); + const sessionPrefix = sessionId ? `${sessionId}_` : ''; + const tempFile = path.join(this.tempDir, `audio_${sessionPrefix}${timestamp}_${random}.wav`); + + const wavHeader = this.createWavHeader(audioBuffer.length); + const wavBuffer = Buffer.concat([wavHeader, audioBuffer]); + + await fsPromises.writeFile(tempFile, wavBuffer); + return tempFile; + } + + createWavHeader(dataSize) { + const header = Buffer.alloc(44); + const sampleRate = 24000; + const numChannels = 1; + const bitsPerSample = 16; + + header.write('RIFF', 0); + header.writeUInt32LE(36 + dataSize, 4); + header.write('WAVE', 8); + header.write('fmt ', 12); + header.writeUInt32LE(16, 16); + header.writeUInt16LE(1, 20); + header.writeUInt16LE(numChannels, 22); + header.writeUInt32LE(sampleRate, 24); + header.writeUInt32LE(sampleRate * numChannels * bitsPerSample / 8, 28); + header.writeUInt16LE(numChannels * bitsPerSample / 8, 32); + header.writeUInt16LE(bitsPerSample, 34); + header.write('data', 36); + header.writeUInt32LE(dataSize, 40); + + return header; + } + + async cleanupTempFile(filePath) { + if (!filePath || typeof filePath !== 'string') { + console.warn('[WhisperService] Invalid file path for cleanup:', filePath); + return; + } + + const filesToCleanup = [ + filePath, + filePath.replace('.wav', '.txt'), + filePath.replace('.wav', '.json') + ]; + + for (const file of filesToCleanup) { + try { + // Check if file exists before attempting to delete + await fsPromises.access(file, fs.constants.F_OK); + await fsPromises.unlink(file); + console.log(`[WhisperService] Cleaned up: ${file}`); + } catch (error) { + // File doesn't exist or already deleted - this is normal + if (error.code !== 'ENOENT') { + console.warn(`[WhisperService] Failed to cleanup ${file}:`, error.message); + } + } + } + } + + async getInstalledModels() { + if (!this.isInitialized) { + console.log('[WhisperService] Service not initialized for getInstalledModels, initializing now...'); + await this.initialize(); + } + + const models = []; + for (const [modelId, modelInfo] of Object.entries(this.availableModels)) { + try { + const modelPath = await this.getModelPath(modelId); + await fsPromises.access(modelPath, fs.constants.R_OK); + models.push({ + id: modelId, + name: modelInfo.name, + size: modelInfo.size, + installed: true + }); + } catch (error) { + models.push({ + id: modelId, + name: modelInfo.name, + size: modelInfo.size, + installed: false + }); + } + } + return models; + } + + async isServiceRunning() { + return this.isInitialized; + } + + async startService() { + if (!this.isInitialized) { + await this.initialize(); + } + return true; + } + + async stopService() { + return true; + } + + async isInstalled() { + try { + const whisperPath = await this.checkCommand('whisper-cli') || await this.checkCommand('whisper'); + return !!whisperPath; + } catch (error) { + return false; + } + } + + async installMacOS() { + throw new Error('Binary installation not available for macOS. Please install Homebrew and run: brew install whisper-cpp'); + } + + async installWindows() { + console.log('[WhisperService] Installing Whisper on Windows...'); + const version = 'v1.7.6'; + const binaryUrl = `https://github.com/ggerganov/whisper.cpp/releases/download/${version}/whisper-cpp-${version}-win-x64.zip`; + const tempFile = path.join(this.tempDir, 'whisper-binary.zip'); + + try { + console.log('[WhisperService] Step 1: Downloading Whisper binary...'); + await this.downloadWithRetry(binaryUrl, tempFile); + + console.log('[WhisperService] Step 2: Extracting archive...'); + const extractDir = path.join(this.tempDir, 'extracted'); + + // ์ž„์‹œ ์••์ถ• ํ•ด์ œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ + await fsPromises.mkdir(extractDir, { recursive: true }); + + // PowerShell ๋ช…๋ น์—์„œ ๊ฒฝ๋กœ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ธ์šฉ + const expandCommand = `Expand-Archive -Path "${tempFile}" -DestinationPath "${extractDir}" -Force`; + await spawnAsync('powershell', ['-command', expandCommand]); + + console.log('[WhisperService] Step 3: Finding and moving whisper executable...'); + + // ์••์ถ• ํ•ด์ œ๋œ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ whisper.exe ํŒŒ์ผ ์ฐพ๊ธฐ + const whisperExecutables = await this.findWhisperExecutables(extractDir); + + if (whisperExecutables.length === 0) { + throw new Error('whisper.exe not found in extracted files'); + } + + // ์ฒซ ๋ฒˆ์งธ๋กœ ์ฐพ์€ whisper.exe๋ฅผ ๋ชฉํ‘œ ์œ„์น˜๋กœ ๋ณต์‚ฌ + const sourceExecutable = whisperExecutables[0]; + const targetDir = path.dirname(this.whisperPath); + await fsPromises.mkdir(targetDir, { recursive: true }); + await fsPromises.copyFile(sourceExecutable, this.whisperPath); + + console.log('[WhisperService] Step 4: Verifying installation...'); + + // ์„ค์น˜ ๊ฒ€์ฆ + await fsPromises.access(this.whisperPath, fs.constants.F_OK); + + // whisper.exe ์‹คํ–‰ ํ…Œ์ŠคํŠธ + try { + await spawnAsync(this.whisperPath, ['--help']); + console.log('[WhisperService] Whisper executable verified successfully'); + } catch (testError) { + console.warn('[WhisperService] Whisper executable test failed, but file exists:', testError.message); + } + + console.log('[WhisperService] Step 5: Cleanup...'); + + // ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ + await fsPromises.unlink(tempFile).catch(() => {}); + await this.removeDirectory(extractDir).catch(() => {}); + + console.log('[WhisperService] Whisper installed successfully on Windows'); + return true; + + } catch (error) { + console.error('[WhisperService] Windows installation failed:', error); + + // ์‹คํŒจ ์‹œ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ + await fsPromises.unlink(tempFile).catch(() => {}); + await this.removeDirectory(path.join(this.tempDir, 'extracted')).catch(() => {}); + + throw new Error(`Failed to install Whisper on Windows: ${error.message}`); + } + } + + // ์••์ถ• ํ•ด์ œ๋œ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ whisper.exe ํŒŒ์ผ๋“ค์„ ์žฌ๊ท€์ ์œผ๋กœ ์ฐพ๊ธฐ + async findWhisperExecutables(dir) { + const executables = []; + + try { + const items = await fsPromises.readdir(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + + if (item.isDirectory()) { + const subExecutables = await this.findWhisperExecutables(fullPath); + executables.push(...subExecutables); + } else if (item.isFile() && (item.name === 'whisper.exe' || item.name === 'main.exe')) { + // main.exe๋„ ํฌํ•จ (์ผ๋ถ€ ๋นŒ๋“œ์—์„œ whisper ์‹คํ–‰ํŒŒ์ผ์ด main.exe๋กœ ๋ช…๋ช…๋จ) + executables.push(fullPath); + } + } + } catch (error) { + console.warn('[WhisperService] Error reading directory:', dir, error.message); + } + + return executables; + } + + // ๋””๋ ‰ํ† ๋ฆฌ ์žฌ๊ท€์  ์‚ญ์ œ + async removeDirectory(dir) { + try { + const items = await fsPromises.readdir(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + + if (item.isDirectory()) { + await this.removeDirectory(fullPath); + } else { + await fsPromises.unlink(fullPath); + } + } + + await fsPromises.rmdir(dir); + } catch (error) { + console.warn('[WhisperService] Error removing directory:', dir, error.message); + } + } + + async installLinux() { + console.log('[WhisperService] Installing Whisper on Linux...'); + const version = 'v1.7.6'; + const binaryUrl = `https://github.com/ggerganov/whisper.cpp/releases/download/${version}/whisper-cpp-${version}-linux-x64.tar.gz`; + const tempFile = path.join(this.tempDir, 'whisper-binary.tar.gz'); + + try { + await this.downloadWithRetry(binaryUrl, tempFile); + const extractDir = path.dirname(this.whisperPath); + await spawnAsync('tar', ['-xzf', tempFile, '-C', extractDir, '--strip-components=1']); + await spawnAsync('chmod', ['+x', this.whisperPath]); + await fsPromises.unlink(tempFile); + console.log('[WhisperService] Whisper installed successfully on Linux'); + return true; + } catch (error) { + console.error('[WhisperService] Linux installation failed:', error); + throw new Error(`Failed to install Whisper on Linux: ${error.message}`); + } + } + + async shutdownMacOS(force) { + return true; + } + + async shutdownWindows(force) { + return true; + } + + async shutdownLinux(force) { + return true; + } +} + +module.exports = { WhisperService }; \ No newline at end of file diff --git a/src/common/utils/spawnHelper.js b/src/common/utils/spawnHelper.js new file mode 100644 index 0000000..5b80eb2 --- /dev/null +++ b/src/common/utils/spawnHelper.js @@ -0,0 +1,39 @@ +const { spawn } = require('child_process'); + +function spawnAsync(command, args = [], options = {}) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, options); + let stdout = ''; + let stderr = ''; + + if (child.stdout) { + child.stdout.on('data', (data) => { + stdout += data.toString(); + }); + } + + if (child.stderr) { + child.stderr.on('data', (data) => { + stderr += data.toString(); + }); + } + + child.on('error', (error) => { + reject(error); + }); + + child.on('close', (code) => { + if (code === 0) { + resolve({ stdout, stderr }); + } else { + const error = new Error(`Command failed with code ${code}: ${stderr || stdout}`); + error.code = code; + error.stdout = stdout; + error.stderr = stderr; + reject(error); + } + }); + }); +} + +module.exports = { spawnAsync }; \ No newline at end of file diff --git a/src/electron/windowManager.js b/src/electron/windowManager.js index e5a3968..4100999 100644 --- a/src/electron/windowManager.js +++ b/src/electron/windowManager.js @@ -6,7 +6,17 @@ const fs = require('node:fs'); const os = require('os'); const util = require('util'); const execFile = util.promisify(require('child_process').execFile); -const sharp = require('sharp'); + +// Try to load sharp, but don't fail if it's not available +let sharp; +try { + sharp = require('sharp'); + console.log('[WindowManager] Sharp module loaded successfully'); +} catch (error) { + console.warn('[WindowManager] Sharp module not available:', error.message); + console.warn('[WindowManager] Screenshot functionality will work with reduced image processing capabilities'); + sharp = null; +} const authService = require('../common/services/authService'); const systemSettingsRepository = require('../common/repositories/systemSettings'); const userRepository = require('../common/repositories/user'); @@ -1448,25 +1458,45 @@ async function captureScreenshot(options = {}) { const imageBuffer = await fs.promises.readFile(tempPath); await fs.promises.unlink(tempPath); - const resizedBuffer = await sharp(imageBuffer) - // .resize({ height: 1080 }) - .resize({ height: 384 }) - .jpeg({ quality: 80 }) - .toBuffer(); + if (sharp) { + try { + // Try using sharp for optimal image processing + const resizedBuffer = await sharp(imageBuffer) + // .resize({ height: 1080 }) + .resize({ height: 384 }) + .jpeg({ quality: 80 }) + .toBuffer(); - const base64 = resizedBuffer.toString('base64'); - const metadata = await sharp(resizedBuffer).metadata(); + const base64 = resizedBuffer.toString('base64'); + const metadata = await sharp(resizedBuffer).metadata(); + lastScreenshot = { + base64, + width: metadata.width, + height: metadata.height, + timestamp: Date.now(), + }; + + return { success: true, base64, width: metadata.width, height: metadata.height }; + } catch (sharpError) { + console.warn('Sharp module failed, falling back to basic image processing:', sharpError.message); + } + } + + // Fallback: Return the original image without resizing + console.log('[WindowManager] Using fallback image processing (no resize/compression)'); + const base64 = imageBuffer.toString('base64'); + lastScreenshot = { base64, - width: metadata.width, - height: metadata.height, + width: null, // We don't have metadata without sharp + height: null, timestamp: Date.now(), }; - return { success: true, base64, width: metadata.width, height: metadata.height }; + return { success: true, base64, width: null, height: null }; } catch (error) { - console.error('Failed to capture and resize screenshot:', error); + console.error('Failed to capture screenshot:', error); return { success: false, error: error.message }; } } diff --git a/src/features/ask/askService.js b/src/features/ask/askService.js index b59b65d..33d661e 100644 --- a/src/features/ask/askService.js +++ b/src/features/ask/askService.js @@ -28,9 +28,18 @@ async function sendMessage(userPrompt) { askWindow.webContents.send('hide-text-input'); } + let sessionId; + try { console.log(`[AskService] ๐Ÿค– Processing message: ${userPrompt.substring(0, 50)}...`); + // --- Save user's message immediately --- + // This ensures the user message is always timestamped before the assistant's response. + sessionId = await sessionRepository.getOrCreateActive('ask'); + await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() }); + console.log(`[AskService] DB: Saved user prompt to session ${sessionId}`); + // --- End of user message saving --- + const modelInfo = await getCurrentModelInfo(null, { type: 'llm' }); if (!modelInfo || !modelInfo.apiKey) { throw new Error('AI model or API key not configured.'); @@ -99,16 +108,13 @@ async function sendMessage(userPrompt) { if (data === '[DONE]') { askWin.webContents.send('ask-response-stream-end'); - // Save to DB + // Save assistant's message to DB try { - const uid = authService.getCurrentUserId(); - if (!uid) throw new Error("User not logged in, cannot save message."); - const sessionId = await sessionRepository.getOrCreateActive(uid, 'ask'); - await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() }); + // sessionId is already available from when we saved the user prompt await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse }); - console.log(`[AskService] DB: Saved ask/answer pair to session ${sessionId}`); + console.log(`[AskService] DB: Saved assistant response to session ${sessionId}`); } catch(dbError) { - console.error("[AskService] DB: Failed to save ask/answer pair:", dbError); + console.error("[AskService] DB: Failed to save assistant response:", dbError); } return { success: true, response: fullResponse }; diff --git a/src/features/ask/repositories/firebase.repository.js b/src/features/ask/repositories/firebase.repository.js new file mode 100644 index 0000000..09ef25c --- /dev/null +++ b/src/features/ask/repositories/firebase.repository.js @@ -0,0 +1,38 @@ +const { collection, addDoc, query, getDocs, orderBy, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../../common/services/firebaseClient'); +const { createEncryptedConverter } = require('../../../common/repositories/firestoreConverter'); + +const aiMessageConverter = createEncryptedConverter(['content']); + +function aiMessagesCol(sessionId) { + if (!sessionId) throw new Error("Session ID is required to access AI messages."); + const db = getFirestoreInstance(); + return collection(db, `sessions/${sessionId}/ai_messages`).withConverter(aiMessageConverter); +} + +async function addAiMessage({ uid, sessionId, role, content, model = 'unknown' }) { + const now = Timestamp.now(); + const newMessage = { + uid, // To identify the author of the message + session_id: sessionId, + sent_at: now, + role, + content, + model, + created_at: now, + }; + + const docRef = await addDoc(aiMessagesCol(sessionId), newMessage); + return { id: docRef.id }; +} + +async function getAllAiMessagesBySessionId(sessionId) { + const q = query(aiMessagesCol(sessionId), orderBy('sent_at', 'asc')); + const querySnapshot = await getDocs(q); + return querySnapshot.docs.map(doc => doc.data()); +} + +module.exports = { + addAiMessage, + getAllAiMessagesBySessionId, +}; \ No newline at end of file diff --git a/src/features/ask/repositories/index.js b/src/features/ask/repositories/index.js index 182d066..f9fdda1 100644 --- a/src/features/ask/repositories/index.js +++ b/src/features/ask/repositories/index.js @@ -1,18 +1,25 @@ const sqliteRepository = require('./sqlite.repository'); -// const firebaseRepository = require('./firebase.repository'); // Future implementation +const firebaseRepository = require('./firebase.repository'); const authService = require('../../../common/services/authService'); -function getRepository() { - // In the future, we can check the user's login status from authService - // const user = authService.getCurrentUser(); - // if (user.isLoggedIn) { - // return firebaseRepository; - // } +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } return sqliteRepository; } -// Directly export functions for ease of use, decided by the strategy -module.exports = { - addAiMessage: (...args) => getRepository().addAiMessage(...args), - getAllAiMessagesBySessionId: (...args) => getRepository().getAllAiMessagesBySessionId(...args), -}; \ No newline at end of file +// The adapter layer that injects the UID +const askRepositoryAdapter = { + addAiMessage: ({ sessionId, role, content, model }) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().addAiMessage({ uid, sessionId, role, content, model }); + }, + getAllAiMessagesBySessionId: (sessionId) => { + // This function does not require a UID at the service level. + return getBaseRepository().getAllAiMessagesBySessionId(sessionId); + } +}; + +module.exports = askRepositoryAdapter; \ No newline at end of file diff --git a/src/features/ask/repositories/sqlite.repository.js b/src/features/ask/repositories/sqlite.repository.js index bbab3fb..19962eb 100644 --- a/src/features/ask/repositories/sqlite.repository.js +++ b/src/features/ask/repositories/sqlite.repository.js @@ -1,6 +1,7 @@ const sqliteClient = require('../../../common/services/sqliteClient'); -function addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) { +function addAiMessage({ uid, sessionId, role, content, model = 'unknown' }) { + // uid is ignored in the SQLite implementation const db = sqliteClient.getDb(); const messageId = require('crypto').randomUUID(); const now = Math.floor(Date.now() / 1000); diff --git a/src/features/listen/listenService.js b/src/features/listen/listenService.js index 4218745..f676d93 100644 --- a/src/features/listen/listenService.js +++ b/src/features/listen/listenService.js @@ -77,12 +77,15 @@ class ListenService { async initializeNewSession() { try { - const uid = authService.getCurrentUserId(); - if (!uid) { - throw new Error("Cannot initialize session: user not logged in."); + // The UID is no longer passed to the repository method directly. + // The adapter layer handles UID injection. We just ensure a user is available. + const user = authService.getCurrentUser(); + if (!user) { + // This case should ideally not happen as authService initializes a default user. + throw new Error("Cannot initialize session: auth service not ready."); } - this.currentSessionId = await sessionRepository.getOrCreateActive(uid, 'listen'); + this.currentSessionId = await sessionRepository.getOrCreateActive('listen'); console.log(`[DB] New listen session ensured: ${this.currentSessionId}`); // Set session ID for summary service diff --git a/src/features/listen/stt/repositories/firebase.repository.js b/src/features/listen/stt/repositories/firebase.repository.js new file mode 100644 index 0000000..3fb1606 --- /dev/null +++ b/src/features/listen/stt/repositories/firebase.repository.js @@ -0,0 +1,36 @@ +const { collection, addDoc, query, getDocs, orderBy, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../../../common/services/firebaseClient'); +const { createEncryptedConverter } = require('../../../../common/repositories/firestoreConverter'); + +const transcriptConverter = createEncryptedConverter(['text']); + +function transcriptsCol(sessionId) { + if (!sessionId) throw new Error("Session ID is required to access transcripts."); + const db = getFirestoreInstance(); + return collection(db, `sessions/${sessionId}/transcripts`).withConverter(transcriptConverter); +} + +async function addTranscript({ uid, sessionId, speaker, text }) { + const now = Timestamp.now(); + const newTranscript = { + uid, // To identify the author/source of the transcript + session_id: sessionId, + start_at: now, + speaker, + text, + created_at: now, + }; + const docRef = await addDoc(transcriptsCol(sessionId), newTranscript); + return { id: docRef.id }; +} + +async function getAllTranscriptsBySessionId(sessionId) { + const q = query(transcriptsCol(sessionId), orderBy('start_at', 'asc')); + const querySnapshot = await getDocs(q); + return querySnapshot.docs.map(doc => doc.data()); +} + +module.exports = { + addTranscript, + getAllTranscriptsBySessionId, +}; \ No newline at end of file diff --git a/src/features/listen/stt/repositories/index.js b/src/features/listen/stt/repositories/index.js index 6de1a98..8554bc5 100644 --- a/src/features/listen/stt/repositories/index.js +++ b/src/features/listen/stt/repositories/index.js @@ -1,5 +1,23 @@ -const sttRepository = require('./sqlite.repository'); +const sqliteRepository = require('./sqlite.repository'); +const firebaseRepository = require('./firebase.repository'); +const authService = require('../../../../common/services/authService'); -module.exports = { - ...sttRepository, -}; \ No newline at end of file +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } + return sqliteRepository; +} + +const sttRepositoryAdapter = { + addTranscript: ({ sessionId, speaker, text }) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().addTranscript({ uid, sessionId, speaker, text }); + }, + getAllTranscriptsBySessionId: (sessionId) => { + return getBaseRepository().getAllTranscriptsBySessionId(sessionId); + } +}; + +module.exports = sttRepositoryAdapter; \ No newline at end of file diff --git a/src/features/listen/stt/repositories/sqlite.repository.js b/src/features/listen/stt/repositories/sqlite.repository.js index 80c838f..be4d00f 100644 --- a/src/features/listen/stt/repositories/sqlite.repository.js +++ b/src/features/listen/stt/repositories/sqlite.repository.js @@ -1,6 +1,7 @@ const sqliteClient = require('../../../../common/services/sqliteClient'); -function addTranscript({ sessionId, speaker, text }) { +function addTranscript({ uid, sessionId, speaker, text }) { + // uid is ignored in the SQLite implementation const db = sqliteClient.getDb(); const transcriptId = require('crypto').randomUUID(); const now = Math.floor(Date.now() / 1000); diff --git a/src/features/listen/stt/sttService.js b/src/features/listen/stt/sttService.js index 187f76e..39f7aa8 100644 --- a/src/features/listen/stt/sttService.js +++ b/src/features/listen/stt/sttService.js @@ -133,7 +133,50 @@ class SttService { return; } - if (this.modelInfo.provider === 'gemini') { + if (this.modelInfo.provider === 'whisper') { + // Whisper STT emits 'transcription' events with different structure + if (message.text && message.text.trim()) { + const finalText = message.text.trim(); + + // Filter out Whisper noise transcriptions + const noisePatterns = [ + '[BLANK_AUDIO]', + '[INAUDIBLE]', + '[MUSIC]', + '[SOUND]', + '[NOISE]', + '(BLANK_AUDIO)', + '(INAUDIBLE)', + '(MUSIC)', + '(SOUND)', + '(NOISE)' + ]; + + + + const normalizedText = finalText.toLowerCase().trim(); + + const isNoise = noisePatterns.some(pattern => + finalText.includes(pattern) || finalText === pattern + ); + + + if (!isNoise && finalText.length > 2) { + this.debounceMyCompletion(finalText); + + this.sendToRenderer('stt-update', { + speaker: 'Me', + text: finalText, + isPartial: false, + isFinal: true, + timestamp: Date.now(), + }); + } else { + console.log(`[Whisper-Me] Filtered noise: "${finalText}"`); + } + } + return; + } else if (this.modelInfo.provider === 'gemini') { if (!message.serverContent?.modelTurn) { console.log('[Gemini STT - Me]', JSON.stringify(message, null, 2)); } @@ -203,7 +246,50 @@ class SttService { return; } - if (this.modelInfo.provider === 'gemini') { + if (this.modelInfo.provider === 'whisper') { + // Whisper STT emits 'transcription' events with different structure + if (message.text && message.text.trim()) { + const finalText = message.text.trim(); + + // Filter out Whisper noise transcriptions + const noisePatterns = [ + '[BLANK_AUDIO]', + '[INAUDIBLE]', + '[MUSIC]', + '[SOUND]', + '[NOISE]', + '(BLANK_AUDIO)', + '(INAUDIBLE)', + '(MUSIC)', + '(SOUND)', + '(NOISE)' + ]; + + + const normalizedText = finalText.toLowerCase().trim(); + + const isNoise = noisePatterns.some(pattern => + finalText.includes(pattern) || finalText === pattern + ); + + + // Only process if it's not noise, not a false positive, and has meaningful content + if (!isNoise && finalText.length > 2) { + this.debounceTheirCompletion(finalText); + + this.sendToRenderer('stt-update', { + speaker: 'Them', + text: finalText, + isPartial: false, + isFinal: true, + timestamp: Date.now(), + }); + } else { + console.log(`[Whisper-Them] Filtered noise: "${finalText}"`); + } + } + return; + } else if (this.modelInfo.provider === 'gemini') { if (!message.serverContent?.modelTurn) { console.log('[Gemini STT - Them]', JSON.stringify(message, null, 2)); } @@ -294,9 +380,13 @@ class SttService { portkeyVirtualKey: this.modelInfo.provider === 'openai-glass' ? this.modelInfo.apiKey : undefined, }; + // Add sessionType for Whisper to distinguish between My and Their sessions + const myOptions = { ...sttOptions, callbacks: mySttConfig.callbacks, sessionType: 'my' }; + const theirOptions = { ...sttOptions, callbacks: theirSttConfig.callbacks, sessionType: 'their' }; + [this.mySttSession, this.theirSttSession] = await Promise.all([ - createSTT(this.modelInfo.provider, { ...sttOptions, callbacks: mySttConfig.callbacks }), - createSTT(this.modelInfo.provider, { ...sttOptions, callbacks: theirSttConfig.callbacks }), + createSTT(this.modelInfo.provider, myOptions), + createSTT(this.modelInfo.provider, theirOptions), ]); console.log('โœ… Both STT sessions initialized successfully.'); diff --git a/src/features/listen/summary/repositories/firebase.repository.js b/src/features/listen/summary/repositories/firebase.repository.js new file mode 100644 index 0000000..573c299 --- /dev/null +++ b/src/features/listen/summary/repositories/firebase.repository.js @@ -0,0 +1,48 @@ +const { collection, doc, setDoc, getDoc, Timestamp } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../../../common/services/firebaseClient'); +const { createEncryptedConverter } = require('../../../../common/repositories/firestoreConverter'); +const encryptionService = require('../../../../common/services/encryptionService'); + +const fieldsToEncrypt = ['tldr', 'text', 'bullet_json', 'action_json']; +const summaryConverter = createEncryptedConverter(fieldsToEncrypt); + +function summaryDocRef(sessionId) { + if (!sessionId) throw new Error("Session ID is required to access summary."); + const db = getFirestoreInstance(); + // Reverting to the original structure with 'data' as the document ID. + const docPath = `sessions/${sessionId}/summary/data`; + return doc(db, docPath).withConverter(summaryConverter); +} + +async function saveSummary({ uid, sessionId, tldr, text, bullet_json, action_json, model = 'unknown' }) { + const now = Timestamp.now(); + const summaryData = { + uid, // To know who generated the summary + session_id: sessionId, + generated_at: now, + model, + text, + tldr, + bullet_json, + action_json, + updated_at: now, + }; + + // The converter attached to summaryDocRef will handle encryption via its `toFirestore` method. + // Manual encryption was removed to fix the double-encryption bug. + const docRef = summaryDocRef(sessionId); + await setDoc(docRef, summaryData, { merge: true }); + + return { changes: 1 }; +} + +async function getSummaryBySessionId(sessionId) { + const docRef = summaryDocRef(sessionId); + const docSnap = await getDoc(docRef); + return docSnap.exists() ? docSnap.data() : null; +} + +module.exports = { + saveSummary, + getSummaryBySessionId, +}; \ No newline at end of file diff --git a/src/features/listen/summary/repositories/index.js b/src/features/listen/summary/repositories/index.js index d5bd3b3..21934ed 100644 --- a/src/features/listen/summary/repositories/index.js +++ b/src/features/listen/summary/repositories/index.js @@ -1,5 +1,23 @@ -const summaryRepository = require('./sqlite.repository'); +const sqliteRepository = require('./sqlite.repository'); +const firebaseRepository = require('./firebase.repository'); +const authService = require('../../../../common/services/authService'); -module.exports = { - ...summaryRepository, -}; \ No newline at end of file +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } + return sqliteRepository; +} + +const summaryRepositoryAdapter = { + saveSummary: ({ sessionId, tldr, text, bullet_json, action_json, model }) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().saveSummary({ uid, sessionId, tldr, text, bullet_json, action_json, model }); + }, + getSummaryBySessionId: (sessionId) => { + return getBaseRepository().getSummaryBySessionId(sessionId); + } +}; + +module.exports = summaryRepositoryAdapter; \ No newline at end of file diff --git a/src/features/listen/summary/repositories/sqlite.repository.js b/src/features/listen/summary/repositories/sqlite.repository.js index 008aa21..1e319cb 100644 --- a/src/features/listen/summary/repositories/sqlite.repository.js +++ b/src/features/listen/summary/repositories/sqlite.repository.js @@ -1,6 +1,7 @@ const sqliteClient = require('../../../../common/services/sqliteClient'); -function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) { +function saveSummary({ uid, sessionId, tldr, text, bullet_json, action_json, model = 'unknown' }) { + // uid is ignored in the SQLite implementation return new Promise((resolve, reject) => { try { const db = sqliteClient.getDb(); diff --git a/src/features/settings/SettingsView.js b/src/features/settings/SettingsView.js index 1aa40b1..d8b864c 100644 --- a/src/features/settings/SettingsView.js +++ b/src/features/settings/SettingsView.js @@ -1,4 +1,5 @@ import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; +import { getOllamaProgressTracker } from '../../common/services/localProgressTracker.js'; export class SettingsView extends LitElement { static styles = css` @@ -403,9 +404,77 @@ export class SettingsView extends LitElement { overflow-y: auto; background: rgba(0,0,0,0.3); border-radius: 4px; padding: 4px; margin-top: 4px; } - .model-item { padding: 5px 8px; font-size: 11px; border-radius: 3px; cursor: pointer; transition: background-color 0.15s; } + .model-item { + padding: 5px 8px; + font-size: 11px; + border-radius: 3px; + cursor: pointer; + transition: background-color 0.15s; + display: flex; + justify-content: space-between; + align-items: center; + } .model-item:hover { background-color: rgba(255,255,255,0.1); } - .model-item.selected { background-color: rgba(0, 122, 255, 0.4); font-weight: 500; + .model-item.selected { background-color: rgba(0, 122, 255, 0.4); font-weight: 500; } + .model-status { + font-size: 9px; + color: rgba(255,255,255,0.6); + margin-left: 8px; + } + .model-status.installed { color: rgba(0, 255, 0, 0.8); } + .model-status.not-installed { color: rgba(255, 200, 0, 0.8); } + .install-progress { + flex: 1; + height: 4px; + background: rgba(255,255,255,0.1); + border-radius: 2px; + margin-left: 8px; + overflow: hidden; + } + .install-progress-bar { + height: 100%; + background: rgba(0, 122, 255, 0.8); + transition: width 0.3s ease; + } + + /* Dropdown styles */ + select.model-dropdown { + background: rgba(0,0,0,0.2); + color: white; + cursor: pointer; + } + + select.model-dropdown option { + background: #1a1a1a; + color: white; + } + + select.model-dropdown option:disabled { + color: rgba(255,255,255,0.4); + } + + /* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€[ GLASS BYPASS ]โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + :host-context(body.has-glass) { + animation: none !important; + transition: none !important; + transform: none !important; + will-change: auto !important; + } + + :host-context(body.has-glass) * { + background: transparent !important; + filter: none !important; + backdrop-filter: none !important; + box-shadow: none !important; + outline: none !important; + border: none !important; + border-radius: 0 !important; + transition: none !important; + animation: none !important; + } + + :host-context(body.has-glass) .settings-container::before { + display: none !important; } `; @@ -430,6 +499,12 @@ export class SettingsView extends LitElement { showPresets: { type: Boolean, state: true }, autoUpdateEnabled: { type: Boolean, state: true }, autoUpdateLoading: { type: Boolean, state: true }, + // Ollama related properties + ollamaStatus: { type: Object, state: true }, + ollamaModels: { type: Array, state: true }, + installingModels: { type: Object, state: true }, + // Whisper related properties + whisperModels: { type: Array, state: true }, }; //////// after_modelStateService //////// @@ -438,7 +513,7 @@ export class SettingsView extends LitElement { //////// after_modelStateService //////// this.shortcuts = {}; this.firebaseUser = null; - this.apiKeys = { openai: '', gemini: '', anthropic: '' }; + this.apiKeys = { openai: '', gemini: '', anthropic: '', whisper: '' }; this.providerConfig = {}; this.isLoading = true; this.isContentProtectionOn = true; @@ -452,6 +527,14 @@ export class SettingsView extends LitElement { this.presets = []; this.selectedPreset = null; this.showPresets = false; + // Ollama related + this.ollamaStatus = { installed: false, running: false }; + this.ollamaModels = []; + this.installingModels = {}; // { modelName: progress } + this.progressTracker = getOllamaProgressTracker(); + // Whisper related + this.whisperModels = []; + this.whisperProgressTracker = null; // Will be initialized when needed this.handleUsePicklesKey = this.handleUsePicklesKey.bind(this) this.autoUpdateEnabled = true; this.autoUpdateLoading = true; @@ -501,7 +584,7 @@ export class SettingsView extends LitElement { this.isLoading = true; const { ipcRenderer } = window.require('electron'); try { - const [userState, config, storedKeys, availableLlm, availableStt, selectedModels, presets, contentProtection, shortcuts] = await Promise.all([ + const [userState, config, storedKeys, availableLlm, availableStt, selectedModels, presets, contentProtection, shortcuts, ollamaStatus, whisperModelsResult] = await Promise.all([ ipcRenderer.invoke('get-current-user'), ipcRenderer.invoke('model:get-provider-config'), // Provider ์„ค์ • ๋กœ๋“œ ipcRenderer.invoke('model:get-all-keys'), @@ -510,7 +593,9 @@ export class SettingsView extends LitElement { ipcRenderer.invoke('model:get-selected-models'), ipcRenderer.invoke('settings:getPresets'), ipcRenderer.invoke('get-content-protection-status'), - ipcRenderer.invoke('get-current-shortcuts') + ipcRenderer.invoke('get-current-shortcuts'), + ipcRenderer.invoke('ollama:get-status'), + ipcRenderer.invoke('whisper:get-installed-models') ]); if (userState && userState.isLoggedIn) this.firebaseUser = userState; @@ -527,6 +612,23 @@ export class SettingsView extends LitElement { const firstUserPreset = this.presets.find(p => p.is_default === 0); if (firstUserPreset) this.selectedPreset = firstUserPreset; } + // Ollama status + if (ollamaStatus?.success) { + this.ollamaStatus = { installed: ollamaStatus.installed, running: ollamaStatus.running }; + this.ollamaModels = ollamaStatus.models || []; + } + // Whisper status + if (whisperModelsResult?.success) { + const installedWhisperModels = whisperModelsResult.models; + if (this.providerConfig.whisper) { + this.providerConfig.whisper.sttModels.forEach(m => { + const installedInfo = installedWhisperModels.find(i => i.id === m.id); + if (installedInfo) { + m.installed = installedInfo.installed; + } + }); + } + } } catch (error) { console.error('Error loading initial settings data:', error); } finally { @@ -538,8 +640,52 @@ export class SettingsView extends LitElement { const input = this.shadowRoot.querySelector(`#key-input-${provider}`); if (!input) return; const key = input.value; + + // For Ollama, we need to ensure it's ready first + if (provider === 'ollama') { + this.saving = true; + const { ipcRenderer } = window.require('electron'); + + // First ensure Ollama is installed and running + const ensureResult = await ipcRenderer.invoke('ollama:ensure-ready'); + if (!ensureResult.success) { + alert(`Failed to setup Ollama: ${ensureResult.error}`); + this.saving = false; + return; + } + + // Now validate (which will check if service is running) + const result = await ipcRenderer.invoke('model:validate-key', { provider, key: 'local' }); + + if (result.success) { + this.apiKeys = { ...this.apiKeys, [provider]: 'local' }; + await this.refreshModelData(); + await this.refreshOllamaStatus(); + } else { + alert(`Failed to connect to Ollama: ${result.error}`); + } + this.saving = false; + return; + } + + // For Whisper, just enable it + if (provider === 'whisper') { + this.saving = true; + const { ipcRenderer } = window.require('electron'); + const result = await ipcRenderer.invoke('model:validate-key', { provider, key: 'local' }); + + if (result.success) { + this.apiKeys = { ...this.apiKeys, [provider]: 'local' }; + await this.refreshModelData(); + } else { + alert(`Failed to enable Whisper: ${result.error}`); + } + this.saving = false; + return; + } + + // For other providers, use the normal flow this.saving = true; - const { ipcRenderer } = window.require('electron'); const result = await ipcRenderer.invoke('model:validate-key', { provider, key }); @@ -564,15 +710,17 @@ export class SettingsView extends LitElement { async refreshModelData() { const { ipcRenderer } = window.require('electron'); - const [availableLlm, availableStt, selected] = await Promise.all([ + const [availableLlm, availableStt, selected, storedKeys] = await Promise.all([ ipcRenderer.invoke('model:get-available-models', { type: 'llm' }), ipcRenderer.invoke('model:get-available-models', { type: 'stt' }), - ipcRenderer.invoke('model:get-selected-models') + ipcRenderer.invoke('model:get-selected-models'), + ipcRenderer.invoke('model:get-all-keys') ]); this.availableLlmModels = availableLlm; this.availableSttModels = availableStt; this.selectedLlm = selected.llm; this.selectedStt = selected.stt; + this.apiKeys = storedKeys; this.requestUpdate(); } @@ -594,6 +742,28 @@ export class SettingsView extends LitElement { } async selectModel(type, modelId) { + // Check if this is an Ollama model that needs to be installed + const provider = this.getProviderForModel(type, modelId); + if (provider === 'ollama') { + const ollamaModel = this.ollamaModels.find(m => m.name === modelId); + if (ollamaModel && !ollamaModel.installed && !ollamaModel.installing) { + // Need to install the model first + await this.installOllamaModel(modelId); + return; + } + } + + // Check if this is a Whisper model that needs to be downloaded + if (provider === 'whisper' && type === 'stt') { + const isInstalling = this.installingModels[modelId] !== undefined; + const whisperModelInfo = this.providerConfig.whisper.sttModels.find(m => m.id === modelId); + + if (whisperModelInfo && !whisperModelInfo.installed && !isInstalling) { + await this.downloadWhisperModel(modelId); + return; + } + } + this.saving = true; const { ipcRenderer } = window.require('electron'); await ipcRenderer.invoke('model:set-selected-model', { type, modelId }); @@ -604,6 +774,102 @@ export class SettingsView extends LitElement { this.saving = false; this.requestUpdate(); } + + async refreshOllamaStatus() { + const { ipcRenderer } = window.require('electron'); + const ollamaStatus = await ipcRenderer.invoke('ollama:get-status'); + if (ollamaStatus?.success) { + this.ollamaStatus = { installed: ollamaStatus.installed, running: ollamaStatus.running }; + this.ollamaModels = ollamaStatus.models || []; + } + } + + async installOllamaModel(modelName) { + // Mark as installing + this.installingModels = { ...this.installingModels, [modelName]: 0 }; + this.requestUpdate(); + + try { + // Use the clean progress tracker - no manual event management needed + const success = await this.progressTracker.installModel(modelName, (progress) => { + this.installingModels = { ...this.installingModels, [modelName]: progress }; + this.requestUpdate(); + }); + + if (success) { + // Refresh status after installation + await this.refreshOllamaStatus(); + await this.refreshModelData(); + // Auto-select the model after installation + await this.selectModel('llm', modelName); + } else { + alert(`Installation of ${modelName} was cancelled`); + } + } catch (error) { + console.error(`[SettingsView] Error installing model ${modelName}:`, error); + alert(`Error installing ${modelName}: ${error.message}`); + } finally { + // Automatic cleanup - no manual event listener management + delete this.installingModels[modelName]; + this.requestUpdate(); + } + } + + async downloadWhisperModel(modelId) { + // Mark as installing + this.installingModels = { ...this.installingModels, [modelId]: 0 }; + this.requestUpdate(); + + try { + const { ipcRenderer } = window.require('electron'); + + // Set up progress listener + const progressHandler = (event, { modelId: id, progress }) => { + if (id === modelId) { + this.installingModels = { ...this.installingModels, [modelId]: progress }; + this.requestUpdate(); + } + }; + + ipcRenderer.on('whisper:download-progress', progressHandler); + + // Start download + const result = await ipcRenderer.invoke('whisper:download-model', modelId); + + if (result.success) { + // Auto-select the model after download + await this.selectModel('stt', modelId); + } else { + alert(`Failed to download Whisper model: ${result.error}`); + } + + // Cleanup + ipcRenderer.removeListener('whisper:download-progress', progressHandler); + } catch (error) { + console.error(`[SettingsView] Error downloading Whisper model ${modelId}:`, error); + alert(`Error downloading ${modelId}: ${error.message}`); + } finally { + delete this.installingModels[modelId]; + this.requestUpdate(); + } + } + + getProviderForModel(type, modelId) { + for (const [providerId, config] of Object.entries(this.providerConfig)) { + const models = type === 'llm' ? config.llmModels : config.sttModels; + if (models?.some(m => m.id === modelId)) { + return providerId; + } + } + return null; + } + + async handleWhisperModelSelect(modelId) { + if (!modelId) return; + + // Select the model (will trigger download if needed) + await this.selectModel('stt', modelId); + } handleUsePicklesKey(e) { e.preventDefault() @@ -637,6 +903,14 @@ export class SettingsView extends LitElement { this.cleanupEventListeners(); this.cleanupIpcListeners(); this.cleanupWindowResize(); + + // Cancel any ongoing Ollama installations when component is destroyed + const installingModels = Object.keys(this.installingModels); + if (installingModels.length > 0) { + installingModels.forEach(modelName => { + this.progressTracker.cancelInstallation(modelName); + }); + } } setupEventListeners() { @@ -892,6 +1166,36 @@ export class SettingsView extends LitElement { } } + async handleOllamaShutdown() { + console.log('[SettingsView] Shutting down Ollama service...'); + + if (!window.require) return; + + const { ipcRenderer } = window.require('electron'); + + try { + // Show loading state + this.ollamaStatus = { ...this.ollamaStatus, running: false }; + this.requestUpdate(); + + const result = await ipcRenderer.invoke('ollama:shutdown', false); // Graceful shutdown + + if (result.success) { + console.log('[SettingsView] Ollama shut down successfully'); + // Refresh status to reflect the change + await this.refreshOllamaStatus(); + } else { + console.error('[SettingsView] Failed to shutdown Ollama:', result.error); + // Restore previous state on error + await this.refreshOllamaStatus(); + } + } catch (error) { + console.error('[SettingsView] Error during Ollama shutdown:', error); + // Restore previous state on error + await this.refreshOllamaStatus(); + } + } + //////// before_modelStateService //////// // render() { @@ -1044,20 +1348,124 @@ export class SettingsView extends LitElement {
${Object.entries(this.providerConfig) .filter(([id, config]) => !id.includes('-glass')) - .map(([id, config]) => html` + .map(([id, config]) => { + if (id === 'ollama') { + // Special UI for Ollama + return html` +
+ + ${this.ollamaStatus.installed && this.ollamaStatus.running ? html` +
+ โœ“ Ollama is running +
+ + ` : this.ollamaStatus.installed ? html` +
+ โš  Ollama installed but not running +
+ + ` : html` +
+ โœ— Ollama not installed +
+ + `} +
+ `; + } + + if (id === 'whisper') { + // Special UI for Whisper with model selection + const whisperModels = config.sttModels || []; + const selectedWhisperModel = this.selectedStt && this.getProviderForModel('stt', this.selectedStt) === 'whisper' + ? this.selectedStt + : null; + + return html` +
+ + ${this.apiKeys[id] === 'local' ? html` +
+ โœ“ Whisper is enabled +
+ + + + + + ${Object.entries(this.installingModels).map(([modelId, progress]) => { + if (modelId.startsWith('whisper-') && progress !== undefined) { + return html` +
+
+ Downloading ${modelId}... +
+
+
+
+
+ `; + } + return null; + })} + + + ` : html` + + `} +
+ `; + } + + // Regular providers + return html`
- `)} + `; + })}
`; @@ -1076,11 +1484,30 @@ export class SettingsView extends LitElement { ${this.isLlmListVisible ? html`
- ${this.availableLlmModels.map(model => html` -
this.selectModel('llm', model.id)}> - ${model.name} + ${this.availableLlmModels.map(model => { + const isOllama = this.getProviderForModel('llm', model.id) === 'ollama'; + const ollamaModel = isOllama ? this.ollamaModels.find(m => m.name === model.id) : null; + const isInstalling = this.installingModels[model.id] !== undefined; + const installProgress = this.installingModels[model.id] || 0; + + return html` +
this.selectModel('llm', model.id)}> + ${model.name} + ${isOllama ? html` + ${isInstalling ? html` +
+
- `)} + ` : ollamaModel?.installed ? html` + โœ“ Installed + ` : html` + Click to install + `} + ` : ''} +
+ `; + })}
` : ''}
@@ -1091,11 +1518,23 @@ export class SettingsView extends LitElement { ${this.isSttListVisible ? html`
- ${this.availableSttModels.map(model => html` -
this.selectModel('stt', model.id)}> - ${model.name} -
- `)} + ${this.availableSttModels.map(model => { + const isWhisper = this.getProviderForModel('stt', model.id) === 'whisper'; + const isInstalling = this.installingModels[model.id] !== undefined; + const installProgress = this.installingModels[model.id] || 0; + + return html` +
this.selectModel('stt', model.id)}> + ${model.name} + ${isWhisper && isInstalling ? html` +
+
+
+ ` : ''} +
+ `; + })}
` : ''}
diff --git a/src/features/settings/repositories/firebase.repository.js b/src/features/settings/repositories/firebase.repository.js new file mode 100644 index 0000000..e007398 --- /dev/null +++ b/src/features/settings/repositories/firebase.repository.js @@ -0,0 +1,148 @@ +const { collection, doc, addDoc, getDoc, getDocs, updateDoc, deleteDoc, query, where, orderBy } = require('firebase/firestore'); +const { getFirestoreInstance } = require('../../../common/services/firebaseClient'); +const { createEncryptedConverter } = require('../../../common/repositories/firestoreConverter'); +const encryptionService = require('../../../common/services/encryptionService'); + +const userPresetConverter = createEncryptedConverter(['prompt', 'title']); + +const defaultPresetConverter = { + toFirestore: (data) => data, + fromFirestore: (snapshot, options) => { + const data = snapshot.data(options); + return { ...data, id: snapshot.id }; + } +}; + +function userPresetsCol() { + const db = getFirestoreInstance(); + return collection(db, 'prompt_presets').withConverter(userPresetConverter); +} + +function defaultPresetsCol() { + const db = getFirestoreInstance(); + return collection(db, 'defaults/v1/prompt_presets').withConverter(defaultPresetConverter); +} + +async function getPresets(uid) { + const userPresetsQuery = query(userPresetsCol(), where('uid', '==', uid)); + const defaultPresetsQuery = query(defaultPresetsCol()); + + const [userSnapshot, defaultSnapshot] = await Promise.all([ + getDocs(userPresetsQuery), + getDocs(defaultPresetsQuery) + ]); + + const presets = [ + ...defaultSnapshot.docs.map(d => d.data()), + ...userSnapshot.docs.map(d => d.data()) + ]; + + return presets.sort((a, b) => { + if (a.is_default && !b.is_default) return -1; + if (!a.is_default && b.is_default) return 1; + return a.title.localeCompare(b.title); + }); +} + +async function getPresetTemplates() { + const q = query(defaultPresetsCol(), orderBy('title', 'asc')); + const snapshot = await getDocs(q); + return snapshot.docs.map(doc => doc.data()); +} + +async function createPreset({ uid, title, prompt }) { + const now = Math.floor(Date.now() / 1000); + const newPreset = { + uid: uid, + title, + prompt, + is_default: 0, + created_at: now, + }; + const docRef = await addDoc(userPresetsCol(), newPreset); + return { id: docRef.id }; +} + +async function updatePreset(id, { title, prompt }, uid) { + const docRef = doc(userPresetsCol(), id); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists() || docSnap.data().uid !== uid || docSnap.data().is_default) { + throw new Error("Preset not found or permission denied to update."); + } + + const updates = {}; + if (title !== undefined) { + updates.title = encryptionService.encrypt(title); + } + if (prompt !== undefined) { + updates.prompt = encryptionService.encrypt(prompt); + } + updates.updated_at = Math.floor(Date.now() / 1000); + + await updateDoc(docRef, updates); + return { changes: 1 }; +} + +async function deletePreset(id, uid) { + const docRef = doc(userPresetsCol(), id); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists() || docSnap.data().uid !== uid || docSnap.data().is_default) { + throw new Error("Preset not found or permission denied to delete."); + } + + await deleteDoc(docRef); + return { changes: 1 }; +} + +async function getAutoUpdate(uid) { + // Assume users are stored in a "users" collection, and auto_update_enabled is a field + const userDocRef = doc(getFirestoreInstance(), 'users', uid); + try { + const userSnap = await getDoc(userDocRef); + if (userSnap.exists()) { + const data = userSnap.data(); + if (typeof data.auto_update_enabled !== 'undefined') { + console.log('Firebase: Auto update setting found:', data.auto_update_enabled); + return !!data.auto_update_enabled; + } else { + // Field does not exist, just return default + return true; + } + } else { + // User doc does not exist, just return default + return true; + } + } catch (error) { + console.error('Firebase: Error getting auto_update_enabled setting:', error); + return true; // fallback to enabled + } +} + +async function setAutoUpdate(uid, isEnabled) { + const userDocRef = doc(getFirestoreInstance(), 'users', uid); + try { + const userSnap = await getDoc(userDocRef); + if (userSnap.exists()) { + await updateDoc(userDocRef, { auto_update_enabled: !!isEnabled }); + } + // If user doc does not exist, do nothing (no creation) + return { success: true }; + } catch (error) { + console.error('Firebase: Error setting auto-update:', error); + return { success: false, error: error.message }; + } +} + + + +module.exports = { + getPresets, + getPresetTemplates, + createPreset, + updatePreset, + deletePreset, + getAutoUpdate, + setAutoUpdate, +}; \ No newline at end of file diff --git a/src/features/settings/repositories/index.js b/src/features/settings/repositories/index.js index 973a149..bca7a9b 100644 --- a/src/features/settings/repositories/index.js +++ b/src/features/settings/repositories/index.js @@ -1,23 +1,49 @@ const sqliteRepository = require('./sqlite.repository'); -// const firebaseRepository = require('./firebase.repository'); // Future implementation +const firebaseRepository = require('./firebase.repository'); const authService = require('../../../common/services/authService'); -function getRepository() { - // In the future, we can check the user's login status from authService - // const user = authService.getCurrentUser(); - // if (user.isLoggedIn) { - // return firebaseRepository; - // } +function getBaseRepository() { + const user = authService.getCurrentUser(); + if (user && user.isLoggedIn) { + return firebaseRepository; + } return sqliteRepository; } -// Directly export functions for ease of use, decided by the strategy -module.exports = { - getPresets: (...args) => getRepository().getPresets(...args), - getPresetTemplates: (...args) => getRepository().getPresetTemplates(...args), - createPreset: (...args) => getRepository().createPreset(...args), - updatePreset: (...args) => getRepository().updatePreset(...args), - deletePreset: (...args) => getRepository().deletePreset(...args), - getAutoUpdate: (...args) => getRepository().getAutoUpdate(...args), - setAutoUpdate: (...args) => getRepository().setAutoUpdate(...args), -}; \ No newline at end of file +const settingsRepositoryAdapter = { + getPresets: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getPresets(uid); + }, + + getPresetTemplates: () => { + return getBaseRepository().getPresetTemplates(); + }, + + createPreset: (options) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().createPreset({ uid, ...options }); + }, + + updatePreset: (id, options) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().updatePreset(id, options, uid); + }, + + deletePreset: (id) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().deletePreset(id, uid); + }, + + getAutoUpdate: () => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().getAutoUpdate(uid); + }, + + setAutoUpdate: (isEnabled) => { + const uid = authService.getCurrentUserId(); + return getBaseRepository().setAutoUpdate(uid, isEnabled); + }, +}; + +module.exports = settingsRepositoryAdapter; diff --git a/src/features/settings/settingsService.js b/src/features/settings/settingsService.js index 76b0d1a..801138a 100644 --- a/src/features/settings/settingsService.js +++ b/src/features/settings/settingsService.js @@ -208,13 +208,8 @@ async function saveSettings(settings) { async function getPresets() { try { - const uid = authService.getCurrentUserId(); - if (!uid) { - // Logged out users only see default presets - return await settingsRepository.getPresetTemplates(); - } - - const presets = await settingsRepository.getPresets(uid); + // The adapter now handles which presets to return based on login state. + const presets = await settingsRepository.getPresets(); return presets; } catch (error) { console.error('[SettingsService] Error getting presets:', error); @@ -234,12 +229,8 @@ async function getPresetTemplates() { async function createPreset(title, prompt) { try { - const uid = authService.getCurrentUserId(); - if (!uid) { - throw new Error("User not logged in, cannot create preset."); - } - - const result = await settingsRepository.createPreset({ uid, title, prompt }); + // The adapter injects the UID. + const result = await settingsRepository.createPreset({ title, prompt }); windowNotificationManager.notifyRelevantWindows('presets-updated', { action: 'created', @@ -256,12 +247,8 @@ async function createPreset(title, prompt) { async function updatePreset(id, title, prompt) { try { - const uid = authService.getCurrentUserId(); - if (!uid) { - throw new Error("User not logged in, cannot update preset."); - } - - await settingsRepository.updatePreset(id, { title, prompt }, uid); + // The adapter injects the UID. + await settingsRepository.updatePreset(id, { title, prompt }); windowNotificationManager.notifyRelevantWindows('presets-updated', { action: 'updated', @@ -278,12 +265,8 @@ async function updatePreset(id, title, prompt) { async function deletePreset(id) { try { - const uid = authService.getCurrentUserId(); - if (!uid) { - throw new Error("User not logged in, cannot delete preset."); - } - - await settingsRepository.deletePreset(id, uid); + // The adapter injects the UID. + await settingsRepository.deletePreset(id); windowNotificationManager.notifyRelevantWindows('presets-updated', { action: 'deleted', @@ -299,10 +282,9 @@ async function deletePreset(id) { async function saveApiKey(apiKey, provider = 'openai') { try { - const uid = authService.getCurrentUserId(); - if (!uid) { + const user = authService.getCurrentUser(); + if (!user.isLoggedIn) { // For non-logged-in users, save to local storage - const { app } = require('electron'); const Store = require('electron-store'); const store = new Store(); store.set('apiKey', apiKey); @@ -318,8 +300,8 @@ async function saveApiKey(apiKey, provider = 'openai') { return { success: true }; } - // For logged-in users, save to database - await userRepository.saveApiKey(apiKey, uid, provider); + // For logged-in users, use the repository adapter which injects the UID. + await userRepository.saveApiKey(apiKey, provider); // Notify windows BrowserWindow.getAllWindows().forEach(win => { @@ -337,17 +319,16 @@ async function saveApiKey(apiKey, provider = 'openai') { async function removeApiKey() { try { - const uid = authService.getCurrentUserId(); - if (!uid) { + const user = authService.getCurrentUser(); + if (!user.isLoggedIn) { // For non-logged-in users, remove from local storage - const { app } = require('electron'); const Store = require('electron-store'); const store = new Store(); store.delete('apiKey'); store.delete('provider'); } else { - // For logged-in users, remove from database - await userRepository.saveApiKey(null, uid, null); + // For logged-in users, use the repository adapter. + await userRepository.saveApiKey(null, null); } // Notify windows @@ -385,10 +366,7 @@ async function updateContentProtection(enabled) { async function getAutoUpdateSetting() { try { - const uid = authService.getCurrentUserId(); - // This can be awaited if the repository returns a promise. - // Assuming it's synchronous for now based on original structure. - return settingsRepository.getAutoUpdate(uid); + return settingsRepository.getAutoUpdate(); } catch (error) { console.error('[SettingsService] Error getting auto update setting:', error); return true; // Fallback to enabled @@ -397,8 +375,7 @@ async function getAutoUpdateSetting() { async function setAutoUpdateSetting(isEnabled) { try { - const uid = authService.getCurrentUserId(); - await settingsRepository.setAutoUpdate(uid, isEnabled); + await settingsRepository.setAutoUpdate(isEnabled); return { success: true }; } catch (error) { console.error('[SettingsService] Error setting auto update setting:', error); diff --git a/src/index.js b/src/index.js index 6015c26..22a06f5 100644 --- a/src/index.js +++ b/src/index.js @@ -28,8 +28,10 @@ const sessionRepository = require('./common/repositories/session'); const ModelStateService = require('./common/services/modelStateService'); const sqliteClient = require('./common/services/sqliteClient'); +// Global variables const eventBridge = new EventEmitter(); let WEB_PORT = 3000; +let isShuttingDown = false; // Flag to prevent infinite shutdown loop const listenService = new ListenService(); // Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance @@ -40,6 +42,10 @@ const modelStateService = new ModelStateService(authService); global.modelStateService = modelStateService; //////// after_modelStateService //////// +// Import and initialize OllamaService +const ollamaService = require('./common/services/ollamaService'); +const ollamaModelRepository = require('./common/repositories/ollamaModel'); + // Native deep link handling - cross-platform compatible let pendingDeepLinkUrl = null; @@ -187,8 +193,8 @@ app.whenReady().then(async () => { await databaseInitializer.initialize(); console.log('>>> [index.js] Database initialized successfully'); - // Clean up zombie sessions from previous runs first - sessionRepository.endAllActiveSessions(); + // Clean up zombie sessions from previous runs first - MOVED TO authService + // sessionRepository.endAllActiveSessions(); await authService.initialize(); @@ -200,6 +206,21 @@ app.whenReady().then(async () => { askService.initialize(); settingsService.initialize(); setupGeneralIpcHandlers(); + setupOllamaIpcHandlers(); + setupWhisperIpcHandlers(); + + // Initialize Ollama models in database + await ollamaModelRepository.initializeDefaultModels(); + + // Auto warm-up selected Ollama model in background (non-blocking) + setTimeout(async () => { + try { + console.log('[index.js] Starting background Ollama model warm-up...'); + await ollamaService.autoWarmUpSelectedModel(); + } catch (error) { + console.log('[index.js] Background warm-up failed (non-critical):', error.message); + } + }, 2000); // Wait 2 seconds after app start // Start web server and create windows ONLY after all initializations are successful WEB_PORT = await startWebStack(); @@ -234,11 +255,71 @@ app.on('window-all-closed', () => { } }); -app.on('before-quit', async () => { - console.log('[Shutdown] App is about to quit.'); - listenService.stopMacOSAudioCapture(); - await sessionRepository.endAllActiveSessions(); - databaseInitializer.close(); +app.on('before-quit', async (event) => { + // Prevent infinite loop by checking if shutdown is already in progress + if (isShuttingDown) { + console.log('[Shutdown] ๐Ÿ”„ Shutdown already in progress, allowing quit...'); + return; + } + + console.log('[Shutdown] App is about to quit. Starting graceful shutdown...'); + + // Set shutdown flag to prevent infinite loop + isShuttingDown = true; + + // Prevent immediate quit to allow graceful shutdown + event.preventDefault(); + + try { + // 1. Stop audio capture first (immediate) + listenService.stopMacOSAudioCapture(); + console.log('[Shutdown] Audio capture stopped'); + + // 2. End all active sessions (database operations) - with error handling + try { + await sessionRepository.endAllActiveSessions(); + console.log('[Shutdown] Active sessions ended'); + } catch (dbError) { + console.warn('[Shutdown] Could not end active sessions (database may be closed):', dbError.message); + } + + // 3. Shutdown Ollama service (potentially time-consuming) + console.log('[Shutdown] shutting down Ollama service...'); + const ollamaShutdownSuccess = await Promise.race([ + ollamaService.shutdown(false), // Graceful shutdown + new Promise(resolve => setTimeout(() => resolve(false), 8000)) // 8s timeout + ]); + + if (ollamaShutdownSuccess) { + console.log('[Shutdown] Ollama service shut down gracefully'); + } else { + console.log('[Shutdown] Ollama shutdown timeout, forcing...'); + // Force shutdown if graceful failed + try { + await ollamaService.shutdown(true); + } catch (forceShutdownError) { + console.warn('[Shutdown] Force shutdown also failed:', forceShutdownError.message); + } + } + + // 4. Close database connections (final cleanup) + try { + databaseInitializer.close(); + console.log('[Shutdown] Database connections closed'); + } catch (closeError) { + console.warn('[Shutdown] Error closing database:', closeError.message); + } + + console.log('[Shutdown] Graceful shutdown completed successfully'); + + } catch (error) { + console.error('[Shutdown] Error during graceful shutdown:', error); + // Continue with shutdown even if there were errors + } finally { + // Actually quit the app now + console.log('[Shutdown] Exiting application...'); + app.exit(0); // Use app.exit() instead of app.quit() to force quit + } }); app.on('activate', () => { @@ -247,13 +328,79 @@ app.on('activate', () => { } }); +function setupWhisperIpcHandlers() { + const { WhisperService } = require('./common/services/whisperService'); + const whisperService = new WhisperService(); + + // Forward download progress events to renderer + whisperService.on('downloadProgress', (data) => { + const windows = BrowserWindow.getAllWindows(); + windows.forEach(window => { + window.webContents.send('whisper:download-progress', data); + }); + }); + + // IPC handlers for Whisper operations + ipcMain.handle('whisper:download-model', async (event, modelId) => { + try { + console.log(`[Whisper IPC] Starting download for model: ${modelId}`); + + // Ensure WhisperService is initialized first + if (!whisperService.isInitialized) { + console.log('[Whisper IPC] Initializing WhisperService...'); + await whisperService.initialize(); + } + + // Set up progress listener + const progressHandler = (data) => { + if (data.modelId === modelId) { + event.sender.send('whisper:download-progress', data); + } + }; + + whisperService.on('downloadProgress', progressHandler); + + try { + await whisperService.ensureModelAvailable(modelId); + console.log(`[Whisper IPC] Model ${modelId} download completed successfully`); + } finally { + // Cleanup listener + whisperService.removeListener('downloadProgress', progressHandler); + } + + return { success: true }; + } catch (error) { + console.error(`[Whisper IPC] Failed to download model ${modelId}:`, error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('whisper:get-installed-models', async () => { + try { + // Ensure WhisperService is initialized first + if (!whisperService.isInitialized) { + console.log('[Whisper IPC] Initializing WhisperService for model list...'); + await whisperService.initialize(); + } + + const models = await whisperService.getInstalledModels(); + return { success: true, models }; + } catch (error) { + console.error('[Whisper IPC] Failed to get installed models:', error); + return { success: false, error: error.message }; + } + }); +} + function setupGeneralIpcHandlers() { const userRepository = require('./common/repositories/user'); const presetRepository = require('./common/repositories/preset'); ipcMain.handle('save-api-key', (event, apiKey) => { try { - userRepository.saveApiKey(apiKey, authService.getCurrentUserId()); + // The adapter injects the UID and handles local/firebase logic. + // Assuming a default provider if not specified. + userRepository.saveApiKey(apiKey, 'openai'); BrowserWindow.getAllWindows().forEach(win => { win.webContents.send('api-key-updated'); }); @@ -265,7 +412,8 @@ function setupGeneralIpcHandlers() { }); ipcMain.handle('get-user-presets', () => { - return presetRepository.getPresets(authService.getCurrentUserId()); + // The adapter injects the UID. + return presetRepository.getPresets(); }); ipcMain.handle('get-preset-templates', () => { @@ -296,6 +444,201 @@ function setupGeneralIpcHandlers() { setupWebDataHandlers(); } +function setupOllamaIpcHandlers() { + // Ollama status and installation + ipcMain.handle('ollama:get-status', async () => { + try { + const installed = await ollamaService.isInstalled(); + const running = installed ? await ollamaService.isServiceRunning() : false; + const models = await ollamaService.getAllModelsWithStatus(); + + return { + installed, + running, + models, + success: true + }; + } catch (error) { + console.error('[Ollama IPC] Failed to get status:', error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('ollama:install', async (event) => { + try { + const onProgress = (data) => { + event.sender.send('ollama:install-progress', data); + }; + + await ollamaService.autoInstall(onProgress); + + if (!await ollamaService.isServiceRunning()) { + onProgress({ stage: 'starting', message: 'Starting Ollama service...', progress: 0 }); + await ollamaService.startService(); + onProgress({ stage: 'starting', message: 'Ollama service started.', progress: 100 }); + } + event.sender.send('ollama:install-complete', { success: true }); + return { success: true }; + } catch (error) { + console.error('[Ollama IPC] Failed to install:', error); + event.sender.send('ollama:install-complete', { success: false, error: error.message }); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('ollama:start-service', async (event) => { + try { + if (!await ollamaService.isServiceRunning()) { + console.log('[Ollama IPC] Starting Ollama service...'); + await ollamaService.startService(); + } + event.sender.send('ollama:install-complete', { success: true }); + return { success: true }; + } catch (error) { + console.error('[Ollama IPC] Failed to start service:', error); + event.sender.send('ollama:install-complete', { success: false, error: error.message }); + return { success: false, error: error.message }; + } + }); + + // Ensure Ollama is ready (starts service if installed but not running) + ipcMain.handle('ollama:ensure-ready', async () => { + try { + if (await ollamaService.isInstalled() && !await ollamaService.isServiceRunning()) { + console.log('[Ollama IPC] Ollama installed but not running, starting service...'); + await ollamaService.startService(); + } + return { success: true }; + } catch (error) { + console.error('[Ollama IPC] Failed to ensure ready:', error); + return { success: false, error: error.message }; + } + }); + + // Get all models with their status + ipcMain.handle('ollama:get-models', async () => { + try { + const models = await ollamaService.getAllModelsWithStatus(); + return { success: true, models }; + } catch (error) { + console.error('[Ollama IPC] Failed to get models:', error); + return { success: false, error: error.message }; + } + }); + + // Get model suggestions for autocomplete + ipcMain.handle('ollama:get-model-suggestions', async () => { + try { + const suggestions = await ollamaService.getModelSuggestions(); + return { success: true, suggestions }; + } catch (error) { + console.error('[Ollama IPC] Failed to get model suggestions:', error); + return { success: false, error: error.message }; + } + }); + + // Pull/install a specific model + ipcMain.handle('ollama:pull-model', async (event, modelName) => { + try { + console.log(`[Ollama IPC] Starting model pull: ${modelName}`); + + // Update DB status to installing + await ollamaModelRepository.updateInstallStatus(modelName, false, true); + + // Set up progress listener for real-time updates + const progressHandler = (data) => { + if (data.model === modelName) { + event.sender.send('ollama:pull-progress', data); + } + }; + + const completeHandler = (data) => { + if (data.model === modelName) { + console.log(`[Ollama IPC] Model ${modelName} pull completed`); + // Clean up listeners + ollamaService.removeListener('pull-progress', progressHandler); + ollamaService.removeListener('pull-complete', completeHandler); + } + }; + + ollamaService.on('pull-progress', progressHandler); + ollamaService.on('pull-complete', completeHandler); + + // Pull the model using REST API + await ollamaService.pullModel(modelName); + + // Update DB status to installed + await ollamaModelRepository.updateInstallStatus(modelName, true, false); + + console.log(`[Ollama IPC] Model ${modelName} pull successful`); + return { success: true }; + } catch (error) { + console.error('[Ollama IPC] Failed to pull model:', error); + // Reset status on error + await ollamaModelRepository.updateInstallStatus(modelName, false, false); + return { success: false, error: error.message }; + } + }); + + // Check if a specific model is installed + ipcMain.handle('ollama:is-model-installed', async (event, modelName) => { + try { + const installed = await ollamaService.isModelInstalled(modelName); + return { success: true, installed }; + } catch (error) { + console.error('[Ollama IPC] Failed to check model installation:', error); + return { success: false, error: error.message }; + } + }); + + // Warm up a specific model + ipcMain.handle('ollama:warm-up-model', async (event, modelName) => { + try { + const success = await ollamaService.warmUpModel(modelName); + return { success }; + } catch (error) { + console.error('[Ollama IPC] Failed to warm up model:', error); + return { success: false, error: error.message }; + } + }); + + // Auto warm-up currently selected model + ipcMain.handle('ollama:auto-warm-up', async () => { + try { + const success = await ollamaService.autoWarmUpSelectedModel(); + return { success }; + } catch (error) { + console.error('[Ollama IPC] Failed to auto warm-up:', error); + return { success: false, error: error.message }; + } + }); + + // Get warm-up status for debugging + ipcMain.handle('ollama:get-warm-up-status', async () => { + try { + const status = ollamaService.getWarmUpStatus(); + return { success: true, status }; + } catch (error) { + console.error('[Ollama IPC] Failed to get warm-up status:', error); + return { success: false, error: error.message }; + } + }); + + // Shutdown Ollama service manually + ipcMain.handle('ollama:shutdown', async (event, force = false) => { + try { + console.log(`[Ollama IPC] Manual shutdown requested (force: ${force})`); + const success = await ollamaService.shutdown(force); + return { success }; + } catch (error) { + console.error('[Ollama IPC] Failed to shutdown Ollama:', error); + return { success: false, error: error.message }; + } + }); + + console.log('[Ollama IPC] Handlers registered'); +} + function setupWebDataHandlers() { const sessionRepository = require('./common/repositories/session'); const sttRepository = require('./features/listen/stt/repositories'); @@ -304,89 +647,112 @@ function setupWebDataHandlers() { const userRepository = require('./common/repositories/user'); const presetRepository = require('./common/repositories/preset'); - const handleRequest = (channel, responseChannel, payload) => { + const handleRequest = async (channel, responseChannel, payload) => { let result; - const currentUserId = authService.getCurrentUserId(); + // const currentUserId = authService.getCurrentUserId(); // No longer needed here try { switch (channel) { // SESSION case 'get-sessions': - result = sessionRepository.getAllByUserId(currentUserId); + // Adapter injects UID + result = await sessionRepository.getAllByUserId(); break; case 'get-session-details': - const session = sessionRepository.getById(payload); + const session = await sessionRepository.getById(payload); if (!session) { result = null; break; } - const transcripts = sttRepository.getAllTranscriptsBySessionId(payload); - const ai_messages = askRepository.getAllAiMessagesBySessionId(payload); - const summary = summaryRepository.getSummaryBySessionId(payload); + const [transcripts, ai_messages, summary] = await Promise.all([ + sttRepository.getAllTranscriptsBySessionId(payload), + askRepository.getAllAiMessagesBySessionId(payload), + summaryRepository.getSummaryBySessionId(payload) + ]); result = { session, transcripts, ai_messages, summary }; break; case 'delete-session': - result = sessionRepository.deleteWithRelatedData(payload); + result = await sessionRepository.deleteWithRelatedData(payload); break; case 'create-session': - const id = sessionRepository.create(currentUserId, 'ask'); - if (payload.title) { - sessionRepository.updateTitle(id, payload.title); + // Adapter injects UID + const id = await sessionRepository.create('ask'); + if (payload && payload.title) { + await sessionRepository.updateTitle(id, payload.title); } result = { id }; break; // USER case 'get-user-profile': - result = userRepository.getById(currentUserId); + // Adapter injects UID + result = await userRepository.getById(); break; case 'update-user-profile': - result = userRepository.update({ uid: currentUserId, ...payload }); + // Adapter injects UID + result = await userRepository.update(payload); break; case 'find-or-create-user': - result = userRepository.findOrCreate(payload); + result = await userRepository.findOrCreate(payload); break; case 'save-api-key': - result = userRepository.saveApiKey(payload, currentUserId); + // Assuming payload is { apiKey, provider } + result = await userRepository.saveApiKey(payload.apiKey, payload.provider); break; case 'check-api-key-status': - const user = userRepository.getById(currentUserId); + // Adapter injects UID + const user = await userRepository.getById(); result = { hasApiKey: !!user?.api_key && user.api_key.length > 0 }; break; case 'delete-account': - result = userRepository.deleteById(currentUserId); + // Adapter injects UID + result = await userRepository.deleteById(); break; // PRESET case 'get-presets': - result = presetRepository.getPresets(currentUserId); + // Adapter injects UID + result = await presetRepository.getPresets(); break; case 'create-preset': - result = presetRepository.create({ ...payload, uid: currentUserId }); + // Adapter injects UID + result = await presetRepository.create(payload); settingsService.notifyPresetUpdate('created', result.id, payload.title); break; case 'update-preset': - result = presetRepository.update(payload.id, payload.data, currentUserId); + // Adapter injects UID + result = await presetRepository.update(payload.id, payload.data); settingsService.notifyPresetUpdate('updated', payload.id, payload.data.title); break; case 'delete-preset': - result = presetRepository.delete(payload, currentUserId); + // Adapter injects UID + result = await presetRepository.delete(payload); settingsService.notifyPresetUpdate('deleted', payload); break; // BATCH case 'get-batch-data': const includes = payload ? payload.split(',').map(item => item.trim()) : ['profile', 'presets', 'sessions']; - const batchResult = {}; + const promises = {}; if (includes.includes('profile')) { - batchResult.profile = userRepository.getById(currentUserId); + // Adapter injects UID + promises.profile = userRepository.getById(); } if (includes.includes('presets')) { - batchResult.presets = presetRepository.getPresets(currentUserId); + // Adapter injects UID + promises.presets = presetRepository.getPresets(); } if (includes.includes('sessions')) { - batchResult.sessions = sessionRepository.getAllByUserId(currentUserId); + // Adapter injects UID + promises.sessions = sessionRepository.getAllByUserId(); } + + const batchResult = {}; + const promiseResults = await Promise.all(Object.values(promises)); + Object.keys(promises).forEach((key, index) => { + batchResult[key] = promiseResults[index]; + }); + result = batchResult; break;