mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 01:28:35 +02:00
Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
# Claude Context: Detaching Tauri from Yaak
|
# Claude Context: Detaching Tauri from Yaak
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
Make Yaak runnable as a standalone CLI without Tauri as a dependency. The core Rust crates in `crates/` should be usable independently, while Tauri-specific code lives in `crates-tauri/`.
|
Make Yaak runnable as a standalone CLI without Tauri as a dependency. The core Rust crates in `crates/` should be usable independently, while Tauri-specific code lives in `crates-tauri/`.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
crates/ # Core crates - should NOT depend on Tauri
|
crates/ # Core crates - should NOT depend on Tauri
|
||||||
crates-tauri/ # Tauri-specific crates (yaak-app, yaak-tauri-utils, etc.)
|
crates-tauri/ # Tauri-specific crates (yaak-app, yaak-tauri-utils, etc.)
|
||||||
@@ -13,11 +15,13 @@ crates-cli/ # CLI crate (yaak-cli)
|
|||||||
## Completed Work
|
## Completed Work
|
||||||
|
|
||||||
### 1. Folder Restructure
|
### 1. Folder Restructure
|
||||||
|
|
||||||
- Moved Tauri-dependent app code to `crates-tauri/yaak-app/`
|
- Moved Tauri-dependent app code to `crates-tauri/yaak-app/`
|
||||||
- Created `crates-tauri/yaak-tauri-utils/` for shared Tauri utilities (window traits, api_client, error handling)
|
- Created `crates-tauri/yaak-tauri-utils/` for shared Tauri utilities (window traits, api_client, error handling)
|
||||||
- Created `crates-cli/yaak-cli/` for the standalone CLI
|
- Created `crates-cli/yaak-cli/` for the standalone CLI
|
||||||
|
|
||||||
### 2. Decoupled Crates (no longer depend on Tauri)
|
### 2. Decoupled Crates (no longer depend on Tauri)
|
||||||
|
|
||||||
- **yaak-models**: Uses `init_standalone()` pattern for CLI database access
|
- **yaak-models**: Uses `init_standalone()` pattern for CLI database access
|
||||||
- **yaak-http**: Removed Tauri plugin, HttpConnectionManager initialized in yaak-app setup
|
- **yaak-http**: Removed Tauri plugin, HttpConnectionManager initialized in yaak-app setup
|
||||||
- **yaak-common**: Only contains Tauri-free utilities (serde, platform)
|
- **yaak-common**: Only contains Tauri-free utilities (serde, platform)
|
||||||
@@ -25,6 +29,7 @@ crates-cli/ # CLI crate (yaak-cli)
|
|||||||
- **yaak-grpc**: Replaced AppHandle with GrpcConfig struct, uses tokio::process::Command instead of Tauri sidecar
|
- **yaak-grpc**: Replaced AppHandle with GrpcConfig struct, uses tokio::process::Command instead of Tauri sidecar
|
||||||
|
|
||||||
### 3. CLI Implementation
|
### 3. CLI Implementation
|
||||||
|
|
||||||
- Basic CLI at `crates-cli/yaak-cli/src/main.rs`
|
- Basic CLI at `crates-cli/yaak-cli/src/main.rs`
|
||||||
- Commands: workspaces, requests, send (by ID), get (ad-hoc URL), create
|
- Commands: workspaces, requests, send (by ID), get (ad-hoc URL), create
|
||||||
- Uses same database as Tauri app via `yaak_models::init_standalone()`
|
- Uses same database as Tauri app via `yaak_models::init_standalone()`
|
||||||
@@ -32,12 +37,14 @@ crates-cli/ # CLI crate (yaak-cli)
|
|||||||
## Remaining Work
|
## Remaining Work
|
||||||
|
|
||||||
### Crates Still Depending on Tauri (in `crates/`)
|
### Crates Still Depending on Tauri (in `crates/`)
|
||||||
|
|
||||||
1. **yaak-git** (3 files) - Moderate complexity
|
1. **yaak-git** (3 files) - Moderate complexity
|
||||||
2. **yaak-plugins** (13 files) - **Hardest** - deeply integrated with Tauri for plugin-to-window communication
|
2. **yaak-plugins** (13 files) - **Hardest** - deeply integrated with Tauri for plugin-to-window communication
|
||||||
3. **yaak-sync** (4 files) - Moderate complexity
|
3. **yaak-sync** (4 files) - Moderate complexity
|
||||||
4. **yaak-ws** (5 files) - Moderate complexity
|
4. **yaak-ws** (5 files) - Moderate complexity
|
||||||
|
|
||||||
### Pattern for Decoupling
|
### Pattern for Decoupling
|
||||||
|
|
||||||
1. Remove Tauri plugin `init()` function from the crate
|
1. Remove Tauri plugin `init()` function from the crate
|
||||||
2. Move commands to `yaak-app/src/commands.rs` or keep inline in `lib.rs`
|
2. Move commands to `yaak-app/src/commands.rs` or keep inline in `lib.rs`
|
||||||
3. Move extension traits (e.g., `SomethingManagerExt`) to yaak-app or yaak-tauri-utils
|
3. Move extension traits (e.g., `SomethingManagerExt`) to yaak-app or yaak-tauri-utils
|
||||||
@@ -47,6 +54,7 @@ crates-cli/ # CLI crate (yaak-cli)
|
|||||||
7. Replace `tauri::async_runtime::block_on` with `tokio::runtime::Handle::current().block_on()`
|
7. Replace `tauri::async_runtime::block_on` with `tokio::runtime::Handle::current().block_on()`
|
||||||
|
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
||||||
- `crates-tauri/yaak-app/src/lib.rs` - Main Tauri app, setup block initializes managers
|
- `crates-tauri/yaak-app/src/lib.rs` - Main Tauri app, setup block initializes managers
|
||||||
- `crates-tauri/yaak-app/src/commands.rs` - Migrated Tauri commands
|
- `crates-tauri/yaak-app/src/commands.rs` - Migrated Tauri commands
|
||||||
- `crates-tauri/yaak-app/src/models_ext.rs` - Database plugin and extension traits
|
- `crates-tauri/yaak-app/src/models_ext.rs` - Database plugin and extension traits
|
||||||
@@ -54,9 +62,11 @@ crates-cli/ # CLI crate (yaak-cli)
|
|||||||
- `crates/yaak-models/src/lib.rs` - Contains `init_standalone()` for CLI usage
|
- `crates/yaak-models/src/lib.rs` - Contains `init_standalone()` for CLI usage
|
||||||
|
|
||||||
## Git Branch
|
## Git Branch
|
||||||
|
|
||||||
Working on `detach-tauri` branch.
|
Working on `detach-tauri` branch.
|
||||||
|
|
||||||
## Recent Commits
|
## Recent Commits
|
||||||
|
|
||||||
```
|
```
|
||||||
c40cff40 Remove Tauri dependencies from yaak-crypto and yaak-grpc
|
c40cff40 Remove Tauri dependencies from yaak-crypto and yaak-grpc
|
||||||
df495f1d Move Tauri utilities from yaak-common to yaak-tauri-utils
|
df495f1d Move Tauri utilities from yaak-common to yaak-tauri-utils
|
||||||
@@ -67,6 +77,7 @@ e718a5f1 Refactor models_ext to use init_standalone from yaak-models
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
- Run `cargo check -p <crate>` to verify a crate builds without Tauri
|
- Run `cargo check -p <crate>` to verify a crate builds without Tauri
|
||||||
- Run `npm run app-dev` to test the Tauri app still works
|
- Run `npm run app-dev` to test the Tauri app still works
|
||||||
- Run `cargo run -p yaak-cli -- --help` to test the CLI
|
- Run `cargo run -p yaak-cli -- --help` to test the CLI
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Generate formatted release notes for Yaak releases by analyzing git history and
|
|||||||
## What to do
|
## What to do
|
||||||
|
|
||||||
1. Identifies the version tag and previous version
|
1. Identifies the version tag and previous version
|
||||||
2. Retrieves all commits between versions
|
2. Retrieves all commits between versions
|
||||||
- If the version is a beta version, it retrieves commits between the beta version and previous beta version
|
- If the version is a beta version, it retrieves commits between the beta version and previous beta version
|
||||||
- If the version is a stable version, it retrieves commits between the stable version and the previous stable version
|
- If the version is a stable version, it retrieves commits between the stable version and the previous stable version
|
||||||
3. Fetches PR descriptions for linked issues to find:
|
3. Fetches PR descriptions for linked issues to find:
|
||||||
|
|||||||
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ""
|
||||||
labels: ''
|
labels: ""
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
|||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
|
|||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
- OS: [e.g. iOS]
|
||||||
- Version [e.g. 22]
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
**Smartphone (please complete the following information):**
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- OS: [e.g. iOS8.1]
|
- Device: [e.g. iPhone6]
|
||||||
- Browser [e.g. stock browser, safari]
|
- OS: [e.g. iOS8.1]
|
||||||
- Version [e.g. 22]
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|||||||
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -11,6 +11,7 @@
|
|||||||
- [ ] I added or updated tests when reasonable.
|
- [ ] I added or updated tests when reasonable.
|
||||||
|
|
||||||
Approved feedback item (required if not a bug fix or small-scope improvement):
|
Approved feedback item (required if not a bug fix or small-scope improvement):
|
||||||
|
|
||||||
<!-- https://yaak.app/feedback/... -->
|
<!-- https://yaak.app/feedback/... -->
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|||||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: voidzero-dev/setup-vp@v1
|
- uses: voidzero-dev/setup-vp@v1
|
||||||
with:
|
with:
|
||||||
node-version: '24'
|
node-version: "24"
|
||||||
cache: true
|
cache: true
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|||||||
1
.github/workflows/claude.yml
vendored
1
.github/workflows/claude.yml
vendored
@@ -47,4 +47,3 @@ jobs:
|
|||||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||||
# or https://code.claude.com/docs/en/cli-reference for available options
|
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||||
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/release-app.yml
vendored
2
.github/workflows/release-app.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
- name: Setup Vite+
|
- name: Setup Vite+
|
||||||
uses: voidzero-dev/setup-vp@v1
|
uses: voidzero-dev/setup-vp@v1
|
||||||
with:
|
with:
|
||||||
node-version: '24'
|
node-version: "24"
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: install Rust stable
|
- name: install Rust stable
|
||||||
|
|||||||
10
.github/workflows/sponsors.yml
vendored
10
.github/workflows/sponsors.yml
vendored
@@ -16,23 +16,23 @@ jobs:
|
|||||||
uses: JamesIves/github-sponsors-readme-action@v1
|
uses: JamesIves/github-sponsors-readme-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SPONSORS_PAT }}
|
token: ${{ secrets.SPONSORS_PAT }}
|
||||||
file: 'README.md'
|
file: "README.md"
|
||||||
maximum: 1999
|
maximum: 1999
|
||||||
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="50px" alt="User avatar: {{{ login }}}" /></a> '
|
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="50px" alt="User avatar: {{{ login }}}" /></a> '
|
||||||
active-only: false
|
active-only: false
|
||||||
include-private: true
|
include-private: true
|
||||||
marker: 'sponsors-base'
|
marker: "sponsors-base"
|
||||||
|
|
||||||
- name: Generate Sponsors
|
- name: Generate Sponsors
|
||||||
uses: JamesIves/github-sponsors-readme-action@v1
|
uses: JamesIves/github-sponsors-readme-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SPONSORS_PAT }}
|
token: ${{ secrets.SPONSORS_PAT }}
|
||||||
file: 'README.md'
|
file: "README.md"
|
||||||
minimum: 2000
|
minimum: 2000
|
||||||
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="80px" alt="User avatar: {{{ login }}}" /></a> '
|
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="80px" alt="User avatar: {{{ login }}}" /></a> '
|
||||||
active-only: false
|
active-only: false
|
||||||
include-private: true
|
include-private: true
|
||||||
marker: 'sponsors-premium'
|
marker: "sponsors-premium"
|
||||||
|
|
||||||
# ⚠️ Note: You can use any deployment step here to automatically push the README
|
# ⚠️ Note: You can use any deployment step here to automatically push the README
|
||||||
# changes back to your branch.
|
# changes back to your branch.
|
||||||
@@ -41,4 +41,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
branch: main
|
branch: main
|
||||||
force: false
|
force: false
|
||||||
folder: '.'
|
folder: "."
|
||||||
|
|||||||
2
.oxfmtignore
Normal file
2
.oxfmtignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
**/bindings/**
|
||||||
|
crates/yaak-templates/pkg/**
|
||||||
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@@ -1,3 +1,7 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["rust-lang.rust-analyzer", "bradlc.vscode-tailwindcss", "VoidZero.vite-plus-extension-pack"]
|
"recommendations": [
|
||||||
|
"rust-lang.rust-analyzer",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"VoidZero.vite-plus-extension-pack"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
48
Cargo.toml
48
Cargo.toml
@@ -1,30 +1,30 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"crates/yaak",
|
"crates/yaak",
|
||||||
# Shared crates (no Tauri dependency)
|
# Shared crates (no Tauri dependency)
|
||||||
"crates/yaak-core",
|
"crates/yaak-core",
|
||||||
"crates/yaak-common",
|
"crates/yaak-common",
|
||||||
"crates/yaak-crypto",
|
"crates/yaak-crypto",
|
||||||
"crates/yaak-git",
|
"crates/yaak-git",
|
||||||
"crates/yaak-grpc",
|
"crates/yaak-grpc",
|
||||||
"crates/yaak-http",
|
"crates/yaak-http",
|
||||||
"crates/yaak-models",
|
"crates/yaak-models",
|
||||||
"crates/yaak-plugins",
|
"crates/yaak-plugins",
|
||||||
"crates/yaak-sse",
|
"crates/yaak-sse",
|
||||||
"crates/yaak-sync",
|
"crates/yaak-sync",
|
||||||
"crates/yaak-templates",
|
"crates/yaak-templates",
|
||||||
"crates/yaak-tls",
|
"crates/yaak-tls",
|
||||||
"crates/yaak-ws",
|
"crates/yaak-ws",
|
||||||
"crates/yaak-api",
|
"crates/yaak-api",
|
||||||
# CLI crates
|
# CLI crates
|
||||||
"crates-cli/yaak-cli",
|
"crates-cli/yaak-cli",
|
||||||
# Tauri-specific crates
|
# Tauri-specific crates
|
||||||
"crates-tauri/yaak-app",
|
"crates-tauri/yaak-app",
|
||||||
"crates-tauri/yaak-fonts",
|
"crates-tauri/yaak-fonts",
|
||||||
"crates-tauri/yaak-license",
|
"crates-tauri/yaak-license",
|
||||||
"crates-tauri/yaak-mac-window",
|
"crates-tauri/yaak-mac-window",
|
||||||
"crates-tauri/yaak-tauri-utils",
|
"crates-tauri/yaak-tauri-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
# Developer Setup
|
# Developer Setup
|
||||||
|
|
||||||
Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so
|
Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so
|
||||||
uses Rust and HTML/CSS/JS for the main application but there is also a plugin system powered
|
uses Rust and HTML/CSS/JS for the main application but there is also a plugin system powered
|
||||||
by a Node.js sidecar that communicates to the app over gRPC.
|
by a Node.js sidecar that communicates to the app over gRPC.
|
||||||
|
|
||||||
Because of the moving parts, there are a few setup steps required before development can
|
Because of the moving parts, there are a few setup steps required before development can
|
||||||
begin.
|
begin.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Make sure you have the following tools installed:
|
Make sure you have the following tools installed:
|
||||||
|
|
||||||
- [Node.js](https://nodejs.org/en/download/package-manager)
|
- [Node.js](https://nodejs.org/en/download/package-manager) (v24+)
|
||||||
- [Rust](https://www.rust-lang.org/tools/install)
|
- [Rust](https://www.rust-lang.org/tools/install)
|
||||||
|
- [Vite+](https://vite.dev/guide/vite-plus) (`vp` CLI)
|
||||||
|
|
||||||
Check the installations with the following commands:
|
Check the installations with the following commands:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
node -v
|
node -v
|
||||||
npm -v
|
npm -v
|
||||||
|
vp --version
|
||||||
rustc --version
|
rustc --version
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -45,12 +47,12 @@ npm start
|
|||||||
## SQLite Migrations
|
## SQLite Migrations
|
||||||
|
|
||||||
New migrations can be created from the `src-tauri/` directory:
|
New migrations can be created from the `src-tauri/` directory:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run migration
|
npm run migration
|
||||||
```
|
```
|
||||||
|
|
||||||
Rerun the app to apply the migrations.
|
Rerun the app to apply the migrations.
|
||||||
|
|
||||||
_Note: For safety, development builds use a separate database location from production builds._
|
_Note: For safety, development builds use a separate database location from production builds._
|
||||||
|
|
||||||
@@ -61,9 +63,9 @@ _Note: For safety, development builds use a separate database location from prod
|
|||||||
lezer-generator components/core/Editor/<LANG>/<LANG>.grammar > components/core/Editor/<LANG>/<LANG>.ts
|
lezer-generator components/core/Editor/<LANG>/<LANG>.grammar > components/core/Editor/<LANG>/<LANG>.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linting & Formatting
|
## Linting and Formatting
|
||||||
|
|
||||||
This repo uses Biome for linting and formatting (replacing ESLint + Prettier).
|
This repo uses [Vite+](https://vite.dev/guide/vite-plus) for linting (oxlint) and formatting (oxfmt).
|
||||||
|
|
||||||
- Lint the entire repo:
|
- Lint the entire repo:
|
||||||
|
|
||||||
@@ -71,12 +73,6 @@ This repo uses Biome for linting and formatting (replacing ESLint + Prettier).
|
|||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
- Auto-fix lint issues where possible:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run lint:fix
|
|
||||||
```
|
|
||||||
|
|
||||||
- Format code:
|
- Format code:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -84,5 +80,7 @@ npm run format
|
|||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Many workspace packages also expose the same scripts (`lint`, `lint:fix`, and `format`).
|
|
||||||
- TypeScript type-checking still runs separately via `tsc --noEmit` in relevant packages.
|
- A pre-commit hook runs `vp lint` automatically on commit.
|
||||||
|
- Some workspace packages also run `tsc --noEmit` for type-checking.
|
||||||
|
- VS Code users should install the recommended extensions for format-on-save support.
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -16,8 +16,6 @@
|
|||||||
</p>
|
</p>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<!-- sponsors-premium --><a href="https://github.com/MVST-Solutions"><img src="https://github.com/MVST-Solutions.png" width="80px" alt="User avatar: MVST-Solutions" /></a> <a href="https://github.com/dharsanb"><img src="https://github.com/dharsanb.png" width="80px" alt="User avatar: dharsanb" /></a> <a href="https://github.com/railwayapp"><img src="https://github.com/railwayapp.png" width="80px" alt="User avatar: railwayapp" /></a> <a href="https://github.com/caseyamcl"><img src="https://github.com/caseyamcl.png" width="80px" alt="User avatar: caseyamcl" /></a> <a href="https://github.com/bytebase"><img src="https://github.com/bytebase.png" width="80px" alt="User avatar: bytebase" /></a> <a href="https://github.com/"><img src="https://raw.githubusercontent.com/JamesIves/github-sponsors-readme-action/dev/.github/assets/placeholder.png" width="80px" alt="User avatar: " /></a> <!-- sponsors-premium -->
|
<!-- sponsors-premium --><a href="https://github.com/MVST-Solutions"><img src="https://github.com/MVST-Solutions.png" width="80px" alt="User avatar: MVST-Solutions" /></a> <a href="https://github.com/dharsanb"><img src="https://github.com/dharsanb.png" width="80px" alt="User avatar: dharsanb" /></a> <a href="https://github.com/railwayapp"><img src="https://github.com/railwayapp.png" width="80px" alt="User avatar: railwayapp" /></a> <a href="https://github.com/caseyamcl"><img src="https://github.com/caseyamcl.png" width="80px" alt="User avatar: caseyamcl" /></a> <a href="https://github.com/bytebase"><img src="https://github.com/bytebase.png" width="80px" alt="User avatar: bytebase" /></a> <a href="https://github.com/"><img src="https://raw.githubusercontent.com/JamesIves/github-sponsors-readme-action/dev/.github/assets/placeholder.png" width="80px" alt="User avatar: " /></a> <!-- sponsors-premium -->
|
||||||
</p>
|
</p>
|
||||||
@@ -27,12 +25,10 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it.
|
Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it.
|
||||||
Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in.
|
Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in.
|
||||||
|
|
||||||
|
|
||||||
### 🌐 Work with any API
|
### 🌐 Work with any API
|
||||||
|
|
||||||
@@ -41,21 +37,23 @@ Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight
|
|||||||
- Filter and inspect responses with JSONPath or XPath.
|
- Filter and inspect responses with JSONPath or XPath.
|
||||||
|
|
||||||
### 🔐 Stay secure
|
### 🔐 Stay secure
|
||||||
|
|
||||||
- Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication.
|
- Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication.
|
||||||
- Secure sensitive values with encrypted secrets.
|
- Secure sensitive values with encrypted secrets.
|
||||||
- Store secrets in your OS keychain.
|
- Store secrets in your OS keychain.
|
||||||
|
|
||||||
### ☁️ Organize & collaborate
|
### ☁️ Organize & collaborate
|
||||||
|
|
||||||
- Group requests into workspaces and nested folders.
|
- Group requests into workspaces and nested folders.
|
||||||
- Use environment variables to switch between dev, staging, and prod.
|
- Use environment variables to switch between dev, staging, and prod.
|
||||||
- Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox.
|
- Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox.
|
||||||
|
|
||||||
### 🧩 Extend & customize
|
### 🧩 Extend & customize
|
||||||
|
|
||||||
- Insert dynamic values like UUIDs or timestamps with template tags.
|
- Insert dynamic values like UUIDs or timestamps with template tags.
|
||||||
- Pick from built-in themes or build your own.
|
- Pick from built-in themes or build your own.
|
||||||
- Create plugins to extend authentication, template tags, or the UI.
|
- Create plugins to extend authentication, template tags, or the UI.
|
||||||
|
|
||||||
|
|
||||||
## Contribution Policy
|
## Contribution Policy
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
|
|||||||
@@ -29,7 +29,14 @@ schemars = { workspace = true }
|
|||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "io-util", "net", "signal", "time"] }
|
tokio = { workspace = true, features = [
|
||||||
|
"rt-multi-thread",
|
||||||
|
"macros",
|
||||||
|
"io-util",
|
||||||
|
"net",
|
||||||
|
"signal",
|
||||||
|
"time",
|
||||||
|
] }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
webbrowser = "1"
|
webbrowser = "1"
|
||||||
zip = "4"
|
zip = "4"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Yaak CLI
|
# Yaak CLI
|
||||||
|
|
||||||
The `yaak` CLI for publishing plugins and creating/updating/sending requests.
|
The `yaak` CLI for publishing plugins and creating/updating/sending requests.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ Use the `yaak` CLI with agents like Claude or Codex to do useful things for you.
|
|||||||
Here are some example prompts:
|
Here are some example prompts:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Scan my API routes and create a workspace (using yaak cli) with
|
Scan my API routes and create a workspace (using yaak cli) with
|
||||||
all the requests needed for me to do manual testing?
|
all the requests needed for me to do manual testing?
|
||||||
```
|
```
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -35,7 +35,16 @@ r2d2 = "0.8.10"
|
|||||||
r2d2_sqlite = "0.25.0"
|
r2d2_sqlite = "0.25.0"
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
reqwest = { workspace = true, features = ["multipart", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider", "socks", "http2"] }
|
reqwest = { workspace = true, features = [
|
||||||
|
"multipart",
|
||||||
|
"gzip",
|
||||||
|
"brotli",
|
||||||
|
"deflate",
|
||||||
|
"json",
|
||||||
|
"rustls-tls-manual-roots-no-provider",
|
||||||
|
"socks",
|
||||||
|
"http2",
|
||||||
|
] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true, features = ["raw_value"] }
|
serde_json = { workspace = true, features = ["raw_value"] }
|
||||||
tauri = { workspace = true, features = ["devtools", "protocol-asset"] }
|
tauri = { workspace = true, features = ["devtools", "protocol-asset"] }
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
{
|
{
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Default capabilities for all build variants",
|
"description": "Default capabilities for all build variants",
|
||||||
"windows": [
|
"windows": ["*"],
|
||||||
"*"
|
|
||||||
],
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:app:allow-identifier",
|
"core:app:allow-identifier",
|
||||||
"core:event:allow-emit",
|
"core:event:allow-emit",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/tauri",
|
"name": "@yaakapp-internal/tauri",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "bindings/index.ts"
|
"main": "bindings/index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,7 @@
|
|||||||
"assetProtocol": {
|
"assetProtocol": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"scope": {
|
"scope": {
|
||||||
"allow": [
|
"allow": ["$APPDATA/responses/*", "$RESOURCE/static/*"]
|
||||||
"$APPDATA/responses/*",
|
|
||||||
"$RESOURCE/static/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,9 +22,7 @@
|
|||||||
"plugins": {
|
"plugins": {
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
"desktop": {
|
"desktop": {
|
||||||
"schemes": [
|
"schemes": ["yaak"]
|
||||||
"yaak"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"updater": {
|
"updater": {
|
||||||
"endpoints": [
|
"endpoints": ["https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}"],
|
||||||
"https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}"
|
|
||||||
],
|
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVGRkFGMjQxRUNEOTQ3MzAKUldRd1I5bnNRZkw2NzRtMnRlWTN3R24xYUR3aGRsUjJzWGwvdHdEcGljb3ZJMUNlMjFsaHlqVU4K"
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVGRkFGMjQxRUNEOTQ3MzAKUldRd1I5bnNRZkw2NzRtMnRlWTN3R24xYUR3aGRsUjJzWGwvdHdEcGljb3ZJMUNlMjFsaHlqVU4K"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { Fonts } from './bindings/gen_fonts';
|
import { Fonts } from "./bindings/gen_fonts";
|
||||||
|
|
||||||
export async function listFonts() {
|
export async function listFonts() {
|
||||||
return invoke<Fonts>('plugin:yaak-fonts|list', {});
|
return invoke<Fonts>("plugin:yaak-fonts|list", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFonts() {
|
export function useFonts() {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['list_fonts'],
|
queryKey: ["list_fonts"],
|
||||||
queryFn: () => listFonts(),
|
queryFn: () => listFonts(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/fonts",
|
"name": "@yaakapp-internal/fonts",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { appInfo } from '@yaakapp/app/lib/appInfo';
|
import { appInfo } from "@yaakapp/app/lib/appInfo";
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from "react";
|
||||||
import { LicenseCheckStatus } from './bindings/license';
|
import { LicenseCheckStatus } from "./bindings/license";
|
||||||
|
|
||||||
export * from './bindings/license';
|
export * from "./bindings/license";
|
||||||
|
|
||||||
const CHECK_QUERY_KEY = ['license.check'];
|
const CHECK_QUERY_KEY = ["license.check"];
|
||||||
|
|
||||||
export function useLicense() {
|
export function useLicense() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const activate = useMutation<void, string, { licenseKey: string }>({
|
const activate = useMutation<void, string, { licenseKey: string }>({
|
||||||
mutationKey: ['license.activate'],
|
mutationKey: ["license.activate"],
|
||||||
mutationFn: (payload) => invoke('plugin:yaak-license|activate', payload),
|
mutationFn: (payload) => invoke("plugin:yaak-license|activate", payload),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const deactivate = useMutation<void, string, void>({
|
const deactivate = useMutation<void, string, void>({
|
||||||
mutationKey: ['license.deactivate'],
|
mutationKey: ["license.deactivate"],
|
||||||
mutationFn: () => invoke('plugin:yaak-license|deactivate'),
|
mutationFn: () => invoke("plugin:yaak-license|deactivate"),
|
||||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check the license again after a license is activated
|
// Check the license again after a license is activated
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = listen('license-activated', async () => {
|
const unlisten = listen("license-activated", async () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY });
|
await queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY });
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
@@ -41,7 +41,7 @@ export function useLicense() {
|
|||||||
if (!appInfo.featureLicense) {
|
if (!appInfo.featureLicense) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return invoke<LicenseCheckStatus>('plugin:yaak-license|check');
|
return invoke<LicenseCheckStatus>("plugin:yaak-license|check");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/license",
|
"name": "@yaakapp-internal/license",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
export function setWindowTitle(title: string) {
|
export function setWindowTitle(title: string) {
|
||||||
invoke('plugin:yaak-mac-window|set_title', { title }).catch(console.error);
|
invoke("plugin:yaak-mac-window|set_title", { title }).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWindowTheme(bgColor: string) {
|
export function setWindowTheme(bgColor: string) {
|
||||||
invoke('plugin:yaak-mac-window|set_theme', { bgColor }).catch(console.error);
|
invoke("plugin:yaak-mac-window|set_theme", { bgColor }).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/mac-window",
|
"name": "@yaakapp-internal/mac-window",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
[default]
|
[default]
|
||||||
description = "Default permissions for the plugin"
|
description = "Default permissions for the plugin"
|
||||||
permissions = [
|
permissions = ["allow-set-title", "allow-set-theme"]
|
||||||
"allow-set-title",
|
|
||||||
"allow-set-theme",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
export function enableEncryption(workspaceId: string) {
|
export function enableEncryption(workspaceId: string) {
|
||||||
return invoke<void>('cmd_enable_encryption', { workspaceId });
|
return invoke<void>("cmd_enable_encryption", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function revealWorkspaceKey(workspaceId: string) {
|
export function revealWorkspaceKey(workspaceId: string) {
|
||||||
return invoke<string>('cmd_reveal_workspace_key', { workspaceId });
|
return invoke<string>("cmd_reveal_workspace_key", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setWorkspaceKey(args: { workspaceId: string; key: string }) {
|
export function setWorkspaceKey(args: { workspaceId: string; key: string }) {
|
||||||
return invoke<void>('cmd_set_workspace_key', args);
|
return invoke<void>("cmd_set_workspace_key", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disableEncryption(workspaceId: string) {
|
export function disableEncryption(workspaceId: string) {
|
||||||
return invoke<void>('cmd_disable_encryption', { workspaceId });
|
return invoke<void>("cmd_disable_encryption", { workspaceId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/crypto",
|
"name": "@yaakapp-internal/crypto",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,66 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { createFastMutation } from '@yaakapp/app/hooks/useFastMutation';
|
import { createFastMutation } from "@yaakapp/app/hooks/useFastMutation";
|
||||||
import { queryClient } from '@yaakapp/app/lib/queryClient';
|
import { queryClient } from "@yaakapp/app/lib/queryClient";
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from "react";
|
||||||
import { BranchDeleteResult, CloneResult, GitCommit, GitRemote, GitStatusSummary, PullResult, PushResult } from './bindings/gen_git';
|
import {
|
||||||
import { showToast } from '@yaakapp/app/lib/toast';
|
BranchDeleteResult,
|
||||||
|
CloneResult,
|
||||||
|
GitCommit,
|
||||||
|
GitRemote,
|
||||||
|
GitStatusSummary,
|
||||||
|
PullResult,
|
||||||
|
PushResult,
|
||||||
|
} from "./bindings/gen_git";
|
||||||
|
import { showToast } from "@yaakapp/app/lib/toast";
|
||||||
|
|
||||||
export * from './bindings/gen_git';
|
export * from "./bindings/gen_git";
|
||||||
export * from './bindings/gen_models';
|
export * from "./bindings/gen_models";
|
||||||
|
|
||||||
export interface GitCredentials {
|
export interface GitCredentials {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DivergedStrategy = 'force_reset' | 'merge' | 'cancel';
|
export type DivergedStrategy = "force_reset" | "merge" | "cancel";
|
||||||
|
|
||||||
export type UncommittedChangesStrategy = 'reset' | 'cancel';
|
export type UncommittedChangesStrategy = "reset" | "cancel";
|
||||||
|
|
||||||
export interface GitCallbacks {
|
export interface GitCallbacks {
|
||||||
addRemote: () => Promise<GitRemote | null>;
|
addRemote: () => Promise<GitRemote | null>;
|
||||||
promptCredentials: (
|
promptCredentials: (
|
||||||
result: Extract<PushResult, { type: 'needs_credentials' }>,
|
result: Extract<PushResult, { type: "needs_credentials" }>,
|
||||||
) => Promise<GitCredentials | null>;
|
) => Promise<GitCredentials | null>;
|
||||||
promptDiverged: (
|
promptDiverged: (result: Extract<PullResult, { type: "diverged" }>) => Promise<DivergedStrategy>;
|
||||||
result: Extract<PullResult, { type: 'diverged' }>,
|
|
||||||
) => Promise<DivergedStrategy>;
|
|
||||||
promptUncommittedChanges: () => Promise<UncommittedChangesStrategy>;
|
promptUncommittedChanges: () => Promise<UncommittedChangesStrategy>;
|
||||||
forceSync: () => Promise<void>;
|
forceSync: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSuccess = () => queryClient.invalidateQueries({ queryKey: ['git'] });
|
const onSuccess = () => queryClient.invalidateQueries({ queryKey: ["git"] });
|
||||||
|
|
||||||
export function useGit(dir: string, callbacks: GitCallbacks, refreshKey?: string) {
|
export function useGit(dir: string, callbacks: GitCallbacks, refreshKey?: string) {
|
||||||
const mutations = useMemo(() => gitMutations(dir, callbacks), [dir, callbacks]);
|
const mutations = useMemo(() => gitMutations(dir, callbacks), [dir, callbacks]);
|
||||||
const fetchAll = useQuery<void, string>({
|
const fetchAll = useQuery<void, string>({
|
||||||
queryKey: ['git', 'fetch_all', dir, refreshKey],
|
queryKey: ["git", "fetch_all", dir, refreshKey],
|
||||||
queryFn: () => invoke('cmd_git_fetch_all', { dir }),
|
queryFn: () => invoke("cmd_git_fetch_all", { dir }),
|
||||||
refetchInterval: 10 * 60_000,
|
refetchInterval: 10 * 60_000,
|
||||||
});
|
});
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
remotes: useQuery<GitRemote[], string>({
|
remotes: useQuery<GitRemote[], string>({
|
||||||
queryKey: ['git', 'remotes', dir, refreshKey],
|
queryKey: ["git", "remotes", dir, refreshKey],
|
||||||
queryFn: () => getRemotes(dir),
|
queryFn: () => getRemotes(dir),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
}),
|
}),
|
||||||
log: useQuery<GitCommit[], string>({
|
log: useQuery<GitCommit[], string>({
|
||||||
queryKey: ['git', 'log', dir, refreshKey],
|
queryKey: ["git", "log", dir, refreshKey],
|
||||||
queryFn: () => invoke('cmd_git_log', { dir }),
|
queryFn: () => invoke("cmd_git_log", { dir }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
}),
|
}),
|
||||||
status: useQuery<GitStatusSummary, string>({
|
status: useQuery<GitStatusSummary, string>({
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
queryKey: ['git', 'status', dir, refreshKey, fetchAll.dataUpdatedAt],
|
queryKey: ["git", "status", dir, refreshKey, fetchAll.dataUpdatedAt],
|
||||||
queryFn: () => invoke('cmd_git_status', { dir }),
|
queryFn: () => invoke("cmd_git_status", { dir }),
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -67,151 +73,167 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
const remotes = await getRemotes(dir);
|
const remotes = await getRemotes(dir);
|
||||||
if (remotes.length === 0) {
|
if (remotes.length === 0) {
|
||||||
const remote = await callbacks.addRemote();
|
const remote = await callbacks.addRemote();
|
||||||
if (remote == null) throw new Error('No remote found');
|
if (remote == null) throw new Error("No remote found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await invoke<PushResult>('cmd_git_push', { dir });
|
const result = await invoke<PushResult>("cmd_git_push", { dir });
|
||||||
if (result.type !== 'needs_credentials') return result;
|
if (result.type !== "needs_credentials") return result;
|
||||||
|
|
||||||
// Needs credentials, prompt for them
|
// Needs credentials, prompt for them
|
||||||
const creds = await callbacks.promptCredentials(result);
|
const creds = await callbacks.promptCredentials(result);
|
||||||
if (creds == null) throw new Error('Canceled');
|
if (creds == null) throw new Error("Canceled");
|
||||||
|
|
||||||
await invoke('cmd_git_add_credential', {
|
await invoke("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Push again
|
// Push again
|
||||||
return invoke<PushResult>('cmd_git_push', { dir });
|
return invoke<PushResult>("cmd_git_push", { dir });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (err: unknown) => {
|
const handleError = (err: unknown) => {
|
||||||
showToast({
|
showToast({
|
||||||
id: err instanceof Error ? err.message : String(err),
|
id: err instanceof Error ? err.message : String(err),
|
||||||
message: err instanceof Error ? err.message : String(err),
|
message: err instanceof Error ? err.message : String(err),
|
||||||
color: 'danger',
|
color: "danger",
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: createFastMutation<void, string, void>({
|
init: createFastMutation<void, string, void>({
|
||||||
mutationKey: ['git', 'init'],
|
mutationKey: ["git", "init"],
|
||||||
mutationFn: () => invoke('cmd_git_initialize', { dir }),
|
mutationFn: () => invoke("cmd_git_initialize", { dir }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
add: createFastMutation<void, string, { relaPaths: string[] }>({
|
add: createFastMutation<void, string, { relaPaths: string[] }>({
|
||||||
mutationKey: ['git', 'add', dir],
|
mutationKey: ["git", "add", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_add', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_add", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
addRemote: createFastMutation<GitRemote, string, GitRemote>({
|
addRemote: createFastMutation<GitRemote, string, GitRemote>({
|
||||||
mutationKey: ['git', 'add-remote'],
|
mutationKey: ["git", "add-remote"],
|
||||||
mutationFn: (args) => invoke('cmd_git_add_remote', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_add_remote", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
rmRemote: createFastMutation<void, string, { name: string }>({
|
rmRemote: createFastMutation<void, string, { name: string }>({
|
||||||
mutationKey: ['git', 'rm-remote', dir],
|
mutationKey: ["git", "rm-remote", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_rm_remote', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_rm_remote", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
createBranch: createFastMutation<void, string, { branch: string; base?: string }>({
|
createBranch: createFastMutation<void, string, { branch: string; base?: string }>({
|
||||||
mutationKey: ['git', 'branch', dir],
|
mutationKey: ["git", "branch", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_branch', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
mergeBranch: createFastMutation<void, string, { branch: string }>({
|
mergeBranch: createFastMutation<void, string, { branch: string }>({
|
||||||
mutationKey: ['git', 'merge', dir],
|
mutationKey: ["git", "merge", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_merge_branch', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_merge_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
deleteBranch: createFastMutation<BranchDeleteResult, string, { branch: string, force?: boolean }>({
|
deleteBranch: createFastMutation<
|
||||||
mutationKey: ['git', 'delete-branch', dir],
|
BranchDeleteResult,
|
||||||
mutationFn: (args) => invoke('cmd_git_delete_branch', { dir, ...args }),
|
string,
|
||||||
|
{ branch: string; force?: boolean }
|
||||||
|
>({
|
||||||
|
mutationKey: ["git", "delete-branch", dir],
|
||||||
|
mutationFn: (args) => invoke("cmd_git_delete_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
deleteRemoteBranch: createFastMutation<void, string, { branch: string }>({
|
deleteRemoteBranch: createFastMutation<void, string, { branch: string }>({
|
||||||
mutationKey: ['git', 'delete-remote-branch', dir],
|
mutationKey: ["git", "delete-remote-branch", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_delete_remote_branch', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_delete_remote_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
renameBranch: createFastMutation<void, string, { oldName: string, newName: string }>({
|
renameBranch: createFastMutation<void, string, { oldName: string; newName: string }>({
|
||||||
mutationKey: ['git', 'rename-branch', dir],
|
mutationKey: ["git", "rename-branch", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_rename_branch', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_rename_branch", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
checkout: createFastMutation<string, string, { branch: string; force: boolean }>({
|
checkout: createFastMutation<string, string, { branch: string; force: boolean }>({
|
||||||
mutationKey: ['git', 'checkout', dir],
|
mutationKey: ["git", "checkout", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_checkout', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_checkout", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
commit: createFastMutation<void, string, { message: string }>({
|
commit: createFastMutation<void, string, { message: string }>({
|
||||||
mutationKey: ['git', 'commit', dir],
|
mutationKey: ["git", "commit", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_commit', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_commit", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
commitAndPush: createFastMutation<PushResult, string, { message: string }>({
|
commitAndPush: createFastMutation<PushResult, string, { message: string }>({
|
||||||
mutationKey: ['git', 'commit_push', dir],
|
mutationKey: ["git", "commit_push", dir],
|
||||||
mutationFn: async (args) => {
|
mutationFn: async (args) => {
|
||||||
await invoke('cmd_git_commit', { dir, ...args });
|
await invoke("cmd_git_commit", { dir, ...args });
|
||||||
return push();
|
return push();
|
||||||
},
|
},
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
push: createFastMutation<PushResult, string, void>({
|
push: createFastMutation<PushResult, string, void>({
|
||||||
mutationKey: ['git', 'push', dir],
|
mutationKey: ["git", "push", dir],
|
||||||
mutationFn: push,
|
mutationFn: push,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
pull: createFastMutation<PullResult, string, void>({
|
pull: createFastMutation<PullResult, string, void>({
|
||||||
mutationKey: ['git', 'pull', dir],
|
mutationKey: ["git", "pull", dir],
|
||||||
async mutationFn() {
|
async mutationFn() {
|
||||||
const result = await invoke<PullResult>('cmd_git_pull', { dir });
|
const result = await invoke<PullResult>("cmd_git_pull", { dir });
|
||||||
|
|
||||||
if (result.type === 'needs_credentials') {
|
if (result.type === "needs_credentials") {
|
||||||
const creds = await callbacks.promptCredentials(result);
|
const creds = await callbacks.promptCredentials(result);
|
||||||
if (creds == null) throw new Error('Canceled');
|
if (creds == null) throw new Error("Canceled");
|
||||||
|
|
||||||
await invoke('cmd_git_add_credential', {
|
await invoke("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pull again after credentials
|
// Pull again after credentials
|
||||||
return invoke<PullResult>('cmd_git_pull', { dir });
|
return invoke<PullResult>("cmd_git_pull", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.type === 'uncommitted_changes') {
|
if (result.type === "uncommitted_changes") {
|
||||||
void callbacks.promptUncommittedChanges().then(async (strategy) => {
|
void callbacks
|
||||||
if (strategy === 'cancel') return;
|
.promptUncommittedChanges()
|
||||||
|
.then(async (strategy) => {
|
||||||
|
if (strategy === "cancel") return;
|
||||||
|
|
||||||
await invoke('cmd_git_reset_changes', { dir });
|
await invoke("cmd_git_reset_changes", { dir });
|
||||||
return invoke<PullResult>('cmd_git_pull', { dir });
|
return invoke<PullResult>("cmd_git_pull", { dir });
|
||||||
}).then(async () => { await onSuccess(); await callbacks.forceSync(); }, handleError);
|
})
|
||||||
|
.then(async () => {
|
||||||
|
await onSuccess();
|
||||||
|
await callbacks.forceSync();
|
||||||
|
}, handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.type === 'diverged') {
|
if (result.type === "diverged") {
|
||||||
void callbacks.promptDiverged(result).then((strategy) => {
|
void callbacks
|
||||||
if (strategy === 'cancel') return;
|
.promptDiverged(result)
|
||||||
|
.then((strategy) => {
|
||||||
|
if (strategy === "cancel") return;
|
||||||
|
|
||||||
if (strategy === 'force_reset') {
|
if (strategy === "force_reset") {
|
||||||
return invoke<PullResult>('cmd_git_pull_force_reset', {
|
return invoke<PullResult>("cmd_git_pull_force_reset", {
|
||||||
|
dir,
|
||||||
|
remote: result.remote,
|
||||||
|
branch: result.branch,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke<PullResult>("cmd_git_pull_merge", {
|
||||||
dir,
|
dir,
|
||||||
remote: result.remote,
|
remote: result.remote,
|
||||||
branch: result.branch,
|
branch: result.branch,
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
|
.then(async () => {
|
||||||
return invoke<PullResult>('cmd_git_pull_merge', {
|
await onSuccess();
|
||||||
dir,
|
await callbacks.forceSync();
|
||||||
remote: result.remote,
|
}, handleError);
|
||||||
branch: result.branch,
|
|
||||||
});
|
|
||||||
}).then(async () => { await onSuccess(); await callbacks.forceSync(); }, handleError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -219,20 +241,20 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
|
|||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
unstage: createFastMutation<void, string, { relaPaths: string[] }>({
|
unstage: createFastMutation<void, string, { relaPaths: string[] }>({
|
||||||
mutationKey: ['git', 'unstage', dir],
|
mutationKey: ["git", "unstage", dir],
|
||||||
mutationFn: (args) => invoke('cmd_git_unstage', { dir, ...args }),
|
mutationFn: (args) => invoke("cmd_git_unstage", { dir, ...args }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
resetChanges: createFastMutation<void, string, void>({
|
resetChanges: createFastMutation<void, string, void>({
|
||||||
mutationKey: ['git', 'reset-changes', dir],
|
mutationKey: ["git", "reset-changes", dir],
|
||||||
mutationFn: () => invoke('cmd_git_reset_changes', { dir }),
|
mutationFn: () => invoke("cmd_git_reset_changes", { dir }),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}),
|
}),
|
||||||
} as const;
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getRemotes(dir: string) {
|
async function getRemotes(dir: string) {
|
||||||
return invoke<GitRemote[]>('cmd_git_remotes', { dir });
|
return invoke<GitRemote[]>("cmd_git_remotes", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,21 +263,24 @@ async function getRemotes(dir: string) {
|
|||||||
export async function gitClone(
|
export async function gitClone(
|
||||||
url: string,
|
url: string,
|
||||||
dir: string,
|
dir: string,
|
||||||
promptCredentials: (args: { url: string; error: string | null }) => Promise<GitCredentials | null>,
|
promptCredentials: (args: {
|
||||||
|
url: string;
|
||||||
|
error: string | null;
|
||||||
|
}) => Promise<GitCredentials | null>,
|
||||||
): Promise<CloneResult> {
|
): Promise<CloneResult> {
|
||||||
const result = await invoke<CloneResult>('cmd_git_clone', { url, dir });
|
const result = await invoke<CloneResult>("cmd_git_clone", { url, dir });
|
||||||
if (result.type !== 'needs_credentials') return result;
|
if (result.type !== "needs_credentials") return result;
|
||||||
|
|
||||||
// Prompt for credentials
|
// Prompt for credentials
|
||||||
const creds = await promptCredentials({ url: result.url, error: result.error });
|
const creds = await promptCredentials({ url: result.url, error: result.error });
|
||||||
if (creds == null) return {type: 'cancelled'};
|
if (creds == null) return { type: "cancelled" };
|
||||||
|
|
||||||
// Store credentials and retry
|
// Store credentials and retry
|
||||||
await invoke('cmd_git_add_credential', {
|
await invoke("cmd_git_add_credential", {
|
||||||
remoteUrl: result.url,
|
remoteUrl: result.url,
|
||||||
username: creds.username,
|
username: creds.username,
|
||||||
password: creds.password,
|
password: creds.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
return invoke<CloneResult>('cmd_git_clone', { url, dir });
|
return invoke<CloneResult>("cmd_git_clone", { url, dir });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/git",
|
"name": "@yaakapp-internal/git",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,12 @@ hyper-util = { version = "0.1.17", default-features = false, features = ["client
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "socks", "http2", "stream"] }
|
reqwest = { workspace = true, features = [
|
||||||
|
"rustls-tls-manual-roots-no-provider",
|
||||||
|
"socks",
|
||||||
|
"http2",
|
||||||
|
"stream",
|
||||||
|
] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|||||||
@@ -1,35 +1,39 @@
|
|||||||
import { atom } from 'jotai';
|
import { atom } from "jotai";
|
||||||
|
|
||||||
import { selectAtom } from 'jotai/utils';
|
import { selectAtom } from "jotai/utils";
|
||||||
import type { AnyModel } from '../bindings/gen_models';
|
import type { AnyModel } from "../bindings/gen_models";
|
||||||
import { ExtractModel } from './types';
|
import { ExtractModel } from "./types";
|
||||||
import { newStoreData } from './util';
|
import { newStoreData } from "./util";
|
||||||
|
|
||||||
export const modelStoreDataAtom = atom(newStoreData());
|
export const modelStoreDataAtom = atom(newStoreData());
|
||||||
|
|
||||||
export const cookieJarsAtom = createOrderedModelAtom('cookie_jar', 'name', 'asc');
|
export const cookieJarsAtom = createOrderedModelAtom("cookie_jar", "name", "asc");
|
||||||
export const environmentsAtom = createOrderedModelAtom('environment', 'sortPriority', 'asc');
|
export const environmentsAtom = createOrderedModelAtom("environment", "sortPriority", "asc");
|
||||||
export const foldersAtom = createModelAtom('folder');
|
export const foldersAtom = createModelAtom("folder");
|
||||||
export const grpcConnectionsAtom = createOrderedModelAtom('grpc_connection', 'createdAt', 'desc');
|
export const grpcConnectionsAtom = createOrderedModelAtom("grpc_connection", "createdAt", "desc");
|
||||||
export const grpcEventsAtom = createOrderedModelAtom('grpc_event', 'createdAt', 'asc');
|
export const grpcEventsAtom = createOrderedModelAtom("grpc_event", "createdAt", "asc");
|
||||||
export const grpcRequestsAtom = createModelAtom('grpc_request');
|
export const grpcRequestsAtom = createModelAtom("grpc_request");
|
||||||
export const httpRequestsAtom = createModelAtom('http_request');
|
export const httpRequestsAtom = createModelAtom("http_request");
|
||||||
export const httpResponsesAtom = createOrderedModelAtom('http_response', 'createdAt', 'desc');
|
export const httpResponsesAtom = createOrderedModelAtom("http_response", "createdAt", "desc");
|
||||||
export const httpResponseEventsAtom = createOrderedModelAtom('http_response_event', 'createdAt', 'asc');
|
export const httpResponseEventsAtom = createOrderedModelAtom(
|
||||||
export const keyValuesAtom = createModelAtom('key_value');
|
"http_response_event",
|
||||||
export const pluginsAtom = createModelAtom('plugin');
|
"createdAt",
|
||||||
export const settingsAtom = createSingularModelAtom('settings');
|
"asc",
|
||||||
export const websocketRequestsAtom = createModelAtom('websocket_request');
|
|
||||||
export const websocketEventsAtom = createOrderedModelAtom('websocket_event', 'createdAt', 'asc');
|
|
||||||
export const websocketConnectionsAtom = createOrderedModelAtom(
|
|
||||||
'websocket_connection',
|
|
||||||
'createdAt',
|
|
||||||
'desc',
|
|
||||||
);
|
);
|
||||||
export const workspaceMetasAtom = createModelAtom('workspace_meta');
|
export const keyValuesAtom = createModelAtom("key_value");
|
||||||
export const workspacesAtom = createOrderedModelAtom('workspace', 'name', 'asc');
|
export const pluginsAtom = createModelAtom("plugin");
|
||||||
|
export const settingsAtom = createSingularModelAtom("settings");
|
||||||
|
export const websocketRequestsAtom = createModelAtom("websocket_request");
|
||||||
|
export const websocketEventsAtom = createOrderedModelAtom("websocket_event", "createdAt", "asc");
|
||||||
|
export const websocketConnectionsAtom = createOrderedModelAtom(
|
||||||
|
"websocket_connection",
|
||||||
|
"createdAt",
|
||||||
|
"desc",
|
||||||
|
);
|
||||||
|
export const workspaceMetasAtom = createModelAtom("workspace_meta");
|
||||||
|
export const workspacesAtom = createOrderedModelAtom("workspace", "name", "asc");
|
||||||
|
|
||||||
export function createModelAtom<M extends AnyModel['model']>(modelType: M) {
|
export function createModelAtom<M extends AnyModel["model"]>(modelType: M) {
|
||||||
return selectAtom(
|
return selectAtom(
|
||||||
modelStoreDataAtom,
|
modelStoreDataAtom,
|
||||||
(data) => Object.values(data[modelType] ?? {}),
|
(data) => Object.values(data[modelType] ?? {}),
|
||||||
@@ -37,19 +41,19 @@ export function createModelAtom<M extends AnyModel['model']>(modelType: M) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSingularModelAtom<M extends AnyModel['model']>(modelType: M) {
|
export function createSingularModelAtom<M extends AnyModel["model"]>(modelType: M) {
|
||||||
return selectAtom(modelStoreDataAtom, (data) => {
|
return selectAtom(modelStoreDataAtom, (data) => {
|
||||||
const modelData = Object.values(data[modelType] ?? {});
|
const modelData = Object.values(data[modelType] ?? {});
|
||||||
const item = modelData[0];
|
const item = modelData[0];
|
||||||
if (item == null) throw new Error('Failed creating singular model with no data: ' + modelType);
|
if (item == null) throw new Error("Failed creating singular model with no data: " + modelType);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createOrderedModelAtom<M extends AnyModel['model']>(
|
export function createOrderedModelAtom<M extends AnyModel["model"]>(
|
||||||
modelType: M,
|
modelType: M,
|
||||||
field: keyof ExtractModel<AnyModel, M>,
|
field: keyof ExtractModel<AnyModel, M>,
|
||||||
order: 'asc' | 'desc',
|
order: "asc" | "desc",
|
||||||
) {
|
) {
|
||||||
return selectAtom(
|
return selectAtom(
|
||||||
modelStoreDataAtom,
|
modelStoreDataAtom,
|
||||||
@@ -58,7 +62,7 @@ export function createOrderedModelAtom<M extends AnyModel['model']>(
|
|||||||
return Object.values(modelData).sort(
|
return Object.values(modelData).sort(
|
||||||
(a: ExtractModel<AnyModel, M>, b: ExtractModel<AnyModel, M>) => {
|
(a: ExtractModel<AnyModel, M>, b: ExtractModel<AnyModel, M>) => {
|
||||||
const n = a[field] > b[field] ? 1 : -1;
|
const n = a[field] > b[field] ? 1 : -1;
|
||||||
return order === 'desc' ? n * -1 : n;
|
return order === "desc" ? n * -1 : n;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { AnyModel } from '../bindings/gen_models';
|
import { AnyModel } from "../bindings/gen_models";
|
||||||
|
|
||||||
export * from '../bindings/gen_models';
|
export * from "../bindings/gen_models";
|
||||||
export * from '../bindings/gen_util';
|
export * from "../bindings/gen_util";
|
||||||
export * from './store';
|
export * from "./store";
|
||||||
export * from './atoms';
|
export * from "./atoms";
|
||||||
|
|
||||||
export function modelTypeLabel(m: AnyModel): string {
|
export function modelTypeLabel(m: AnyModel): string {
|
||||||
const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
return m.model.split('_').map(capitalize).join(' ');
|
return m.model.split("_").map(capitalize).join(" ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||||
import { resolvedModelName } from '@yaakapp/app/lib/resolvedModelName';
|
import { resolvedModelName } from "@yaakapp/app/lib/resolvedModelName";
|
||||||
import { AnyModel, ModelPayload } from '../bindings/gen_models';
|
import { AnyModel, ModelPayload } from "../bindings/gen_models";
|
||||||
import { modelStoreDataAtom } from './atoms';
|
import { modelStoreDataAtom } from "./atoms";
|
||||||
import { ExtractModel, JotaiStore, ModelStoreData } from './types';
|
import { ExtractModel, JotaiStore, ModelStoreData } from "./types";
|
||||||
import { newStoreData } from './util';
|
import { newStoreData } from "./util";
|
||||||
|
|
||||||
let _store: JotaiStore | null = null;
|
let _store: JotaiStore | null = null;
|
||||||
|
|
||||||
@@ -12,11 +12,11 @@ export function initModelStore(store: JotaiStore) {
|
|||||||
_store = store;
|
_store = store;
|
||||||
|
|
||||||
getCurrentWebviewWindow()
|
getCurrentWebviewWindow()
|
||||||
.listen<ModelPayload>('model_write', ({ payload }) => {
|
.listen<ModelPayload>("model_write", ({ payload }) => {
|
||||||
if (shouldIgnoreModel(payload)) return;
|
if (shouldIgnoreModel(payload)) return;
|
||||||
|
|
||||||
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
||||||
if (payload.change.type === 'upsert') {
|
if (payload.change.type === "upsert") {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[payload.model.model]: {
|
[payload.model.model]: {
|
||||||
@@ -36,7 +36,7 @@ export function initModelStore(store: JotaiStore) {
|
|||||||
|
|
||||||
function mustStore(): JotaiStore {
|
function mustStore(): JotaiStore {
|
||||||
if (_store == null) {
|
if (_store == null) {
|
||||||
throw new Error('Model store was not initialized');
|
throw new Error("Model store was not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return _store;
|
return _store;
|
||||||
@@ -45,8 +45,8 @@ function mustStore(): JotaiStore {
|
|||||||
let _activeWorkspaceId: string | null = null;
|
let _activeWorkspaceId: string | null = null;
|
||||||
|
|
||||||
export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
||||||
console.log('Syncing models with new workspace', workspaceId);
|
console.log("Syncing models with new workspace", workspaceId);
|
||||||
const workspaceModelsStr = await invoke<string>('models_workspace_models', {
|
const workspaceModelsStr = await invoke<string>("models_workspace_models", {
|
||||||
workspaceId, // NOTE: if no workspace id provided, it will just fetch global models
|
workspaceId, // NOTE: if no workspace id provided, it will just fetch global models
|
||||||
});
|
});
|
||||||
const workspaceModels = JSON.parse(workspaceModelsStr) as AnyModel[];
|
const workspaceModels = JSON.parse(workspaceModelsStr) as AnyModel[];
|
||||||
@@ -57,12 +57,12 @@ export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
|||||||
|
|
||||||
mustStore().set(modelStoreDataAtom, data);
|
mustStore().set(modelStoreDataAtom, data);
|
||||||
|
|
||||||
console.log('Synced model store with workspace', workspaceId, data);
|
console.log("Synced model store with workspace", workspaceId, data);
|
||||||
|
|
||||||
_activeWorkspaceId = workspaceId;
|
_activeWorkspaceId = workspaceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listModels<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export function listModels<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
modelType: M | ReadonlyArray<M>,
|
modelType: M | ReadonlyArray<M>,
|
||||||
): T[] {
|
): T[] {
|
||||||
let data = mustStore().get(modelStoreDataAtom);
|
let data = mustStore().get(modelStoreDataAtom);
|
||||||
@@ -70,7 +70,7 @@ export function listModels<M extends AnyModel['model'], T extends ExtractModel<A
|
|||||||
return types.flatMap((t) => Object.values(data[t]) as T[]);
|
return types.flatMap((t) => Object.values(data[t]) as T[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export function getModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
modelType: M | ReadonlyArray<M>,
|
modelType: M | ReadonlyArray<M>,
|
||||||
id: string,
|
id: string,
|
||||||
): T | null {
|
): T | null {
|
||||||
@@ -83,9 +83,7 @@ export function getModel<M extends AnyModel['model'], T extends ExtractModel<Any
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAnyModel(
|
export function getAnyModel(id: string): AnyModel | null {
|
||||||
id: string,
|
|
||||||
): AnyModel | null {
|
|
||||||
let data = mustStore().get(modelStoreDataAtom);
|
let data = mustStore().get(modelStoreDataAtom);
|
||||||
for (const t of Object.keys(data)) {
|
for (const t of Object.keys(data)) {
|
||||||
// oxlint-disable-next-line no-explicit-any
|
// oxlint-disable-next-line no-explicit-any
|
||||||
@@ -95,7 +93,7 @@ export function getAnyModel(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchModelById<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export function patchModelById<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
model: M,
|
model: M,
|
||||||
id: string,
|
id: string,
|
||||||
patch: Partial<T> | ((prev: T) => T),
|
patch: Partial<T> | ((prev: T) => T),
|
||||||
@@ -105,54 +103,54 @@ export function patchModelById<M extends AnyModel['model'], T extends ExtractMod
|
|||||||
throw new Error(`Failed to get model to patch id=${id} model=${model}`);
|
throw new Error(`Failed to get model to patch id=${id} model=${model}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newModel = typeof patch === 'function' ? patch(prev) : { ...prev, ...patch };
|
const newModel = typeof patch === "function" ? patch(prev) : { ...prev, ...patch };
|
||||||
return updateModel(newModel);
|
return updateModel(newModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function patchModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export async function patchModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
base: Pick<T, 'id' | 'model'>,
|
base: Pick<T, "id" | "model">,
|
||||||
patch: Partial<T>,
|
patch: Partial<T>,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return patchModelById<M, T>(base.model, base.id, patch);
|
return patchModelById<M, T>(base.model, base.id, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export async function updateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
model: T,
|
model: T,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return invoke<string>('models_upsert', { model });
|
return invoke<string>("models_upsert", { model });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteModelById<
|
export async function deleteModelById<
|
||||||
M extends AnyModel['model'],
|
M extends AnyModel["model"],
|
||||||
T extends ExtractModel<AnyModel, M>,
|
T extends ExtractModel<AnyModel, M>,
|
||||||
>(modelType: M | M[], id: string) {
|
>(modelType: M | M[], id: string) {
|
||||||
let model = getModel<M, T>(modelType, id);
|
let model = getModel<M, T>(modelType, id);
|
||||||
await deleteModel(model);
|
await deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export async function deleteModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
model: T | null,
|
model: T | null,
|
||||||
) {
|
) {
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
throw new Error('Failed to delete null model');
|
throw new Error("Failed to delete null model");
|
||||||
}
|
}
|
||||||
await invoke<string>('models_delete', { model });
|
await invoke<string>("models_delete", { model });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function duplicateModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
export function duplicateModel<M extends AnyModel["model"], T extends ExtractModel<AnyModel, M>>(
|
||||||
model: T | null,
|
model: T | null,
|
||||||
) {
|
) {
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
throw new Error('Failed to duplicate null model');
|
throw new Error("Failed to duplicate null model");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the model has a name, try to duplicate it with a name that doesn't conflict
|
// If the model has a name, try to duplicate it with a name that doesn't conflict
|
||||||
let name = 'name' in model ? resolvedModelName(model) : undefined;
|
let name = "name" in model ? resolvedModelName(model) : undefined;
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
const existingModels = listModels(model.model);
|
const existingModels = listModels(model.model);
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const hasConflict = existingModels.some((m) => {
|
const hasConflict = existingModels.some((m) => {
|
||||||
if ('folderId' in m && 'folderId' in model && model.folderId !== m.folderId) {
|
if ("folderId" in m && "folderId" in model && model.folderId !== m.folderId) {
|
||||||
return false;
|
return false;
|
||||||
} else if (resolvedModelName(m) !== name) {
|
} else if (resolvedModelName(m) !== name) {
|
||||||
return false;
|
return false;
|
||||||
@@ -166,7 +164,7 @@ export function duplicateModel<M extends AnyModel['model'], T extends ExtractMod
|
|||||||
// Name conflict. Try another one
|
// Name conflict. Try another one
|
||||||
const m: RegExpMatchArray | null = name.match(/ Copy( (?<n>\d+))?$/);
|
const m: RegExpMatchArray | null = name.match(/ Copy( (?<n>\d+))?$/);
|
||||||
if (m != null && m.groups?.n == null) {
|
if (m != null && m.groups?.n == null) {
|
||||||
name = name.substring(0, m.index) + ' Copy 2';
|
name = name.substring(0, m.index) + " Copy 2";
|
||||||
} else if (m != null && m.groups?.n != null) {
|
} else if (m != null && m.groups?.n != null) {
|
||||||
name = name.substring(0, m.index) + ` Copy ${parseInt(m.groups.n) + 1}`;
|
name = name.substring(0, m.index) + ` Copy ${parseInt(m.groups.n) + 1}`;
|
||||||
} else {
|
} else {
|
||||||
@@ -175,23 +173,23 @@ export function duplicateModel<M extends AnyModel['model'], T extends ExtractMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return invoke<string>('models_duplicate', { model: { ...model, name } });
|
return invoke<string>("models_duplicate", { model: { ...model, name } });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
|
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
|
||||||
patch: Partial<T> & Pick<T, 'model'>,
|
patch: Partial<T> & Pick<T, "model">,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return invoke<string>('models_upsert', { model: patch });
|
return invoke<string>("models_upsert", { model: patch });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createWorkspaceModel<T extends Extract<AnyModel, { workspaceId: string }>>(
|
export async function createWorkspaceModel<T extends Extract<AnyModel, { workspaceId: string }>>(
|
||||||
patch: Partial<T> & Pick<T, 'model' | 'workspaceId'>,
|
patch: Partial<T> & Pick<T, "model" | "workspaceId">,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return invoke<string>('models_upsert', { model: patch });
|
return invoke<string>("models_upsert", { model: patch });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replaceModelsInStore<
|
export function replaceModelsInStore<
|
||||||
M extends AnyModel['model'],
|
M extends AnyModel["model"],
|
||||||
T extends Extract<AnyModel, { model: M }>,
|
T extends Extract<AnyModel, { model: M }>,
|
||||||
>(model: M, models: T[]) {
|
>(model: M, models: T[]) {
|
||||||
const newModels: Record<string, T> = {};
|
const newModels: Record<string, T> = {};
|
||||||
@@ -208,7 +206,7 @@ export function replaceModelsInStore<
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function mergeModelsInStore<
|
export function mergeModelsInStore<
|
||||||
M extends AnyModel['model'],
|
M extends AnyModel["model"],
|
||||||
T extends Extract<AnyModel, { model: M }>,
|
T extends Extract<AnyModel, { model: M }>,
|
||||||
>(model: M, models: T[], filter?: (model: T) => boolean) {
|
>(model: M, models: T[], filter?: (model: T) => boolean) {
|
||||||
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => {
|
||||||
@@ -237,7 +235,7 @@ export function mergeModelsInStore<
|
|||||||
|
|
||||||
function shouldIgnoreModel({ model, updateSource }: ModelPayload) {
|
function shouldIgnoreModel({ model, updateSource }: ModelPayload) {
|
||||||
// Never ignore updates from non-user sources
|
// Never ignore updates from non-user sources
|
||||||
if (updateSource.type !== 'window') {
|
if (updateSource.type !== "window") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,11 +245,11 @@ function shouldIgnoreModel({ model, updateSource }: ModelPayload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only sync models that belong to this workspace, if a workspace ID is present
|
// Only sync models that belong to this workspace, if a workspace ID is present
|
||||||
if ('workspaceId' in model && model.workspaceId !== _activeWorkspaceId) {
|
if ("workspaceId" in model && model.workspaceId !== _activeWorkspaceId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.model === 'key_value' && model.namespace === 'no_sync') {
|
if (model.model === "key_value" && model.namespace === "no_sync") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createStore } from 'jotai';
|
import { createStore } from "jotai";
|
||||||
import { AnyModel } from '../bindings/gen_models';
|
import { AnyModel } from "../bindings/gen_models";
|
||||||
|
|
||||||
export type ExtractModel<T, M> = T extends { model: M } ? T : never;
|
export type ExtractModel<T, M> = T extends { model: M } ? T : never;
|
||||||
export type ModelStoreData<T extends AnyModel = AnyModel> = {
|
export type ModelStoreData<T extends AnyModel = AnyModel> = {
|
||||||
[M in T['model']]: Record<string, Extract<T, { model: M }>>;
|
[M in T["model"]]: Record<string, Extract<T, { model: M }>>;
|
||||||
};
|
};
|
||||||
export type JotaiStore = ReturnType<typeof createStore>;
|
export type JotaiStore = ReturnType<typeof createStore>;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ModelStoreData } from './types';
|
import { ModelStoreData } from "./types";
|
||||||
|
|
||||||
export function newStoreData(): ModelStoreData {
|
export function newStoreData(): ModelStoreData {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/models",
|
"name": "@yaakapp-internal/models",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "guest-js/index.ts"
|
"main": "guest-js/index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from './bindings/gen_api';
|
import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from "./bindings/gen_api";
|
||||||
|
|
||||||
export * from './bindings/gen_models';
|
export * from "./bindings/gen_models";
|
||||||
export * from './bindings/gen_events';
|
export * from "./bindings/gen_events";
|
||||||
export * from './bindings/gen_search';
|
export * from "./bindings/gen_search";
|
||||||
|
|
||||||
export async function searchPlugins(query: string) {
|
export async function searchPlugins(query: string) {
|
||||||
return invoke<PluginSearchResponse>('cmd_plugins_search', { query });
|
return invoke<PluginSearchResponse>("cmd_plugins_search", { query });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPlugin(name: string, version: string | null) {
|
export async function installPlugin(name: string, version: string | null) {
|
||||||
return invoke<void>('cmd_plugins_install', { name, version });
|
return invoke<void>("cmd_plugins_install", { name, version });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uninstallPlugin(pluginId: string) {
|
export async function uninstallPlugin(pluginId: string) {
|
||||||
return invoke<void>('cmd_plugins_uninstall', { pluginId });
|
return invoke<void>("cmd_plugins_uninstall", { pluginId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkPluginUpdates() {
|
export async function checkPluginUpdates() {
|
||||||
return invoke<PluginUpdatesResponse>('cmd_plugins_updates', {});
|
return invoke<PluginUpdatesResponse>("cmd_plugins_updates", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateAllPlugins() {
|
export async function updateAllPlugins() {
|
||||||
return invoke<PluginNameVersion[]>('cmd_plugins_update_all', {});
|
return invoke<PluginNameVersion[]>("cmd_plugins_update_all", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPluginFromDirectory(directory: string) {
|
export async function installPluginFromDirectory(directory: string) {
|
||||||
return invoke<void>('cmd_plugins_install_from_directory', { directory });
|
return invoke<void>("cmd_plugins_install_from_directory", { directory });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/plugins",
|
"name": "@yaakapp-internal/plugins",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './bindings/sse';
|
export * from "./bindings/sse";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/sse",
|
"name": "@yaakapp-internal/sse",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { Channel, invoke } from '@tauri-apps/api/core';
|
import { Channel, invoke } from "@tauri-apps/api/core";
|
||||||
import { emit } from '@tauri-apps/api/event';
|
import { emit } from "@tauri-apps/api/event";
|
||||||
import type { WatchResult } from '@yaakapp-internal/tauri';
|
import type { WatchResult } from "@yaakapp-internal/tauri";
|
||||||
import { SyncOp } from './bindings/gen_sync';
|
import { SyncOp } from "./bindings/gen_sync";
|
||||||
import { WatchEvent } from './bindings/gen_watch';
|
import { WatchEvent } from "./bindings/gen_watch";
|
||||||
|
|
||||||
export * from './bindings/gen_models';
|
export * from "./bindings/gen_models";
|
||||||
|
|
||||||
export async function calculateSync(workspaceId: string, syncDir: string) {
|
export async function calculateSync(workspaceId: string, syncDir: string) {
|
||||||
return invoke<SyncOp[]>('cmd_sync_calculate', {
|
return invoke<SyncOp[]>("cmd_sync_calculate", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
syncDir,
|
syncDir,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function calculateSyncFsOnly(dir: string) {
|
export async function calculateSyncFsOnly(dir: string) {
|
||||||
return invoke<SyncOp[]>('cmd_sync_calculate_fs', { dir });
|
return invoke<SyncOp[]>("cmd_sync_calculate_fs", { dir });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function applySync(workspaceId: string, syncDir: string, syncOps: SyncOp[]) {
|
export async function applySync(workspaceId: string, syncDir: string, syncOps: SyncOp[]) {
|
||||||
return invoke<void>('cmd_sync_apply', {
|
return invoke<void>("cmd_sync_apply", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
syncDir,
|
syncDir,
|
||||||
syncOps: syncOps,
|
syncOps: syncOps,
|
||||||
@@ -30,10 +30,10 @@ export function watchWorkspaceFiles(
|
|||||||
syncDir: string,
|
syncDir: string,
|
||||||
callback: (e: WatchEvent) => void,
|
callback: (e: WatchEvent) => void,
|
||||||
) {
|
) {
|
||||||
console.log('Watching workspace files', workspaceId, syncDir);
|
console.log("Watching workspace files", workspaceId, syncDir);
|
||||||
const channel = new Channel<WatchEvent>();
|
const channel = new Channel<WatchEvent>();
|
||||||
channel.onmessage = callback;
|
channel.onmessage = callback;
|
||||||
const unlistenPromise = invoke<WatchResult>('cmd_sync_watch', {
|
const unlistenPromise = invoke<WatchResult>("cmd_sync_watch", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
syncDir,
|
syncDir,
|
||||||
channel,
|
channel,
|
||||||
@@ -46,7 +46,7 @@ export function watchWorkspaceFiles(
|
|||||||
return () =>
|
return () =>
|
||||||
unlistenPromise
|
unlistenPromise
|
||||||
.then(async ({ unlistenEvent }) => {
|
.then(async ({ unlistenEvent }) => {
|
||||||
console.log('Unwatching workspace files', workspaceId, syncDir);
|
console.log("Unwatching workspace files", workspaceId, syncDir);
|
||||||
unlistenToWatcher(unlistenEvent);
|
unlistenToWatcher(unlistenEvent);
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
@@ -59,11 +59,11 @@ function unlistenToWatcher(unlistenEvent: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWatchKeys() {
|
function getWatchKeys() {
|
||||||
return sessionStorage.getItem('workspace-file-watchers')?.split(',').filter(Boolean) ?? [];
|
return sessionStorage.getItem("workspace-file-watchers")?.split(",").filter(Boolean) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWatchKeys(keys: string[]) {
|
function setWatchKeys(keys: string[]) {
|
||||||
sessionStorage.setItem('workspace-file-watchers', keys.join(','));
|
sessionStorage.setItem("workspace-file-watchers", keys.join(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWatchKey(key: string) {
|
function addWatchKey(key: string) {
|
||||||
@@ -79,6 +79,6 @@ function removeWatchKey(key: string) {
|
|||||||
// On page load, unlisten to all zombie watchers
|
// On page load, unlisten to all zombie watchers
|
||||||
const keys = getWatchKeys();
|
const keys = getWatchKeys();
|
||||||
if (keys.length > 0) {
|
if (keys.length > 0) {
|
||||||
console.log('Unsubscribing to zombie file watchers', keys);
|
console.log("Unsubscribing to zombie file watchers", keys);
|
||||||
keys.forEach(unlistenToWatcher);
|
keys.forEach(unlistenToWatcher);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/sync",
|
"name": "@yaakapp-internal/sync",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
const { execSync } = require('node:child_process');
|
const { execSync } = require("node:child_process");
|
||||||
const fs = require('node:fs');
|
const fs = require("node:fs");
|
||||||
const path = require('node:path');
|
const path = require("node:path");
|
||||||
|
|
||||||
if (process.env.SKIP_WASM_BUILD === '1') {
|
if (process.env.SKIP_WASM_BUILD === "1") {
|
||||||
console.log('Skipping wasm-pack build (SKIP_WASM_BUILD=1)');
|
console.log("Skipping wasm-pack build (SKIP_WASM_BUILD=1)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
execSync('wasm-pack build --target bundler', { stdio: 'inherit' });
|
execSync("wasm-pack build --target bundler", { stdio: "inherit" });
|
||||||
|
|
||||||
// Rewrite the generated entry to use Vite's ?init import style instead of
|
// Rewrite the generated entry to use Vite's ?init import style instead of
|
||||||
// the ES Module Integration style that wasm-pack generates, which Vite/rolldown
|
// the ES Module Integration style that wasm-pack generates, which Vite/rolldown
|
||||||
// does not support in production builds.
|
// does not support in production builds.
|
||||||
const entry = path.join(__dirname, 'pkg', 'yaak_templates.js');
|
const entry = path.join(__dirname, "pkg", "yaak_templates.js");
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
entry,
|
entry,
|
||||||
[
|
[
|
||||||
@@ -20,8 +20,8 @@ fs.writeFileSync(
|
|||||||
'export * from "./yaak_templates_bg.js";',
|
'export * from "./yaak_templates_bg.js";',
|
||||||
'import * as bg from "./yaak_templates_bg.js";',
|
'import * as bg from "./yaak_templates_bg.js";',
|
||||||
'const instance = await init({ "./yaak_templates_bg.js": bg });',
|
'const instance = await init({ "./yaak_templates_bg.js": bg });',
|
||||||
'bg.__wbg_set_wasm(instance.exports);',
|
"bg.__wbg_set_wasm(instance.exports);",
|
||||||
'instance.exports.__wbindgen_start();',
|
"instance.exports.__wbindgen_start();",
|
||||||
'',
|
"",
|
||||||
].join('\n'),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export * from './bindings/parser';
|
export * from "./bindings/parser";
|
||||||
import { Tokens } from './bindings/parser';
|
import { Tokens } from "./bindings/parser";
|
||||||
import { escape_template, parse_template, unescape_template } from './pkg';
|
import { escape_template, parse_template, unescape_template } from "./pkg";
|
||||||
|
|
||||||
export function parseTemplate(template: string) {
|
export function parseTemplate(template: string) {
|
||||||
return parse_template(template) as Tokens;
|
return parse_template(template) as Tokens;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/templates",
|
"name": "@yaakapp-internal/templates",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "npm run build",
|
"bootstrap": "npm run build",
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ url = "2"
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["macros", "time", "test-util", "rt"] }
|
tokio = { workspace = true, features = ["macros", "time", "test-util", "rt"] }
|
||||||
tokio-tungstenite = { version = "0.26.2", default-features = false, features = ["rustls-tls-native-roots", "connect"] }
|
tokio-tungstenite = { version = "0.26.2", default-features = false, features = [
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
"connect",
|
||||||
|
] }
|
||||||
yaak-http = { workspace = true }
|
yaak-http = { workspace = true }
|
||||||
yaak-tls = { workspace = true }
|
yaak-tls = { workspace = true }
|
||||||
yaak-models = { workspace = true }
|
yaak-models = { workspace = true }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { WebsocketConnection } from '@yaakapp-internal/models';
|
import { WebsocketConnection } from "@yaakapp-internal/models";
|
||||||
|
|
||||||
export function deleteWebsocketConnections(requestId: string) {
|
export function deleteWebsocketConnections(requestId: string) {
|
||||||
return invoke('cmd_ws_delete_connections', {
|
return invoke("cmd_ws_delete_connections", {
|
||||||
requestId,
|
requestId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ export function connectWebsocket({
|
|||||||
environmentId: string | null;
|
environmentId: string | null;
|
||||||
cookieJarId: string | null;
|
cookieJarId: string | null;
|
||||||
}) {
|
}) {
|
||||||
return invoke('cmd_ws_connect', {
|
return invoke("cmd_ws_connect", {
|
||||||
requestId,
|
requestId,
|
||||||
environmentId,
|
environmentId,
|
||||||
cookieJarId,
|
cookieJarId,
|
||||||
@@ -24,7 +24,7 @@ export function connectWebsocket({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function closeWebsocket({ connectionId }: { connectionId: string }) {
|
export function closeWebsocket({ connectionId }: { connectionId: string }) {
|
||||||
return invoke('cmd_ws_close', {
|
return invoke("cmd_ws_close", {
|
||||||
connectionId,
|
connectionId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ export function sendWebsocket({
|
|||||||
connectionId: string;
|
connectionId: string;
|
||||||
environmentId: string | null;
|
environmentId: string | null;
|
||||||
}) {
|
}) {
|
||||||
return invoke('cmd_ws_send', {
|
return invoke("cmd_ws_send", {
|
||||||
connectionId,
|
connectionId,
|
||||||
environmentId,
|
environmentId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/ws",
|
"name": "@yaakapp-internal/ws",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["darwin"],
|
"os": [
|
||||||
"cpu": ["arm64"]
|
"darwin"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["darwin"],
|
"os": [
|
||||||
"cpu": ["x64"]
|
"darwin"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["linux"],
|
"os": [
|
||||||
"cpu": ["arm64"]
|
"linux"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["linux"],
|
"os": [
|
||||||
"cpu": ["x64"]
|
"linux"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["win32"],
|
"os": [
|
||||||
"cpu": ["arm64"]
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"os": ["win32"],
|
"os": [
|
||||||
"cpu": ["x64"]
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const BINARY_DISTRIBUTION_PACKAGES = {
|
|||||||
linux_arm64: "@yaakapp/cli-linux-arm64",
|
linux_arm64: "@yaakapp/cli-linux-arm64",
|
||||||
linux_x64: "@yaakapp/cli-linux-x64",
|
linux_x64: "@yaakapp/cli-linux-x64",
|
||||||
win32_x64: "@yaakapp/cli-win32-x64",
|
win32_x64: "@yaakapp/cli-win32-x64",
|
||||||
win32_arm64: "@yaakapp/cli-win32-arm64"
|
win32_arm64: "@yaakapp/cli-win32-arm64",
|
||||||
};
|
};
|
||||||
|
|
||||||
const BINARY_DISTRIBUTION_VERSION = require("./package.json").version;
|
const BINARY_DISTRIBUTION_VERSION = require("./package.json").version;
|
||||||
@@ -16,5 +16,5 @@ module.exports = {
|
|||||||
BINARY_DISTRIBUTION_PACKAGES,
|
BINARY_DISTRIBUTION_PACKAGES,
|
||||||
BINARY_DISTRIBUTION_VERSION,
|
BINARY_DISTRIBUTION_VERSION,
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
PLATFORM_SPECIFIC_PACKAGE_NAME
|
PLATFORM_SPECIFIC_PACKAGE_NAME,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const https = require("node:https");
|
|||||||
const {
|
const {
|
||||||
BINARY_DISTRIBUTION_VERSION,
|
BINARY_DISTRIBUTION_VERSION,
|
||||||
BINARY_NAME,
|
BINARY_NAME,
|
||||||
PLATFORM_SPECIFIC_PACKAGE_NAME
|
PLATFORM_SPECIFIC_PACKAGE_NAME,
|
||||||
} = require("./common");
|
} = require("./common");
|
||||||
|
|
||||||
const fallbackBinaryPath = path.join(__dirname, BINARY_NAME);
|
const fallbackBinaryPath = path.join(__dirname, BINARY_NAME);
|
||||||
@@ -27,8 +27,8 @@ function makeRequest(url) {
|
|||||||
} else {
|
} else {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`npm responded with status code ${response.statusCode} when downloading package ${url}`
|
`npm responded with status code ${response.statusCode} when downloading package ${url}`,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp/cli",
|
"name": "@yaakapp/cli",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"main": "./index.js",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"postinstall": "node ./install.js",
|
|
||||||
"prepublishOnly": "node ./prepublish.js"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaak": "bin/cli.js",
|
"yaak": "bin/cli.js",
|
||||||
"yaakcli": "bin/cli.js"
|
"yaakcli": "bin/cli.js"
|
||||||
},
|
},
|
||||||
|
"main": "./index.js",
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "node ./install.js",
|
||||||
|
"prepublishOnly": "node ./prepublish.js"
|
||||||
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@yaakapp/cli-darwin-x64": "0.0.1",
|
|
||||||
"@yaakapp/cli-darwin-arm64": "0.0.1",
|
"@yaakapp/cli-darwin-arm64": "0.0.1",
|
||||||
|
"@yaakapp/cli-darwin-x64": "0.0.1",
|
||||||
"@yaakapp/cli-linux-arm64": "0.0.1",
|
"@yaakapp/cli-linux-arm64": "0.0.1",
|
||||||
"@yaakapp/cli-linux-x64": "0.0.1",
|
"@yaakapp/cli-linux-x64": "0.0.1",
|
||||||
"@yaakapp/cli-win32-x64": "0.0.1",
|
"@yaakapp/cli-win32-arm64": "0.0.1",
|
||||||
"@yaakapp/cli-win32-arm64": "0.0.1"
|
"@yaakapp/cli-win32-x64": "0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,34 +14,34 @@ const packages = [
|
|||||||
"cli-linux-arm64",
|
"cli-linux-arm64",
|
||||||
"cli-linux-x64",
|
"cli-linux-x64",
|
||||||
"cli-win32-arm64",
|
"cli-win32-arm64",
|
||||||
"cli-win32-x64"
|
"cli-win32-x64",
|
||||||
];
|
];
|
||||||
|
|
||||||
const binaries = [
|
const binaries = [
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-darwin-arm64", "yaak"),
|
src: join(__dirname, "dist", "cli-darwin-arm64", "yaak"),
|
||||||
dest: join(__dirname, "cli-darwin-arm64", "bin", "yaak")
|
dest: join(__dirname, "cli-darwin-arm64", "bin", "yaak"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-darwin-x64", "yaak"),
|
src: join(__dirname, "dist", "cli-darwin-x64", "yaak"),
|
||||||
dest: join(__dirname, "cli-darwin-x64", "bin", "yaak")
|
dest: join(__dirname, "cli-darwin-x64", "bin", "yaak"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-linux-arm64", "yaak"),
|
src: join(__dirname, "dist", "cli-linux-arm64", "yaak"),
|
||||||
dest: join(__dirname, "cli-linux-arm64", "bin", "yaak")
|
dest: join(__dirname, "cli-linux-arm64", "bin", "yaak"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-linux-x64", "yaak"),
|
src: join(__dirname, "dist", "cli-linux-x64", "yaak"),
|
||||||
dest: join(__dirname, "cli-linux-x64", "bin", "yaak")
|
dest: join(__dirname, "cli-linux-x64", "bin", "yaak"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-win32-arm64", "yaak.exe"),
|
src: join(__dirname, "dist", "cli-win32-arm64", "yaak.exe"),
|
||||||
dest: join(__dirname, "cli-win32-arm64", "bin", "yaak.exe")
|
dest: join(__dirname, "cli-win32-arm64", "bin", "yaak.exe"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: join(__dirname, "dist", "cli-win32-x64", "yaak.exe"),
|
src: join(__dirname, "dist", "cli-win32-x64", "yaak.exe"),
|
||||||
dest: join(__dirname, "cli-win32-x64", "bin", "yaak.exe")
|
dest: join(__dirname, "cli-win32-x64", "bin", "yaak.exe"),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const { src, dest } of binaries) {
|
for (const { src, dest } of binaries) {
|
||||||
@@ -67,7 +67,7 @@ for (const pkg of packages) {
|
|||||||
"@yaakapp/cli-linux-arm64": version,
|
"@yaakapp/cli-linux-arm64": version,
|
||||||
"@yaakapp/cli-linux-x64": version,
|
"@yaakapp/cli-linux-x64": version,
|
||||||
"@yaakapp/cli-win32-x64": version,
|
"@yaakapp/cli-win32-x64": version,
|
||||||
"@yaakapp/cli-win32-arm64": version
|
"@yaakapp/cli-win32-arm64": version,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "yaak-app",
|
"name": "yaak-app",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||||
@@ -82,6 +82,7 @@
|
|||||||
"vendor:vendor-plugins": "node scripts/vendor-plugins.cjs",
|
"vendor:vendor-plugins": "node scripts/vendor-plugins.cjs",
|
||||||
"vendor:vendor-protoc": "node scripts/vendor-protoc.cjs",
|
"vendor:vendor-protoc": "node scripts/vendor-protoc.cjs",
|
||||||
"vendor:vendor-node": "node scripts/vendor-node.cjs",
|
"vendor:vendor-node": "node scripts/vendor-node.cjs",
|
||||||
|
"format": "vp fmt --ignore-path .oxfmtignore",
|
||||||
"lint": "run-p lint:*",
|
"lint": "run-p lint:*",
|
||||||
"lint:vp": "vp lint",
|
"lint:vp": "vp lint",
|
||||||
"lint:workspaces": "npm run --workspaces --if-present lint",
|
"lint:workspaces": "npm run --workspaces --if-present lint",
|
||||||
@@ -90,10 +91,12 @@
|
|||||||
"tauri-before-build": "npm run bootstrap",
|
"tauri-before-build": "npm run bootstrap",
|
||||||
"tauri-before-dev": "node scripts/run-workspaces-dev.mjs"
|
"tauri-before-dev": "node scripts/run-workspaces-dev.mjs"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"dependencies": {
|
||||||
"js-yaml": "^4.1.1",
|
"@codemirror/lang-go": "^6.0.1",
|
||||||
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
"@codemirror/lang-java": "^6.0.2",
|
||||||
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
"@codemirror/lang-php": "^6.0.2",
|
||||||
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@codemirror/legacy-modes": "^6.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.9.6",
|
"@tauri-apps/cli": "^2.9.6",
|
||||||
@@ -105,12 +108,10 @@
|
|||||||
"vite-plus": "latest",
|
"vite-plus": "latest",
|
||||||
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"overrides": {
|
||||||
"@codemirror/lang-go": "^6.0.1",
|
"js-yaml": "^4.1.1",
|
||||||
"@codemirror/lang-java": "^6.0.2",
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
||||||
"@codemirror/lang-php": "^6.0.2",
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
||||||
"@codemirror/lang-python": "^6.2.1",
|
|
||||||
"@codemirror/legacy-modes": "^6.5.2"
|
|
||||||
},
|
},
|
||||||
"packageManager": "npm@11.11.1"
|
"packageManager": "npm@11.11.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ export function formatSize(bytes: number): string {
|
|||||||
|
|
||||||
if (bytes > 1000 * 1000 * 1000) {
|
if (bytes > 1000 * 1000 * 1000) {
|
||||||
num = bytes / 1000 / 1000 / 1000;
|
num = bytes / 1000 / 1000 / 1000;
|
||||||
unit = 'GB';
|
unit = "GB";
|
||||||
} else if (bytes > 1000 * 1000) {
|
} else if (bytes > 1000 * 1000) {
|
||||||
num = bytes / 1000 / 1000;
|
num = bytes / 1000 / 1000;
|
||||||
unit = 'MB';
|
unit = "MB";
|
||||||
} else if (bytes > 1000) {
|
} else if (bytes > 1000) {
|
||||||
num = bytes / 1000;
|
num = bytes / 1000;
|
||||||
unit = 'KB';
|
unit = "KB";
|
||||||
} else {
|
} else {
|
||||||
num = bytes;
|
num = bytes;
|
||||||
unit = 'B';
|
unit = "B";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${Math.round(num * 10) / 10} ${unit}`;
|
return `${Math.round(num * 10) / 10} ${unit}`;
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from './debounce';
|
export * from "./debounce";
|
||||||
export * from './formatSize';
|
export * from "./formatSize";
|
||||||
export * from './templateFunction';
|
export * from "./templateFunction";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp-internal/lib",
|
"name": "@yaakapp-internal/lib",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "index.ts"
|
"main": "index.ts"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ import type {
|
|||||||
CallTemplateFunctionArgs,
|
CallTemplateFunctionArgs,
|
||||||
JsonPrimitive,
|
JsonPrimitive,
|
||||||
TemplateFunctionArg,
|
TemplateFunctionArg,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from "@yaakapp-internal/plugins";
|
||||||
|
|
||||||
export function validateTemplateFunctionArgs(
|
export function validateTemplateFunctionArgs(
|
||||||
fnName: string,
|
fnName: string,
|
||||||
args: TemplateFunctionArg[],
|
args: TemplateFunctionArg[],
|
||||||
values: CallTemplateFunctionArgs['values'],
|
values: CallTemplateFunctionArgs["values"],
|
||||||
): string | null {
|
): string | null {
|
||||||
for (const arg of args) {
|
for (const arg of args) {
|
||||||
if ('inputs' in arg && arg.inputs) {
|
if ("inputs" in arg && arg.inputs) {
|
||||||
// Recurse down
|
// Recurse down
|
||||||
const err = validateTemplateFunctionArgs(fnName, arg.inputs, values);
|
const err = validateTemplateFunctionArgs(fnName, arg.inputs, values);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
}
|
}
|
||||||
if (!('name' in arg)) continue;
|
if (!("name" in arg)) continue;
|
||||||
if (arg.optional) continue;
|
if (arg.optional) continue;
|
||||||
if (arg.defaultValue != null) continue;
|
if (arg.defaultValue != null) continue;
|
||||||
if (arg.hidden) continue;
|
if (arg.hidden) continue;
|
||||||
@@ -34,14 +34,14 @@ export function applyFormInputDefaults(
|
|||||||
) {
|
) {
|
||||||
let newValues: { [p: string]: JsonPrimitive | undefined } = { ...values };
|
let newValues: { [p: string]: JsonPrimitive | undefined } = { ...values };
|
||||||
for (const input of inputs) {
|
for (const input of inputs) {
|
||||||
if ('defaultValue' in input && values[input.name] === undefined) {
|
if ("defaultValue" in input && values[input.name] === undefined) {
|
||||||
newValues[input.name] = input.defaultValue;
|
newValues[input.name] = input.defaultValue;
|
||||||
}
|
}
|
||||||
if (input.type === 'checkbox' && values[input.name] === undefined) {
|
if (input.type === "checkbox" && values[input.name] === undefined) {
|
||||||
newValues[input.name] = false;
|
newValues[input.name] = false;
|
||||||
}
|
}
|
||||||
// Recurse down to all child inputs
|
// Recurse down to all child inputs
|
||||||
if ('inputs' in input) {
|
if ("inputs" in input) {
|
||||||
newValues = applyFormInputDefaults(input.inputs ?? [], newValues);
|
newValues = applyFormInputDefaults(input.inputs ?? [], newValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,23 +3,23 @@
|
|||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"api-client",
|
"api-client",
|
||||||
"insomnia-alternative",
|
|
||||||
"bruno-alternative",
|
"bruno-alternative",
|
||||||
|
"insomnia-alternative",
|
||||||
"postman-alternative"
|
"postman-alternative"
|
||||||
],
|
],
|
||||||
|
"homepage": "https://yaak.app",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://feedback.yaak.app"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mountain-loop/yaak"
|
"url": "https://github.com/mountain-loop/yaak"
|
||||||
},
|
},
|
||||||
"bugs": {
|
|
||||||
"url": "https://feedback.yaak.app"
|
|
||||||
},
|
|
||||||
"homepage": "https://yaak.app",
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"typings": "./lib/index.d.ts",
|
|
||||||
"files": [
|
"files": [
|
||||||
"lib/**/*"
|
"lib/**/*"
|
||||||
],
|
],
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"typings": "./lib/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "npm run build",
|
"bootstrap": "npm run build",
|
||||||
"build": "run-s build:copy-types build:tsc",
|
"build": "run-s build:copy-types build:tsc",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export type * from './plugins';
|
export type * from "./plugins";
|
||||||
export type * from './themes';
|
export type * from "./themes";
|
||||||
|
|
||||||
export * from './bindings/gen_models';
|
export * from "./bindings/gen_models";
|
||||||
export * from './bindings/gen_events';
|
export * from "./bindings/gen_events";
|
||||||
|
|
||||||
// Some extras for utility
|
// Some extras for utility
|
||||||
|
|
||||||
export type { PartialImportResources } from './plugins/ImporterPlugin';
|
export type { PartialImportResources } from "./plugins/ImporterPlugin";
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import type {
|
|||||||
FormInput,
|
FormInput,
|
||||||
GetHttpAuthenticationSummaryResponse,
|
GetHttpAuthenticationSummaryResponse,
|
||||||
HttpAuthenticationAction,
|
HttpAuthenticationAction,
|
||||||
} from '../bindings/gen_events';
|
} from "../bindings/gen_events";
|
||||||
import type { MaybePromise } from '../helpers';
|
import type { MaybePromise } from "../helpers";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
type AddDynamicMethod<T> = {
|
type AddDynamicMethod<T> = {
|
||||||
dynamic?: (
|
dynamic?: (
|
||||||
@@ -19,13 +19,13 @@ type AddDynamicMethod<T> = {
|
|||||||
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
||||||
type AddDynamic<T> = T extends any
|
type AddDynamic<T> = T extends any
|
||||||
? T extends { inputs?: FormInput[] }
|
? T extends { inputs?: FormInput[] }
|
||||||
? Omit<T, 'inputs'> & {
|
? Omit<T, "inputs"> & {
|
||||||
inputs: Array<AddDynamic<FormInput>>;
|
inputs: Array<AddDynamic<FormInput>>;
|
||||||
dynamic?: (
|
dynamic?: (
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
args: CallHttpAuthenticationActionArgs,
|
args: CallHttpAuthenticationActionArgs,
|
||||||
) => MaybePromise<
|
) => MaybePromise<
|
||||||
Partial<Omit<T, 'inputs'> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
Partial<Omit<T, "inputs"> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
: T & AddDynamicMethod<T>
|
: T & AddDynamicMethod<T>
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ import type {
|
|||||||
ShowToastRequest,
|
ShowToastRequest,
|
||||||
TemplateRenderRequest,
|
TemplateRenderRequest,
|
||||||
WorkspaceInfo,
|
WorkspaceInfo,
|
||||||
} from '../bindings/gen_events.ts';
|
} from "../bindings/gen_events.ts";
|
||||||
import type { Folder, HttpRequest } from '../bindings/gen_models.ts';
|
import type { Folder, HttpRequest } from "../bindings/gen_models.ts";
|
||||||
import type { JsonValue } from '../bindings/serde_json/JsonValue';
|
import type { JsonValue } from "../bindings/serde_json/JsonValue";
|
||||||
import type { MaybePromise } from '../helpers';
|
import type { MaybePromise } from "../helpers";
|
||||||
|
|
||||||
export type CallPromptFormDynamicArgs = {
|
export type CallPromptFormDynamicArgs = {
|
||||||
values: { [key in string]?: JsonPrimitive };
|
values: { [key in string]?: JsonPrimitive };
|
||||||
@@ -45,13 +45,13 @@ type AddDynamicMethod<T> = {
|
|||||||
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
||||||
type AddDynamic<T> = T extends any
|
type AddDynamic<T> = T extends any
|
||||||
? T extends { inputs?: FormInput[] }
|
? T extends { inputs?: FormInput[] }
|
||||||
? Omit<T, 'inputs'> & {
|
? Omit<T, "inputs"> & {
|
||||||
inputs: Array<AddDynamic<FormInput>>;
|
inputs: Array<AddDynamic<FormInput>>;
|
||||||
dynamic?: (
|
dynamic?: (
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
args: CallPromptFormDynamicArgs,
|
args: CallPromptFormDynamicArgs,
|
||||||
) => MaybePromise<
|
) => MaybePromise<
|
||||||
Partial<Omit<T, 'inputs'> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
Partial<Omit<T, "inputs"> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
: T & AddDynamicMethod<T>
|
: T & AddDynamicMethod<T>
|
||||||
@@ -59,11 +59,11 @@ type AddDynamic<T> = T extends any
|
|||||||
|
|
||||||
export type DynamicPromptFormArg = AddDynamic<FormInput>;
|
export type DynamicPromptFormArg = AddDynamic<FormInput>;
|
||||||
|
|
||||||
type DynamicPromptFormRequest = Omit<PromptFormRequest, 'inputs'> & {
|
type DynamicPromptFormRequest = Omit<PromptFormRequest, "inputs"> & {
|
||||||
inputs: DynamicPromptFormArg[];
|
inputs: DynamicPromptFormArg[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceHandle = Pick<WorkspaceInfo, 'id' | 'name'>;
|
export type WorkspaceHandle = Pick<WorkspaceInfo, "id" | "name">;
|
||||||
|
|
||||||
export interface Context {
|
export interface Context {
|
||||||
clipboard: {
|
clipboard: {
|
||||||
@@ -73,8 +73,8 @@ export interface Context {
|
|||||||
show(args: ShowToastRequest): Promise<void>;
|
show(args: ShowToastRequest): Promise<void>;
|
||||||
};
|
};
|
||||||
prompt: {
|
prompt: {
|
||||||
text(args: PromptTextRequest): Promise<PromptTextResponse['value']>;
|
text(args: PromptTextRequest): Promise<PromptTextResponse["value"]>;
|
||||||
form(args: DynamicPromptFormRequest): Promise<PromptFormResponse['values']>;
|
form(args: DynamicPromptFormRequest): Promise<PromptFormResponse["values"]>;
|
||||||
};
|
};
|
||||||
store: {
|
store: {
|
||||||
set<T>(key: string, value: T): Promise<void>;
|
set<T>(key: string, value: T): Promise<void>;
|
||||||
@@ -94,41 +94,41 @@ export interface Context {
|
|||||||
openExternalUrl(url: string): Promise<void>;
|
openExternalUrl(url: string): Promise<void>;
|
||||||
};
|
};
|
||||||
cookies: {
|
cookies: {
|
||||||
listNames(): Promise<ListCookieNamesResponse['names']>;
|
listNames(): Promise<ListCookieNamesResponse["names"]>;
|
||||||
getValue(args: GetCookieValueRequest): Promise<GetCookieValueResponse['value']>;
|
getValue(args: GetCookieValueRequest): Promise<GetCookieValueResponse["value"]>;
|
||||||
};
|
};
|
||||||
grpcRequest: {
|
grpcRequest: {
|
||||||
render(args: RenderGrpcRequestRequest): Promise<RenderGrpcRequestResponse['grpcRequest']>;
|
render(args: RenderGrpcRequestRequest): Promise<RenderGrpcRequestResponse["grpcRequest"]>;
|
||||||
};
|
};
|
||||||
httpRequest: {
|
httpRequest: {
|
||||||
send(args: SendHttpRequestRequest): Promise<SendHttpRequestResponse['httpResponse']>;
|
send(args: SendHttpRequestRequest): Promise<SendHttpRequestResponse["httpResponse"]>;
|
||||||
getById(args: GetHttpRequestByIdRequest): Promise<GetHttpRequestByIdResponse['httpRequest']>;
|
getById(args: GetHttpRequestByIdRequest): Promise<GetHttpRequestByIdResponse["httpRequest"]>;
|
||||||
render(args: RenderHttpRequestRequest): Promise<RenderHttpRequestResponse['httpRequest']>;
|
render(args: RenderHttpRequestRequest): Promise<RenderHttpRequestResponse["httpRequest"]>;
|
||||||
list(args?: ListHttpRequestsRequest): Promise<ListHttpRequestsResponse['httpRequests']>;
|
list(args?: ListHttpRequestsRequest): Promise<ListHttpRequestsResponse["httpRequests"]>;
|
||||||
create(
|
create(
|
||||||
args: Omit<Partial<HttpRequest>, 'id' | 'model' | 'createdAt' | 'updatedAt'> &
|
args: Omit<Partial<HttpRequest>, "id" | "model" | "createdAt" | "updatedAt"> &
|
||||||
Pick<HttpRequest, 'workspaceId' | 'url'>,
|
Pick<HttpRequest, "workspaceId" | "url">,
|
||||||
): Promise<HttpRequest>;
|
): Promise<HttpRequest>;
|
||||||
update(
|
update(
|
||||||
args: Omit<Partial<HttpRequest>, 'model' | 'createdAt' | 'updatedAt'> &
|
args: Omit<Partial<HttpRequest>, "model" | "createdAt" | "updatedAt"> &
|
||||||
Pick<HttpRequest, 'id'>,
|
Pick<HttpRequest, "id">,
|
||||||
): Promise<HttpRequest>;
|
): Promise<HttpRequest>;
|
||||||
delete(args: { id: string }): Promise<HttpRequest>;
|
delete(args: { id: string }): Promise<HttpRequest>;
|
||||||
};
|
};
|
||||||
folder: {
|
folder: {
|
||||||
list(args?: ListFoldersRequest): Promise<ListFoldersResponse['folders']>;
|
list(args?: ListFoldersRequest): Promise<ListFoldersResponse["folders"]>;
|
||||||
getById(args: { id: string }): Promise<Folder | null>;
|
getById(args: { id: string }): Promise<Folder | null>;
|
||||||
create(
|
create(
|
||||||
args: Omit<Partial<Folder>, 'id' | 'model' | 'createdAt' | 'updatedAt'> &
|
args: Omit<Partial<Folder>, "id" | "model" | "createdAt" | "updatedAt"> &
|
||||||
Pick<Folder, 'workspaceId' | 'name'>,
|
Pick<Folder, "workspaceId" | "name">,
|
||||||
): Promise<Folder>;
|
): Promise<Folder>;
|
||||||
update(
|
update(
|
||||||
args: Omit<Partial<Folder>, 'model' | 'createdAt' | 'updatedAt'> & Pick<Folder, 'id'>,
|
args: Omit<Partial<Folder>, "model" | "createdAt" | "updatedAt"> & Pick<Folder, "id">,
|
||||||
): Promise<Folder>;
|
): Promise<Folder>;
|
||||||
delete(args: { id: string }): Promise<Folder>;
|
delete(args: { id: string }): Promise<Folder>;
|
||||||
};
|
};
|
||||||
httpResponse: {
|
httpResponse: {
|
||||||
find(args: FindHttpResponsesRequest): Promise<FindHttpResponsesResponse['httpResponses']>;
|
find(args: FindHttpResponsesRequest): Promise<FindHttpResponsesResponse["httpResponses"]>;
|
||||||
};
|
};
|
||||||
templates: {
|
templates: {
|
||||||
render<T extends JsonValue>(args: TemplateRenderRequest & { data: T }): Promise<T>;
|
render<T extends JsonValue>(args: TemplateRenderRequest & { data: T }): Promise<T>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { FilterResponse } from '../bindings/gen_events';
|
import type { FilterResponse } from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type FilterPlugin = {
|
export type FilterPlugin = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CallFolderActionArgs, FolderAction } from '../bindings/gen_events';
|
import type { CallFolderActionArgs, FolderAction } from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type FolderActionPlugin = FolderAction & {
|
export type FolderActionPlugin = FolderAction & {
|
||||||
onSelect(ctx: Context, args: CallFolderActionArgs): Promise<void> | void;
|
onSelect(ctx: Context, args: CallFolderActionArgs): Promise<void> | void;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CallGrpcRequestActionArgs, GrpcRequestAction } from '../bindings/gen_events';
|
import type { CallGrpcRequestActionArgs, GrpcRequestAction } from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type GrpcRequestActionPlugin = GrpcRequestAction & {
|
export type GrpcRequestActionPlugin = GrpcRequestAction & {
|
||||||
onSelect(ctx: Context, args: CallGrpcRequestActionArgs): Promise<void> | void;
|
onSelect(ctx: Context, args: CallGrpcRequestActionArgs): Promise<void> | void;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CallHttpRequestActionArgs, HttpRequestAction } from '../bindings/gen_events';
|
import type { CallHttpRequestActionArgs, HttpRequestAction } from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type HttpRequestActionPlugin = HttpRequestAction & {
|
export type HttpRequestActionPlugin = HttpRequestAction & {
|
||||||
onSelect(ctx: Context, args: CallHttpRequestActionArgs): Promise<void> | void;
|
onSelect(ctx: Context, args: CallHttpRequestActionArgs): Promise<void> | void;
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import type { ImportResources } from '../bindings/gen_events';
|
import type { ImportResources } from "../bindings/gen_events";
|
||||||
import type { AtLeast, MaybePromise } from '../helpers';
|
import type { AtLeast, MaybePromise } from "../helpers";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
type RootFields = 'name' | 'id' | 'model';
|
type RootFields = "name" | "id" | "model";
|
||||||
type CommonFields = RootFields | 'workspaceId';
|
type CommonFields = RootFields | "workspaceId";
|
||||||
|
|
||||||
export type PartialImportResources = {
|
export type PartialImportResources = {
|
||||||
workspaces: Array<AtLeast<ImportResources['workspaces'][0], RootFields>>;
|
workspaces: Array<AtLeast<ImportResources["workspaces"][0], RootFields>>;
|
||||||
environments: Array<AtLeast<ImportResources['environments'][0], CommonFields>>;
|
environments: Array<AtLeast<ImportResources["environments"][0], CommonFields>>;
|
||||||
folders: Array<AtLeast<ImportResources['folders'][0], CommonFields>>;
|
folders: Array<AtLeast<ImportResources["folders"][0], CommonFields>>;
|
||||||
httpRequests: Array<AtLeast<ImportResources['httpRequests'][0], CommonFields>>;
|
httpRequests: Array<AtLeast<ImportResources["httpRequests"][0], CommonFields>>;
|
||||||
grpcRequests: Array<AtLeast<ImportResources['grpcRequests'][0], CommonFields>>;
|
grpcRequests: Array<AtLeast<ImportResources["grpcRequests"][0], CommonFields>>;
|
||||||
websocketRequests: Array<AtLeast<ImportResources['websocketRequests'][0], CommonFields>>;
|
websocketRequests: Array<AtLeast<ImportResources["websocketRequests"][0], CommonFields>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ImportPluginResponse = null | {
|
export type ImportPluginResponse = null | {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CallTemplateFunctionArgs, FormInput, TemplateFunction } from '../bindings/gen_events';
|
import type { CallTemplateFunctionArgs, FormInput, TemplateFunction } from "../bindings/gen_events";
|
||||||
import type { MaybePromise } from '../helpers';
|
import type { MaybePromise } from "../helpers";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
type AddDynamicMethod<T> = {
|
type AddDynamicMethod<T> = {
|
||||||
dynamic?: (
|
dynamic?: (
|
||||||
@@ -12,13 +12,13 @@ type AddDynamicMethod<T> = {
|
|||||||
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
// oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern
|
||||||
type AddDynamic<T> = T extends any
|
type AddDynamic<T> = T extends any
|
||||||
? T extends { inputs?: FormInput[] }
|
? T extends { inputs?: FormInput[] }
|
||||||
? Omit<T, 'inputs'> & {
|
? Omit<T, "inputs"> & {
|
||||||
inputs: Array<AddDynamic<FormInput>>;
|
inputs: Array<AddDynamic<FormInput>>;
|
||||||
dynamic?: (
|
dynamic?: (
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
args: CallTemplateFunctionArgs,
|
args: CallTemplateFunctionArgs,
|
||||||
) => MaybePromise<
|
) => MaybePromise<
|
||||||
Partial<Omit<T, 'inputs'> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
Partial<Omit<T, "inputs"> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
: T & AddDynamicMethod<T>
|
: T & AddDynamicMethod<T>
|
||||||
@@ -26,7 +26,7 @@ type AddDynamic<T> = T extends any
|
|||||||
|
|
||||||
export type DynamicTemplateFunctionArg = AddDynamic<FormInput>;
|
export type DynamicTemplateFunctionArg = AddDynamic<FormInput>;
|
||||||
|
|
||||||
export type TemplateFunctionPlugin = Omit<TemplateFunction, 'args'> & {
|
export type TemplateFunctionPlugin = Omit<TemplateFunction, "args"> & {
|
||||||
args: DynamicTemplateFunctionArg[];
|
args: DynamicTemplateFunctionArg[];
|
||||||
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;
|
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import type { Theme } from '../bindings/gen_events';
|
import type { Theme } from "../bindings/gen_events";
|
||||||
|
|
||||||
export type ThemePlugin = Theme;
|
export type ThemePlugin = Theme;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
CallWebsocketRequestActionArgs,
|
CallWebsocketRequestActionArgs,
|
||||||
WebsocketRequestAction,
|
WebsocketRequestAction,
|
||||||
} from '../bindings/gen_events';
|
} from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type WebsocketRequestActionPlugin = WebsocketRequestAction & {
|
export type WebsocketRequestActionPlugin = WebsocketRequestAction & {
|
||||||
onSelect(ctx: Context, args: CallWebsocketRequestActionArgs): Promise<void> | void;
|
onSelect(ctx: Context, args: CallWebsocketRequestActionArgs): Promise<void> | void;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CallWorkspaceActionArgs, WorkspaceAction } from '../bindings/gen_events';
|
import type { CallWorkspaceActionArgs, WorkspaceAction } from "../bindings/gen_events";
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
|
|
||||||
export type WorkspaceActionPlugin = WorkspaceAction & {
|
export type WorkspaceActionPlugin = WorkspaceAction & {
|
||||||
onSelect(ctx: Context, args: CallWorkspaceActionArgs): Promise<void> | void;
|
onSelect(ctx: Context, args: CallWorkspaceActionArgs): Promise<void> | void;
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import type { AuthenticationPlugin } from './AuthenticationPlugin';
|
import type { AuthenticationPlugin } from "./AuthenticationPlugin";
|
||||||
|
|
||||||
import type { Context } from './Context';
|
import type { Context } from "./Context";
|
||||||
import type { FilterPlugin } from './FilterPlugin';
|
import type { FilterPlugin } from "./FilterPlugin";
|
||||||
import type { FolderActionPlugin } from './FolderActionPlugin';
|
import type { FolderActionPlugin } from "./FolderActionPlugin";
|
||||||
import type { GrpcRequestActionPlugin } from './GrpcRequestActionPlugin';
|
import type { GrpcRequestActionPlugin } from "./GrpcRequestActionPlugin";
|
||||||
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
|
import type { HttpRequestActionPlugin } from "./HttpRequestActionPlugin";
|
||||||
import type { ImporterPlugin } from './ImporterPlugin';
|
import type { ImporterPlugin } from "./ImporterPlugin";
|
||||||
import type { TemplateFunctionPlugin } from './TemplateFunctionPlugin';
|
import type { TemplateFunctionPlugin } from "./TemplateFunctionPlugin";
|
||||||
import type { ThemePlugin } from './ThemePlugin';
|
import type { ThemePlugin } from "./ThemePlugin";
|
||||||
import type { WebsocketRequestActionPlugin } from './WebsocketRequestActionPlugin';
|
import type { WebsocketRequestActionPlugin } from "./WebsocketRequestActionPlugin";
|
||||||
import type { WorkspaceActionPlugin } from './WorkspaceActionPlugin';
|
import type { WorkspaceActionPlugin } from "./WorkspaceActionPlugin";
|
||||||
|
|
||||||
export type { Context };
|
export type { Context };
|
||||||
export type { DynamicAuthenticationArg } from './AuthenticationPlugin';
|
export type { DynamicAuthenticationArg } from "./AuthenticationPlugin";
|
||||||
export type { CallPromptFormDynamicArgs, DynamicPromptFormArg } from './Context';
|
export type { CallPromptFormDynamicArgs, DynamicPromptFormArg } from "./Context";
|
||||||
export type { DynamicTemplateFunctionArg } from './TemplateFunctionPlugin';
|
export type { DynamicTemplateFunctionArg } from "./TemplateFunctionPlugin";
|
||||||
export type { TemplateFunctionPlugin };
|
export type { TemplateFunctionPlugin };
|
||||||
export type { FolderActionPlugin } from './FolderActionPlugin';
|
export type { FolderActionPlugin } from "./FolderActionPlugin";
|
||||||
export type { WorkspaceActionPlugin } from './WorkspaceActionPlugin';
|
export type { WorkspaceActionPlugin } from "./WorkspaceActionPlugin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global structure of a Yaak plugin
|
* The global structure of a Yaak plugin
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { InternalEvent } from '@yaakapp/api';
|
import type { InternalEvent } from "@yaakapp/api";
|
||||||
|
|
||||||
export class EventChannel {
|
export class EventChannel {
|
||||||
#listeners = new Set<(event: InternalEvent) => void>();
|
#listeners = new Set<(event: InternalEvent) => void>();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { BootRequest, InternalEvent } from '@yaakapp/api';
|
import type { BootRequest, InternalEvent } from "@yaakapp/api";
|
||||||
import type { PluginContext } from '@yaakapp-internal/plugins';
|
import type { PluginContext } from "@yaakapp-internal/plugins";
|
||||||
import type { EventChannel } from './EventChannel';
|
import type { EventChannel } from "./EventChannel";
|
||||||
import { PluginInstance, type PluginWorkerData } from './PluginInstance';
|
import { PluginInstance, type PluginWorkerData } from "./PluginInstance";
|
||||||
|
|
||||||
export class PluginHandle {
|
export class PluginHandle {
|
||||||
#instance: PluginInstance;
|
#instance: PluginInstance;
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import console from 'node:console';
|
import console from "node:console";
|
||||||
import { type Stats, statSync, watch } from 'node:fs';
|
import { type Stats, statSync, watch } from "node:fs";
|
||||||
import path from 'node:path';
|
import path from "node:path";
|
||||||
import type {
|
import type {
|
||||||
CallPromptFormDynamicArgs,
|
CallPromptFormDynamicArgs,
|
||||||
Context,
|
Context,
|
||||||
DynamicPromptFormArg,
|
DynamicPromptFormArg,
|
||||||
PluginDefinition,
|
PluginDefinition,
|
||||||
} from '@yaakapp/api';
|
} from "@yaakapp/api";
|
||||||
import {
|
import {
|
||||||
applyFormInputDefaults,
|
applyFormInputDefaults,
|
||||||
validateTemplateFunctionArgs,
|
validateTemplateFunctionArgs,
|
||||||
} from '@yaakapp-internal/lib/templateFunction';
|
} from "@yaakapp-internal/lib/templateFunction";
|
||||||
import type {
|
import type {
|
||||||
BootRequest,
|
BootRequest,
|
||||||
DeleteKeyValueResponse,
|
DeleteKeyValueResponse,
|
||||||
@@ -45,10 +45,10 @@ import type {
|
|||||||
TemplateRenderResponse,
|
TemplateRenderResponse,
|
||||||
UpsertModelResponse,
|
UpsertModelResponse,
|
||||||
WindowInfoResponse,
|
WindowInfoResponse,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from "@yaakapp-internal/plugins";
|
||||||
import { applyDynamicFormInput } from './common';
|
import { applyDynamicFormInput } from "./common";
|
||||||
import { EventChannel } from './EventChannel';
|
import { EventChannel } from "./EventChannel";
|
||||||
import { migrateTemplateFunctionSelectOptions } from './migrations';
|
import { migrateTemplateFunctionSelectOptions } from "./migrations";
|
||||||
|
|
||||||
export interface PluginWorkerData {
|
export interface PluginWorkerData {
|
||||||
bootRequest: BootRequest;
|
bootRequest: BootRequest;
|
||||||
@@ -84,16 +84,16 @@ export class PluginInstance {
|
|||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
workerData.context,
|
workerData.context,
|
||||||
{
|
{
|
||||||
type: 'reload_response',
|
type: "reload_response",
|
||||||
silent: false,
|
silent: false,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
await ctx.toast.show({
|
await ctx.toast.show({
|
||||||
message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split('/').pop()}: ${err instanceof Error ? err.message : String(err)}`,
|
message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split("/").pop()}: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
color: 'notice',
|
color: "notice",
|
||||||
icon: 'alert_triangle',
|
icon: "alert_triangle",
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -123,15 +123,15 @@ export class PluginInstance {
|
|||||||
const { context, payload, id: replyId } = event;
|
const { context, payload, id: replyId } = event;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (payload.type === 'boot_request') {
|
if (payload.type === "boot_request") {
|
||||||
await this.#mod?.init?.(ctx);
|
await this.#mod?.init?.(ctx);
|
||||||
this.#sendPayload(context, { type: 'boot_response' }, replyId);
|
this.#sendPayload(context, { type: "boot_response" }, replyId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'terminate_request') {
|
if (payload.type === "terminate_request") {
|
||||||
const payload: InternalEventPayload = {
|
const payload: InternalEventPayload = {
|
||||||
type: 'terminate_response',
|
type: "terminate_response",
|
||||||
};
|
};
|
||||||
await this.terminate();
|
await this.terminate();
|
||||||
this.#sendPayload(context, payload, replyId);
|
this.#sendPayload(context, payload, replyId);
|
||||||
@@ -139,15 +139,15 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'import_request' &&
|
payload.type === "import_request" &&
|
||||||
typeof this.#mod?.importer?.onImport === 'function'
|
typeof this.#mod?.importer?.onImport === "function"
|
||||||
) {
|
) {
|
||||||
const reply = await this.#mod.importer.onImport(ctx, {
|
const reply = await this.#mod.importer.onImport(ctx, {
|
||||||
text: payload.content,
|
text: payload.content,
|
||||||
});
|
});
|
||||||
if (reply != null) {
|
if (reply != null) {
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'import_response',
|
type: "import_response",
|
||||||
resources: reply.resources as ImportResources,
|
resources: reply.resources as ImportResources,
|
||||||
};
|
};
|
||||||
this.#sendPayload(context, replyPayload, replyId);
|
this.#sendPayload(context, replyPayload, replyId);
|
||||||
@@ -157,18 +157,18 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'filter_request' && typeof this.#mod?.filter?.onFilter === 'function') {
|
if (payload.type === "filter_request" && typeof this.#mod?.filter?.onFilter === "function") {
|
||||||
const reply = await this.#mod.filter.onFilter(ctx, {
|
const reply = await this.#mod.filter.onFilter(ctx, {
|
||||||
filter: payload.filter,
|
filter: payload.filter,
|
||||||
payload: payload.content,
|
payload: payload.content,
|
||||||
mimeType: payload.type,
|
mimeType: payload.type,
|
||||||
});
|
});
|
||||||
this.#sendPayload(context, { type: 'filter_response', ...reply }, replyId);
|
this.#sendPayload(context, { type: "filter_response", ...reply }, replyId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_grpc_request_actions_request' &&
|
payload.type === "get_grpc_request_actions_request" &&
|
||||||
Array.isArray(this.#mod?.grpcRequestActions)
|
Array.isArray(this.#mod?.grpcRequestActions)
|
||||||
) {
|
) {
|
||||||
const reply: GrpcRequestAction[] = this.#mod.grpcRequestActions.map((a) => ({
|
const reply: GrpcRequestAction[] = this.#mod.grpcRequestActions.map((a) => ({
|
||||||
@@ -177,7 +177,7 @@ export class PluginInstance {
|
|||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_grpc_request_actions_response',
|
type: "get_grpc_request_actions_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
actions: reply,
|
actions: reply,
|
||||||
};
|
};
|
||||||
@@ -186,7 +186,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_http_request_actions_request' &&
|
payload.type === "get_http_request_actions_request" &&
|
||||||
Array.isArray(this.#mod?.httpRequestActions)
|
Array.isArray(this.#mod?.httpRequestActions)
|
||||||
) {
|
) {
|
||||||
const reply: HttpRequestAction[] = this.#mod.httpRequestActions.map((a) => ({
|
const reply: HttpRequestAction[] = this.#mod.httpRequestActions.map((a) => ({
|
||||||
@@ -195,7 +195,7 @@ export class PluginInstance {
|
|||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_http_request_actions_response',
|
type: "get_http_request_actions_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
actions: reply,
|
actions: reply,
|
||||||
};
|
};
|
||||||
@@ -204,7 +204,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_websocket_request_actions_request' &&
|
payload.type === "get_websocket_request_actions_request" &&
|
||||||
Array.isArray(this.#mod?.websocketRequestActions)
|
Array.isArray(this.#mod?.websocketRequestActions)
|
||||||
) {
|
) {
|
||||||
const reply = this.#mod.websocketRequestActions.map((a) => ({
|
const reply = this.#mod.websocketRequestActions.map((a) => ({
|
||||||
@@ -212,7 +212,7 @@ export class PluginInstance {
|
|||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_websocket_request_actions_response',
|
type: "get_websocket_request_actions_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
actions: reply,
|
actions: reply,
|
||||||
};
|
};
|
||||||
@@ -221,7 +221,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_workspace_actions_request' &&
|
payload.type === "get_workspace_actions_request" &&
|
||||||
Array.isArray(this.#mod?.workspaceActions)
|
Array.isArray(this.#mod?.workspaceActions)
|
||||||
) {
|
) {
|
||||||
const reply = this.#mod.workspaceActions.map((a) => ({
|
const reply = this.#mod.workspaceActions.map((a) => ({
|
||||||
@@ -229,7 +229,7 @@ export class PluginInstance {
|
|||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_workspace_actions_response',
|
type: "get_workspace_actions_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
actions: reply,
|
actions: reply,
|
||||||
};
|
};
|
||||||
@@ -238,7 +238,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_folder_actions_request' &&
|
payload.type === "get_folder_actions_request" &&
|
||||||
Array.isArray(this.#mod?.folderActions)
|
Array.isArray(this.#mod?.folderActions)
|
||||||
) {
|
) {
|
||||||
const reply = this.#mod.folderActions.map((a) => ({
|
const reply = this.#mod.folderActions.map((a) => ({
|
||||||
@@ -246,7 +246,7 @@ export class PluginInstance {
|
|||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_folder_actions_response',
|
type: "get_folder_actions_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
actions: reply,
|
actions: reply,
|
||||||
};
|
};
|
||||||
@@ -254,9 +254,9 @@ export class PluginInstance {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'get_themes_request' && Array.isArray(this.#mod?.themes)) {
|
if (payload.type === "get_themes_request" && Array.isArray(this.#mod?.themes)) {
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_themes_response',
|
type: "get_themes_response",
|
||||||
themes: this.#mod.themes,
|
themes: this.#mod.themes,
|
||||||
};
|
};
|
||||||
this.#sendPayload(context, replyPayload, replyId);
|
this.#sendPayload(context, replyPayload, replyId);
|
||||||
@@ -264,7 +264,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_template_function_summary_request' &&
|
payload.type === "get_template_function_summary_request" &&
|
||||||
Array.isArray(this.#mod?.templateFunctions)
|
Array.isArray(this.#mod?.templateFunctions)
|
||||||
) {
|
) {
|
||||||
const functions: TemplateFunction[] = this.#mod.templateFunctions.map(
|
const functions: TemplateFunction[] = this.#mod.templateFunctions.map(
|
||||||
@@ -277,7 +277,7 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_template_function_summary_response',
|
type: "get_template_function_summary_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
functions,
|
functions,
|
||||||
};
|
};
|
||||||
@@ -286,7 +286,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'get_template_function_config_request' &&
|
payload.type === "get_template_function_config_request" &&
|
||||||
Array.isArray(this.#mod?.templateFunctions)
|
Array.isArray(this.#mod?.templateFunctions)
|
||||||
) {
|
) {
|
||||||
const templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name);
|
const templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name);
|
||||||
@@ -301,11 +301,11 @@ export class PluginInstance {
|
|||||||
};
|
};
|
||||||
|
|
||||||
payload.values = applyFormInputDefaults(fn.args, payload.values);
|
payload.values = applyFormInputDefaults(fn.args, payload.values);
|
||||||
const p = { ...payload, purpose: 'preview' } as const;
|
const p = { ...payload, purpose: "preview" } as const;
|
||||||
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, p);
|
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, p);
|
||||||
|
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_template_function_config_response',
|
type: "get_template_function_config_response",
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
function: { ...fn, args: stripDynamicCallbacks(resolvedArgs) },
|
function: { ...fn, args: stripDynamicCallbacks(resolvedArgs) },
|
||||||
};
|
};
|
||||||
@@ -313,9 +313,9 @@ export class PluginInstance {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'get_http_authentication_summary_request' && this.#mod?.authentication) {
|
if (payload.type === "get_http_authentication_summary_request" && this.#mod?.authentication) {
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_http_authentication_summary_response',
|
type: "get_http_authentication_summary_response",
|
||||||
...this.#mod.authentication,
|
...this.#mod.authentication,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ export class PluginInstance {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'get_http_authentication_config_request' && this.#mod?.authentication) {
|
if (payload.type === "get_http_authentication_config_request" && this.#mod?.authentication) {
|
||||||
const { args, actions } = this.#mod.authentication;
|
const { args, actions } = this.#mod.authentication;
|
||||||
payload.values = applyFormInputDefaults(args, payload.values);
|
payload.values = applyFormInputDefaults(args, payload.values);
|
||||||
const resolvedArgs = await applyDynamicFormInput(ctx, args, payload);
|
const resolvedArgs = await applyDynamicFormInput(ctx, args, payload);
|
||||||
@@ -334,7 +334,7 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_http_authentication_config_response',
|
type: "get_http_authentication_config_response",
|
||||||
args: stripDynamicCallbacks(resolvedArgs),
|
args: stripDynamicCallbacks(resolvedArgs),
|
||||||
actions: resolvedActions,
|
actions: resolvedActions,
|
||||||
pluginRefId: this.#workerData.pluginRefId,
|
pluginRefId: this.#workerData.pluginRefId,
|
||||||
@@ -344,15 +344,15 @@ export class PluginInstance {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'call_http_authentication_request' && this.#mod?.authentication) {
|
if (payload.type === "call_http_authentication_request" && this.#mod?.authentication) {
|
||||||
const auth = this.#mod.authentication;
|
const auth = this.#mod.authentication;
|
||||||
if (typeof auth?.onApply === 'function') {
|
if (typeof auth?.onApply === "function") {
|
||||||
const resolvedArgs = await applyDynamicFormInput(ctx, auth.args, payload);
|
const resolvedArgs = await applyDynamicFormInput(ctx, auth.args, payload);
|
||||||
payload.values = applyFormInputDefaults(resolvedArgs, payload.values);
|
payload.values = applyFormInputDefaults(resolvedArgs, payload.values);
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
type: 'call_http_authentication_response',
|
type: "call_http_authentication_response",
|
||||||
...(await auth.onApply(ctx, payload)),
|
...(await auth.onApply(ctx, payload)),
|
||||||
},
|
},
|
||||||
replyId,
|
replyId,
|
||||||
@@ -362,11 +362,11 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_http_authentication_action_request' &&
|
payload.type === "call_http_authentication_action_request" &&
|
||||||
this.#mod.authentication != null
|
this.#mod.authentication != null
|
||||||
) {
|
) {
|
||||||
const action = this.#mod.authentication.actions?.[payload.index];
|
const action = this.#mod.authentication.actions?.[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
@@ -374,11 +374,11 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_http_request_action_request' &&
|
payload.type === "call_http_request_action_request" &&
|
||||||
Array.isArray(this.#mod.httpRequestActions)
|
Array.isArray(this.#mod.httpRequestActions)
|
||||||
) {
|
) {
|
||||||
const action = this.#mod.httpRequestActions[payload.index];
|
const action = this.#mod.httpRequestActions[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
@@ -386,11 +386,11 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_websocket_request_action_request' &&
|
payload.type === "call_websocket_request_action_request" &&
|
||||||
Array.isArray(this.#mod.websocketRequestActions)
|
Array.isArray(this.#mod.websocketRequestActions)
|
||||||
) {
|
) {
|
||||||
const action = this.#mod.websocketRequestActions[payload.index];
|
const action = this.#mod.websocketRequestActions[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
@@ -398,20 +398,20 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_workspace_action_request' &&
|
payload.type === "call_workspace_action_request" &&
|
||||||
Array.isArray(this.#mod.workspaceActions)
|
Array.isArray(this.#mod.workspaceActions)
|
||||||
) {
|
) {
|
||||||
const action = this.#mod.workspaceActions[payload.index];
|
const action = this.#mod.workspaceActions[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'call_folder_action_request' && Array.isArray(this.#mod.folderActions)) {
|
if (payload.type === "call_folder_action_request" && Array.isArray(this.#mod.folderActions)) {
|
||||||
const action = this.#mod.folderActions[payload.index];
|
const action = this.#mod.folderActions[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
@@ -419,11 +419,11 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_grpc_request_action_request' &&
|
payload.type === "call_grpc_request_action_request" &&
|
||||||
Array.isArray(this.#mod.grpcRequestActions)
|
Array.isArray(this.#mod.grpcRequestActions)
|
||||||
) {
|
) {
|
||||||
const action = this.#mod.grpcRequestActions[payload.index];
|
const action = this.#mod.grpcRequestActions[payload.index];
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === "function") {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
this.#sendEmpty(context, replyId);
|
this.#sendEmpty(context, replyId);
|
||||||
return;
|
return;
|
||||||
@@ -431,32 +431,32 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_template_function_request' &&
|
payload.type === "call_template_function_request" &&
|
||||||
Array.isArray(this.#mod?.templateFunctions)
|
Array.isArray(this.#mod?.templateFunctions)
|
||||||
) {
|
) {
|
||||||
const fn = this.#mod.templateFunctions.find((a) => a.name === payload.name);
|
const fn = this.#mod.templateFunctions.find((a) => a.name === payload.name);
|
||||||
if (
|
if (
|
||||||
payload.args.purpose === 'preview' &&
|
payload.args.purpose === "preview" &&
|
||||||
(fn?.previewType === 'click' || fn?.previewType === 'none')
|
(fn?.previewType === "click" || fn?.previewType === "none")
|
||||||
) {
|
) {
|
||||||
// Send empty render response
|
// Send empty render response
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
type: 'call_template_function_response',
|
type: "call_template_function_response",
|
||||||
value: null,
|
value: null,
|
||||||
error: 'Live preview disabled for this function',
|
error: "Live preview disabled for this function",
|
||||||
},
|
},
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
} else if (typeof fn?.onRender === 'function') {
|
} else if (typeof fn?.onRender === "function") {
|
||||||
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, payload.args);
|
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, payload.args);
|
||||||
const values = applyFormInputDefaults(resolvedArgs, payload.args.values);
|
const values = applyFormInputDefaults(resolvedArgs, payload.args.values);
|
||||||
const error = validateTemplateFunctionArgs(fn.name, resolvedArgs, values);
|
const error = validateTemplateFunctionArgs(fn.name, resolvedArgs, values);
|
||||||
if (error && payload.args.purpose !== 'preview') {
|
if (error && payload.args.purpose !== "preview") {
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{ type: 'call_template_function_response', value: null, error },
|
{ type: "call_template_function_response", value: null, error },
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -466,16 +466,19 @@ export class PluginInstance {
|
|||||||
const result = await fn.onRender(ctx, { ...payload.args, values });
|
const result = await fn.onRender(ctx, { ...payload.args, values });
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{ type: 'call_template_function_response', value: result ?? null },
|
{ type: "call_template_function_response", value: result ?? null },
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{
|
{
|
||||||
type: 'call_template_function_response',
|
type: "call_template_function_response",
|
||||||
value: null,
|
value: null,
|
||||||
error: (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, ''),
|
error: (err instanceof Error ? err.message : String(err)).replace(
|
||||||
|
/^Error:\s*/g,
|
||||||
|
"",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
@@ -484,9 +487,9 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, '');
|
const error = (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, "");
|
||||||
console.log('Plugin call threw exception', payload.type, '→', error);
|
console.log("Plugin call threw exception", payload.type, "→", error);
|
||||||
this.#sendPayload(context, { type: 'error_response', error }, replyId);
|
this.#sendPayload(context, { type: "error_response", error }, replyId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,11 +498,11 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pathMod() {
|
#pathMod() {
|
||||||
return path.posix.join(this.#workerData.bootRequest.dir, 'build', 'index.js');
|
return path.posix.join(this.#workerData.bootRequest.dir, "build", "index.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
#pathPkg() {
|
#pathPkg() {
|
||||||
return path.join(this.#workerData.bootRequest.dir, 'package.json');
|
return path.join(this.#workerData.bootRequest.dir, "package.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
#unimportModule() {
|
#unimportModule() {
|
||||||
@@ -546,10 +549,10 @@ export class PluginInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#sendEmpty(context: PluginContext, replyId: string | null = null): string {
|
#sendEmpty(context: PluginContext, replyId: string | null = null): string {
|
||||||
return this.#sendPayload(context, { type: 'empty_response' }, replyId);
|
return this.#sendPayload(context, { type: "empty_response" }, replyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sendForReply<T extends Omit<InternalEventPayload, 'type'>>(
|
#sendForReply<T extends Omit<InternalEventPayload, "type">>(
|
||||||
context: PluginContext,
|
context: PluginContext,
|
||||||
payload: InternalEventPayload,
|
payload: InternalEventPayload,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
@@ -600,7 +603,7 @@ export class PluginInstance {
|
|||||||
throw new Error("Can't get window context without an active window");
|
throw new Error("Can't get window context without an active window");
|
||||||
}
|
}
|
||||||
const payload: InternalEventPayload = {
|
const payload: InternalEventPayload = {
|
||||||
type: 'window_info_request',
|
type: "window_info_request",
|
||||||
label: context.label,
|
label: context.label,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -611,7 +614,7 @@ export class PluginInstance {
|
|||||||
clipboard: {
|
clipboard: {
|
||||||
copyText: async (text) => {
|
copyText: async (text) => {
|
||||||
await this.#sendForReply(context, {
|
await this.#sendForReply(context, {
|
||||||
type: 'copy_text_request',
|
type: "copy_text_request",
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -619,7 +622,7 @@ export class PluginInstance {
|
|||||||
toast: {
|
toast: {
|
||||||
show: async (args) => {
|
show: async (args) => {
|
||||||
await this.#sendForReply(context, {
|
await this.#sendForReply(context, {
|
||||||
type: 'show_toast_request',
|
type: "show_toast_request",
|
||||||
// Handle default here because null/undefined both convert to None in Rust translation
|
// Handle default here because null/undefined both convert to None in Rust translation
|
||||||
timeout: args.timeout === undefined ? 5000 : args.timeout,
|
timeout: args.timeout === undefined ? 5000 : args.timeout,
|
||||||
...args,
|
...args,
|
||||||
@@ -638,11 +641,11 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
openUrl: async ({ onNavigate, onClose, ...args }) => {
|
openUrl: async ({ onNavigate, onClose, ...args }) => {
|
||||||
args.label = args.label || `${Math.random()}`;
|
args.label = args.label || `${Math.random()}`;
|
||||||
const payload: InternalEventPayload = { type: 'open_window_request', ...args };
|
const payload: InternalEventPayload = { type: "open_window_request", ...args };
|
||||||
const onEvent = (event: InternalEventPayload) => {
|
const onEvent = (event: InternalEventPayload) => {
|
||||||
if (event.type === 'window_navigate_event') {
|
if (event.type === "window_navigate_event") {
|
||||||
onNavigate?.(event);
|
onNavigate?.(event);
|
||||||
} else if (event.type === 'window_close_event') {
|
} else if (event.type === "window_close_event") {
|
||||||
onClose?.();
|
onClose?.();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -650,7 +653,7 @@ export class PluginInstance {
|
|||||||
return {
|
return {
|
||||||
close: () => {
|
close: () => {
|
||||||
const closePayload: InternalEventPayload = {
|
const closePayload: InternalEventPayload = {
|
||||||
type: 'close_window_request',
|
type: "close_window_request",
|
||||||
label: args.label,
|
label: args.label,
|
||||||
};
|
};
|
||||||
this.#sendPayload(context, closePayload, null);
|
this.#sendPayload(context, closePayload, null);
|
||||||
@@ -659,7 +662,7 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
openExternalUrl: async (url) => {
|
openExternalUrl: async (url) => {
|
||||||
await this.#sendForReply(context, {
|
await this.#sendForReply(context, {
|
||||||
type: 'open_external_url_request',
|
type: "open_external_url_request",
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -667,7 +670,7 @@ export class PluginInstance {
|
|||||||
prompt: {
|
prompt: {
|
||||||
text: async (args) => {
|
text: async (args) => {
|
||||||
const reply: PromptTextResponse = await this.#sendForReply(context, {
|
const reply: PromptTextResponse = await this.#sendForReply(context, {
|
||||||
type: 'prompt_text_request',
|
type: "prompt_text_request",
|
||||||
...args,
|
...args,
|
||||||
});
|
});
|
||||||
return reply.value;
|
return reply.value;
|
||||||
@@ -686,7 +689,7 @@ export class PluginInstance {
|
|||||||
// Build the event manually so we can get the event ID for keying
|
// Build the event manually so we can get the event ID for keying
|
||||||
const eventToSend = this.#buildEventToSend(
|
const eventToSend = this.#buildEventToSend(
|
||||||
context,
|
context,
|
||||||
{ type: 'prompt_form_request', ...args, inputs: strippedInputs },
|
{ type: "prompt_form_request", ...args, inputs: strippedInputs },
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -697,7 +700,7 @@ export class PluginInstance {
|
|||||||
const cb = (event: InternalEvent) => {
|
const cb = (event: InternalEvent) => {
|
||||||
if (event.replyId !== eventToSend.id) return;
|
if (event.replyId !== eventToSend.id) return;
|
||||||
|
|
||||||
if (event.payload.type === 'prompt_form_response') {
|
if (event.payload.type === "prompt_form_response") {
|
||||||
const { done, values } = event.payload as PromptFormResponse;
|
const { done, values } = event.payload as PromptFormResponse;
|
||||||
if (done) {
|
if (done) {
|
||||||
// Final response — resolve the promise and clean up
|
// Final response — resolve the promise and clean up
|
||||||
@@ -716,12 +719,12 @@ export class PluginInstance {
|
|||||||
const stripped = stripDynamicCallbacks(resolvedInputs);
|
const stripped = stripDynamicCallbacks(resolvedInputs);
|
||||||
this.#sendPayload(
|
this.#sendPayload(
|
||||||
context,
|
context,
|
||||||
{ type: 'prompt_form_request', ...args, inputs: stripped },
|
{ type: "prompt_form_request", ...args, inputs: stripped },
|
||||||
eventToSend.id,
|
eventToSend.id,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('Failed to resolve dynamic form inputs', err);
|
console.error("Failed to resolve dynamic form inputs", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -739,7 +742,7 @@ export class PluginInstance {
|
|||||||
httpResponse: {
|
httpResponse: {
|
||||||
find: async (args) => {
|
find: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'find_http_responses_request',
|
type: "find_http_responses_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { httpResponses } = await this.#sendForReply<FindHttpResponsesResponse>(
|
const { httpResponses } = await this.#sendForReply<FindHttpResponsesResponse>(
|
||||||
@@ -752,7 +755,7 @@ export class PluginInstance {
|
|||||||
grpcRequest: {
|
grpcRequest: {
|
||||||
render: async (args) => {
|
render: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'render_grpc_request_request',
|
type: "render_grpc_request_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { grpcRequest } = await this.#sendForReply<RenderGrpcRequestResponse>(
|
const { grpcRequest } = await this.#sendForReply<RenderGrpcRequestResponse>(
|
||||||
@@ -765,7 +768,7 @@ export class PluginInstance {
|
|||||||
httpRequest: {
|
httpRequest: {
|
||||||
getById: async (args) => {
|
getById: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'get_http_request_by_id_request',
|
type: "get_http_request_by_id_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { httpRequest } = await this.#sendForReply<GetHttpRequestByIdResponse>(
|
const { httpRequest } = await this.#sendForReply<GetHttpRequestByIdResponse>(
|
||||||
@@ -776,7 +779,7 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
send: async (args) => {
|
send: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'send_http_request_request',
|
type: "send_http_request_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { httpResponse } = await this.#sendForReply<SendHttpRequestResponse>(
|
const { httpResponse } = await this.#sendForReply<SendHttpRequestResponse>(
|
||||||
@@ -787,7 +790,7 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
render: async (args) => {
|
render: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'render_http_request_request',
|
type: "render_http_request_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { httpRequest } = await this.#sendForReply<RenderHttpRequestResponse>(
|
const { httpRequest } = await this.#sendForReply<RenderHttpRequestResponse>(
|
||||||
@@ -798,9 +801,9 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
list: async (args?: { folderId?: string }) => {
|
list: async (args?: { folderId?: string }) => {
|
||||||
const payload: InternalEventPayload = {
|
const payload: InternalEventPayload = {
|
||||||
type: 'list_http_requests_request',
|
type: "list_http_requests_request",
|
||||||
folderId: args?.folderId,
|
folderId: args?.folderId,
|
||||||
} satisfies ListHttpRequestsRequest & { type: 'list_http_requests_request' };
|
} satisfies ListHttpRequestsRequest & { type: "list_http_requests_request" };
|
||||||
const { httpRequests } = await this.#sendForReply<ListHttpRequestsResponse>(
|
const { httpRequests } = await this.#sendForReply<ListHttpRequestsResponse>(
|
||||||
context,
|
context,
|
||||||
payload,
|
payload,
|
||||||
@@ -809,13 +812,13 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
create: async (args) => {
|
create: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'upsert_model_request',
|
type: "upsert_model_request",
|
||||||
model: {
|
model: {
|
||||||
name: '',
|
name: "",
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
...args,
|
...args,
|
||||||
id: '',
|
id: "",
|
||||||
model: 'http_request',
|
model: "http_request",
|
||||||
},
|
},
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
|
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
|
||||||
@@ -823,9 +826,9 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
update: async (args) => {
|
update: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'upsert_model_request',
|
type: "upsert_model_request",
|
||||||
model: {
|
model: {
|
||||||
model: 'http_request',
|
model: "http_request",
|
||||||
...args,
|
...args,
|
||||||
},
|
},
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
@@ -834,8 +837,8 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
delete: async (args) => {
|
delete: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'delete_model_request',
|
type: "delete_model_request",
|
||||||
model: 'http_request',
|
model: "http_request",
|
||||||
id: args.id,
|
id: args.id,
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
const response = await this.#sendForReply<DeleteModelResponse>(context, payload);
|
const response = await this.#sendForReply<DeleteModelResponse>(context, payload);
|
||||||
@@ -844,23 +847,23 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
folder: {
|
folder: {
|
||||||
list: async () => {
|
list: async () => {
|
||||||
const payload = { type: 'list_folders_request' } as const;
|
const payload = { type: "list_folders_request" } as const;
|
||||||
const { folders } = await this.#sendForReply<ListFoldersResponse>(context, payload);
|
const { folders } = await this.#sendForReply<ListFoldersResponse>(context, payload);
|
||||||
return folders;
|
return folders;
|
||||||
},
|
},
|
||||||
getById: async (args: { id: string }) => {
|
getById: async (args: { id: string }) => {
|
||||||
const payload = { type: 'list_folders_request' } as const;
|
const payload = { type: "list_folders_request" } as const;
|
||||||
const { folders } = await this.#sendForReply<ListFoldersResponse>(context, payload);
|
const { folders } = await this.#sendForReply<ListFoldersResponse>(context, payload);
|
||||||
return folders.find((f) => f.id === args.id) ?? null;
|
return folders.find((f) => f.id === args.id) ?? null;
|
||||||
},
|
},
|
||||||
create: async ({ name, ...args }) => {
|
create: async ({ name, ...args }) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'upsert_model_request',
|
type: "upsert_model_request",
|
||||||
model: {
|
model: {
|
||||||
...args,
|
...args,
|
||||||
name: name ?? '',
|
name: name ?? "",
|
||||||
id: '',
|
id: "",
|
||||||
model: 'folder',
|
model: "folder",
|
||||||
},
|
},
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
|
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
|
||||||
@@ -868,9 +871,9 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
update: async (args) => {
|
update: async (args) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'upsert_model_request',
|
type: "upsert_model_request",
|
||||||
model: {
|
model: {
|
||||||
model: 'folder',
|
model: "folder",
|
||||||
...args,
|
...args,
|
||||||
},
|
},
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
@@ -879,8 +882,8 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
delete: async (args: { id: string }) => {
|
delete: async (args: { id: string }) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'delete_model_request',
|
type: "delete_model_request",
|
||||||
model: 'folder',
|
model: "folder",
|
||||||
id: args.id,
|
id: args.id,
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
const response = await this.#sendForReply<DeleteModelResponse>(context, payload);
|
const response = await this.#sendForReply<DeleteModelResponse>(context, payload);
|
||||||
@@ -890,14 +893,14 @@ export class PluginInstance {
|
|||||||
cookies: {
|
cookies: {
|
||||||
getValue: async (args: GetCookieValueRequest) => {
|
getValue: async (args: GetCookieValueRequest) => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'get_cookie_value_request',
|
type: "get_cookie_value_request",
|
||||||
...args,
|
...args,
|
||||||
} as const;
|
} as const;
|
||||||
const { value } = await this.#sendForReply<GetCookieValueResponse>(context, payload);
|
const { value } = await this.#sendForReply<GetCookieValueResponse>(context, payload);
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
listNames: async () => {
|
listNames: async () => {
|
||||||
const payload = { type: 'list_cookie_names_request' } as const;
|
const payload = { type: "list_cookie_names_request" } as const;
|
||||||
const { names } = await this.#sendForReply<ListCookieNamesResponse>(context, payload);
|
const { names } = await this.#sendForReply<ListCookieNamesResponse>(context, payload);
|
||||||
return names;
|
return names;
|
||||||
},
|
},
|
||||||
@@ -908,7 +911,7 @@ export class PluginInstance {
|
|||||||
* (eg. object), it will be recursively rendered.
|
* (eg. object), it will be recursively rendered.
|
||||||
*/
|
*/
|
||||||
render: async (args: TemplateRenderRequest) => {
|
render: async (args: TemplateRenderRequest) => {
|
||||||
const payload = { type: 'template_render_request', ...args } as const;
|
const payload = { type: "template_render_request", ...args } as const;
|
||||||
const result = await this.#sendForReply<TemplateRenderResponse>(context, payload);
|
const result = await this.#sendForReply<TemplateRenderResponse>(context, payload);
|
||||||
// oxlint-disable-next-line no-explicit-any -- That's okay
|
// oxlint-disable-next-line no-explicit-any -- That's okay
|
||||||
return result.data as any;
|
return result.data as any;
|
||||||
@@ -916,34 +919,34 @@ export class PluginInstance {
|
|||||||
},
|
},
|
||||||
store: {
|
store: {
|
||||||
get: async <T>(key: string) => {
|
get: async <T>(key: string) => {
|
||||||
const payload = { type: 'get_key_value_request', key } as const;
|
const payload = { type: "get_key_value_request", key } as const;
|
||||||
const result = await this.#sendForReply<GetKeyValueResponse>(context, payload);
|
const result = await this.#sendForReply<GetKeyValueResponse>(context, payload);
|
||||||
return result.value ? (JSON.parse(result.value) as T) : undefined;
|
return result.value ? (JSON.parse(result.value) as T) : undefined;
|
||||||
},
|
},
|
||||||
set: async <T>(key: string, value: T) => {
|
set: async <T>(key: string, value: T) => {
|
||||||
const valueStr = JSON.stringify(value);
|
const valueStr = JSON.stringify(value);
|
||||||
const payload: InternalEventPayload = {
|
const payload: InternalEventPayload = {
|
||||||
type: 'set_key_value_request',
|
type: "set_key_value_request",
|
||||||
key,
|
key,
|
||||||
value: valueStr,
|
value: valueStr,
|
||||||
};
|
};
|
||||||
await this.#sendForReply<GetKeyValueResponse>(context, payload);
|
await this.#sendForReply<GetKeyValueResponse>(context, payload);
|
||||||
},
|
},
|
||||||
delete: async (key: string) => {
|
delete: async (key: string) => {
|
||||||
const payload = { type: 'delete_key_value_request', key } as const;
|
const payload = { type: "delete_key_value_request", key } as const;
|
||||||
const result = await this.#sendForReply<DeleteKeyValueResponse>(context, payload);
|
const result = await this.#sendForReply<DeleteKeyValueResponse>(context, payload);
|
||||||
return result.deleted;
|
return result.deleted;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugin: {
|
plugin: {
|
||||||
reload: () => {
|
reload: () => {
|
||||||
this.#sendPayload(context, { type: 'reload_response', silent: true }, null);
|
this.#sendPayload(context, { type: "reload_response", silent: true }, null);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
list: async () => {
|
list: async () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: 'list_open_workspaces_request',
|
type: "list_open_workspaces_request",
|
||||||
} as InternalEventPayload;
|
} as InternalEventPayload;
|
||||||
const response = await this.#sendForReply<ListOpenWorkspacesResponse>(context, payload);
|
const response = await this.#sendForReply<ListOpenWorkspacesResponse>(context, payload);
|
||||||
return response.workspaces.map((w) => {
|
return response.workspaces.map((w) => {
|
||||||
@@ -975,7 +978,7 @@ function stripDynamicCallbacks(inputs: { dynamic?: unknown }[]): FormInput[] {
|
|||||||
return inputs.map((input) => {
|
return inputs.map((input) => {
|
||||||
// oxlint-disable-next-line no-explicit-any -- stripping dynamic from union type
|
// oxlint-disable-next-line no-explicit-any -- stripping dynamic from union type
|
||||||
const { dynamic: _dynamic, ...rest } = input as any;
|
const { dynamic: _dynamic, ...rest } = input as any;
|
||||||
if ('inputs' in rest && Array.isArray(rest.inputs)) {
|
if ("inputs" in rest && Array.isArray(rest.inputs)) {
|
||||||
rest.inputs = stripDynamicCallbacks(rest.inputs);
|
rest.inputs = stripDynamicCallbacks(rest.inputs);
|
||||||
}
|
}
|
||||||
return rest as FormInput;
|
return rest as FormInput;
|
||||||
@@ -983,8 +986,8 @@ function stripDynamicCallbacks(inputs: { dynamic?: unknown }[]): FormInput[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function genId(len = 5): string {
|
function genId(len = 5): string {
|
||||||
const alphabet = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
const alphabet = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
let id = '';
|
let id = "";
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
id += alphabet[Math.floor(Math.random() * alphabet.length)];
|
id += alphabet[Math.floor(Math.random() * alphabet.length)];
|
||||||
}
|
}
|
||||||
@@ -1004,7 +1007,7 @@ function watchFile(filepath: string, cb: () => void) {
|
|||||||
const stat = statSync(filepath, { throwIfNoEntry: false });
|
const stat = statSync(filepath, { throwIfNoEntry: false });
|
||||||
if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
|
if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
|
||||||
watchedFiles[filepath] = stat ?? null;
|
watchedFiles[filepath] = stat ?? null;
|
||||||
console.log('[plugin-runtime] watchFile triggered', filepath);
|
console.log("[plugin-runtime] watchFile triggered", filepath);
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import type {
|
|||||||
DynamicAuthenticationArg,
|
DynamicAuthenticationArg,
|
||||||
DynamicPromptFormArg,
|
DynamicPromptFormArg,
|
||||||
DynamicTemplateFunctionArg,
|
DynamicTemplateFunctionArg,
|
||||||
} from '@yaakapp/api';
|
} from "@yaakapp/api";
|
||||||
import type {
|
import type {
|
||||||
CallHttpAuthenticationActionArgs,
|
CallHttpAuthenticationActionArgs,
|
||||||
CallTemplateFunctionArgs,
|
CallTemplateFunctionArgs,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from "@yaakapp-internal/plugins";
|
||||||
|
|
||||||
type AnyDynamicArg = DynamicTemplateFunctionArg | DynamicAuthenticationArg | DynamicPromptFormArg;
|
type AnyDynamicArg = DynamicTemplateFunctionArg | DynamicAuthenticationArg | DynamicPromptFormArg;
|
||||||
type AnyCallArgs =
|
type AnyCallArgs =
|
||||||
@@ -42,7 +42,7 @@ export async function applyDynamicFormInput(
|
|||||||
const resolvedArgs: AnyDynamicArg[] = [];
|
const resolvedArgs: AnyDynamicArg[] = [];
|
||||||
for (const { dynamic, ...arg } of args) {
|
for (const { dynamic, ...arg } of args) {
|
||||||
const dynamicResult =
|
const dynamicResult =
|
||||||
typeof dynamic === 'function'
|
typeof dynamic === "function"
|
||||||
? await dynamic(
|
? await dynamic(
|
||||||
ctx,
|
ctx,
|
||||||
callArgs as CallTemplateFunctionArgs &
|
callArgs as CallTemplateFunctionArgs &
|
||||||
@@ -56,7 +56,7 @@ export async function applyDynamicFormInput(
|
|||||||
...dynamicResult,
|
...dynamicResult,
|
||||||
} as AnyDynamicArg;
|
} as AnyDynamicArg;
|
||||||
|
|
||||||
if ('inputs' in newArg && Array.isArray(newArg.inputs)) {
|
if ("inputs" in newArg && Array.isArray(newArg.inputs)) {
|
||||||
try {
|
try {
|
||||||
newArg.inputs = await applyDynamicFormInput(
|
newArg.inputs = await applyDynamicFormInput(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -66,7 +66,7 @@ export async function applyDynamicFormInput(
|
|||||||
CallPromptFormDynamicArgs,
|
CallPromptFormDynamicArgs,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to apply dynamic form input', e);
|
console.error("Failed to apply dynamic form input", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolvedArgs.push(newArg);
|
resolvedArgs.push(newArg);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { InternalEvent } from '@yaakapp/api';
|
import type { InternalEvent } from "@yaakapp/api";
|
||||||
import WebSocket from 'ws';
|
import WebSocket from "ws";
|
||||||
import { EventChannel } from './EventChannel';
|
import { EventChannel } from "./EventChannel";
|
||||||
import { PluginHandle } from './PluginHandle';
|
import { PluginHandle } from "./PluginHandle";
|
||||||
|
|
||||||
const port = process.env.PORT;
|
const port = process.env.PORT;
|
||||||
if (!port) {
|
if (!port) {
|
||||||
throw new Error('Plugin runtime missing PORT');
|
throw new Error("Plugin runtime missing PORT");
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = process.env.HOST;
|
const host = process.env.HOST;
|
||||||
if (!host) {
|
if (!host) {
|
||||||
throw new Error('Plugin runtime missing HOST');
|
throw new Error("Plugin runtime missing HOST");
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginToAppEvents = new EventChannel();
|
const pluginToAppEvents = new EventChannel();
|
||||||
@@ -18,16 +18,16 @@ const plugins: Record<string, PluginHandle> = {};
|
|||||||
|
|
||||||
const ws = new WebSocket(`ws://${host}:${port}`);
|
const ws = new WebSocket(`ws://${host}:${port}`);
|
||||||
|
|
||||||
ws.on('message', async (e: Buffer) => {
|
ws.on("message", async (e: Buffer) => {
|
||||||
try {
|
try {
|
||||||
await handleIncoming(e.toString());
|
await handleIncoming(e.toString());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed to handle incoming plugin event', err);
|
console.log("Failed to handle incoming plugin event", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ws.on('open', () => console.log('Plugin runtime connected to websocket'));
|
ws.on("open", () => console.log("Plugin runtime connected to websocket"));
|
||||||
ws.on('error', (err: unknown) => console.error('Plugin runtime websocket error', err));
|
ws.on("error", (err: unknown) => console.error("Plugin runtime websocket error", err));
|
||||||
ws.on('close', (code: number) => console.log('Plugin runtime websocket closed', code));
|
ws.on("close", (code: number) => console.log("Plugin runtime websocket closed", code));
|
||||||
|
|
||||||
// Listen for incoming events from plugins
|
// Listen for incoming events from plugins
|
||||||
pluginToAppEvents.listen((e) => {
|
pluginToAppEvents.listen((e) => {
|
||||||
@@ -38,7 +38,7 @@ pluginToAppEvents.listen((e) => {
|
|||||||
async function handleIncoming(msg: string) {
|
async function handleIncoming(msg: string) {
|
||||||
const pluginEvent: InternalEvent = JSON.parse(msg);
|
const pluginEvent: InternalEvent = JSON.parse(msg);
|
||||||
// Handle special event to bootstrap plugin
|
// Handle special event to bootstrap plugin
|
||||||
if (pluginEvent.payload.type === 'boot_request') {
|
if (pluginEvent.payload.type === "boot_request") {
|
||||||
const plugin = new PluginHandle(
|
const plugin = new PluginHandle(
|
||||||
pluginEvent.pluginRefId,
|
pluginEvent.pluginRefId,
|
||||||
pluginEvent.context,
|
pluginEvent.context,
|
||||||
@@ -51,23 +51,23 @@ async function handleIncoming(msg: string) {
|
|||||||
// Once booted, forward all events to the plugin worker
|
// Once booted, forward all events to the plugin worker
|
||||||
const plugin = plugins[pluginEvent.pluginRefId];
|
const plugin = plugins[pluginEvent.pluginRefId];
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
console.warn('Failed to get plugin for event by', pluginEvent.pluginRefId);
|
console.warn("Failed to get plugin for event by", pluginEvent.pluginRefId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginEvent.payload.type === 'terminate_request') {
|
if (pluginEvent.payload.type === "terminate_request") {
|
||||||
await plugin.terminate();
|
await plugin.terminate();
|
||||||
console.log('Terminated plugin worker', pluginEvent.pluginRefId);
|
console.log("Terminated plugin worker", pluginEvent.pluginRefId);
|
||||||
delete plugins[pluginEvent.pluginRefId];
|
delete plugins[pluginEvent.pluginRefId];
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.sendToWorker(pluginEvent);
|
plugin.sendToWorker(pluginEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
process.on("uncaughtException", (error) => {
|
||||||
console.error('Uncaught Exception:', error);
|
console.error("Uncaught Exception:", error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* oxlint-disable unbound-method */
|
/* oxlint-disable unbound-method */
|
||||||
import process from 'node:process';
|
import process from "node:process";
|
||||||
|
|
||||||
export function interceptStdout(intercept: (text: string) => string) {
|
export function interceptStdout(intercept: (text: string) => string) {
|
||||||
const old_stdout_write = process.stdout.write;
|
const old_stdout_write = process.stdout.write;
|
||||||
@@ -25,5 +25,5 @@ export function interceptStdout(intercept: (text: string) => string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function interceptor(text: string, fn: (text: string) => string) {
|
function interceptor(text: string, fn: (text: string) => string) {
|
||||||
return fn(text).replace(/\n$/, '') + (fn(text) && text.endsWith('\n') ? '\n' : '');
|
return fn(text).replace(/\n$/, "") + (fn(text) && text.endsWith("\n") ? "\n" : "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { TemplateFunctionPlugin } from '@yaakapp/api';
|
import type { TemplateFunctionPlugin } from "@yaakapp/api";
|
||||||
|
|
||||||
export function migrateTemplateFunctionSelectOptions(
|
export function migrateTemplateFunctionSelectOptions(
|
||||||
f: TemplateFunctionPlugin,
|
f: TemplateFunctionPlugin,
|
||||||
): TemplateFunctionPlugin {
|
): TemplateFunctionPlugin {
|
||||||
const migratedArgs = f.args.map((a) => {
|
const migratedArgs = f.args.map((a) => {
|
||||||
if (a.type === 'select') {
|
if (a.type === "select") {
|
||||||
// Migrate old options that had 'name' instead of 'label'
|
// Migrate old options that had 'name' instead of 'label'
|
||||||
type LegacyOption = { label?: string; value: string; name?: string };
|
type LegacyOption = { label?: string; value: string; name?: string };
|
||||||
a.options = a.options.map((o) => {
|
a.options = a.options.map((o) => {
|
||||||
const legacy = o as LegacyOption;
|
const legacy = o as LegacyOption;
|
||||||
return {
|
return {
|
||||||
label: legacy.label ?? legacy.name ?? '',
|
label: legacy.label ?? legacy.name ?? "",
|
||||||
value: legacy.value,
|
value: legacy.value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,106 +1,106 @@
|
|||||||
import { applyFormInputDefaults } from '@yaakapp-internal/lib/templateFunction';
|
import { applyFormInputDefaults } from "@yaakapp-internal/lib/templateFunction";
|
||||||
import type { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins';
|
import type { CallTemplateFunctionArgs } from "@yaakapp-internal/plugins";
|
||||||
import type { Context, DynamicTemplateFunctionArg } from '@yaakapp/api';
|
import type { Context, DynamicTemplateFunctionArg } from "@yaakapp/api";
|
||||||
import { describe, expect, test } from 'vite-plus/test';
|
import { describe, expect, test } from "vite-plus/test";
|
||||||
import { applyDynamicFormInput } from '../src/common';
|
import { applyDynamicFormInput } from "../src/common";
|
||||||
|
|
||||||
describe('applyFormInputDefaults', () => {
|
describe("applyFormInputDefaults", () => {
|
||||||
test('Works with top-level select', () => {
|
test("Works with top-level select", () => {
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: 'test',
|
name: "test",
|
||||||
options: [{ label: 'Option 1', value: 'one' }],
|
options: [{ label: "Option 1", value: "one" }],
|
||||||
defaultValue: 'one',
|
defaultValue: "one",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(applyFormInputDefaults(args, {})).toEqual({
|
expect(applyFormInputDefaults(args, {})).toEqual({
|
||||||
test: 'one',
|
test: "one",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Works with existing value', () => {
|
test("Works with existing value", () => {
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: 'test',
|
name: "test",
|
||||||
options: [{ label: 'Option 1', value: 'one' }],
|
options: [{ label: "Option 1", value: "one" }],
|
||||||
defaultValue: 'one',
|
defaultValue: "one",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(applyFormInputDefaults(args, { test: 'explicit' })).toEqual({
|
expect(applyFormInputDefaults(args, { test: "explicit" })).toEqual({
|
||||||
test: 'explicit',
|
test: "explicit",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Works with recursive select', () => {
|
test("Works with recursive select", () => {
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{ type: 'text', name: 'dummy', defaultValue: 'top' },
|
{ type: "text", name: "dummy", defaultValue: "top" },
|
||||||
{
|
{
|
||||||
type: 'accordion',
|
type: "accordion",
|
||||||
label: 'Test',
|
label: "Test",
|
||||||
inputs: [
|
inputs: [
|
||||||
{ type: 'text', name: 'name', defaultValue: 'hello' },
|
{ type: "text", name: "name", defaultValue: "hello" },
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: 'test',
|
name: "test",
|
||||||
options: [{ label: 'Option 1', value: 'one' }],
|
options: [{ label: "Option 1", value: "one" }],
|
||||||
defaultValue: 'one',
|
defaultValue: "one",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(applyFormInputDefaults(args, {})).toEqual({
|
expect(applyFormInputDefaults(args, {})).toEqual({
|
||||||
dummy: 'top',
|
dummy: "top",
|
||||||
test: 'one',
|
test: "one",
|
||||||
name: 'hello',
|
name: "hello",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Works with dynamic options', () => {
|
test("Works with dynamic options", () => {
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: 'test',
|
name: "test",
|
||||||
defaultValue: 'one',
|
defaultValue: "one",
|
||||||
options: [],
|
options: [],
|
||||||
dynamic() {
|
dynamic() {
|
||||||
return { options: [{ label: 'Option 1', value: 'one' }] };
|
return { options: [{ label: "Option 1", value: "one" }] };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
expect(applyFormInputDefaults(args, {})).toEqual({
|
expect(applyFormInputDefaults(args, {})).toEqual({
|
||||||
test: 'one',
|
test: "one",
|
||||||
});
|
});
|
||||||
expect(applyFormInputDefaults(args, {})).toEqual({
|
expect(applyFormInputDefaults(args, {})).toEqual({
|
||||||
test: 'one',
|
test: "one",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('applyDynamicFormInput', () => {
|
describe("applyDynamicFormInput", () => {
|
||||||
test('Works with plain input', async () => {
|
test("Works with plain input", async () => {
|
||||||
const ctx = {} as Context;
|
const ctx = {} as Context;
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{ type: 'text', name: 'name' },
|
{ type: "text", name: "name" },
|
||||||
{ type: 'checkbox', name: 'checked' },
|
{ type: "checkbox", name: "checked" },
|
||||||
];
|
];
|
||||||
const callArgs: CallTemplateFunctionArgs = {
|
const callArgs: CallTemplateFunctionArgs = {
|
||||||
values: {},
|
values: {},
|
||||||
purpose: 'preview',
|
purpose: "preview",
|
||||||
};
|
};
|
||||||
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
||||||
{ type: 'text', name: 'name' },
|
{ type: "text", name: "name" },
|
||||||
{ type: 'checkbox', name: 'checked' },
|
{ type: "checkbox", name: "checked" },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Works with dynamic input', async () => {
|
test("Works with dynamic input", async () => {
|
||||||
const ctx = {} as Context;
|
const ctx = {} as Context;
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: "text",
|
||||||
name: 'name',
|
name: "name",
|
||||||
async dynamic(_ctx, _args) {
|
async dynamic(_ctx, _args) {
|
||||||
return { hidden: true };
|
return { hidden: true };
|
||||||
},
|
},
|
||||||
@@ -108,28 +108,28 @@ describe('applyDynamicFormInput', () => {
|
|||||||
];
|
];
|
||||||
const callArgs: CallTemplateFunctionArgs = {
|
const callArgs: CallTemplateFunctionArgs = {
|
||||||
values: {},
|
values: {},
|
||||||
purpose: 'preview',
|
purpose: "preview",
|
||||||
};
|
};
|
||||||
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
||||||
{ type: 'text', name: 'name', hidden: true },
|
{ type: "text", name: "name", hidden: true },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Works with recursive dynamic input', async () => {
|
test("Works with recursive dynamic input", async () => {
|
||||||
const ctx = {} as Context;
|
const ctx = {} as Context;
|
||||||
const callArgs: CallTemplateFunctionArgs = {
|
const callArgs: CallTemplateFunctionArgs = {
|
||||||
values: { hello: 'world' },
|
values: { hello: "world" },
|
||||||
purpose: 'preview',
|
purpose: "preview",
|
||||||
};
|
};
|
||||||
const args: DynamicTemplateFunctionArg[] = [
|
const args: DynamicTemplateFunctionArg[] = [
|
||||||
{
|
{
|
||||||
type: 'banner',
|
type: "banner",
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: "text",
|
||||||
name: 'name',
|
name: "name",
|
||||||
async dynamic(_ctx, args) {
|
async dynamic(_ctx, args) {
|
||||||
return { hidden: args.values.hello === 'world' };
|
return { hidden: args.values.hello === "world" };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -137,11 +137,11 @@ describe('applyDynamicFormInput', () => {
|
|||||||
];
|
];
|
||||||
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
|
||||||
{
|
{
|
||||||
type: 'banner',
|
type: "banner",
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: "text",
|
||||||
name: 'name',
|
name: "name",
|
||||||
hidden: true,
|
hidden: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ This will generate a random JSON body on every request:
|
|||||||
The plugin provides access to all FakerJS modules and their methods:
|
The plugin provides access to all FakerJS modules and their methods:
|
||||||
|
|
||||||
| Category | Description | Example Methods |
|
| Category | Description | Example Methods |
|
||||||
|------------|---------------------------|--------------------------------------------|
|
| ---------- | ------------------------- | ------------------------------------------ |
|
||||||
| `airline` | Airline-related data | `aircraftType`, `airline`, `airplane` |
|
| `airline` | Airline-related data | `aircraftType`, `airline`, `airplane` |
|
||||||
| `animal` | Animal names and types | `bear`, `bird`, `cat`, `dog`, `fish` |
|
| `animal` | Animal names and types | `bear`, `bird`, `cat`, `dog`, `fish` |
|
||||||
| `color` | Colors in various formats | `human`, `rgb`, `hex`, `hsl` |
|
| `color` | Colors in various formats | `human`, `rgb`, `hex`, `hsl` |
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaak/faker",
|
"name": "@yaak/faker",
|
||||||
"private": true,
|
|
||||||
"version": "1.1.1",
|
|
||||||
"displayName": "Faker",
|
"displayName": "Faker",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"private": true,
|
||||||
"description": "Template functions for generating fake data using FakerJS",
|
"description": "Template functions for generating fake data using FakerJS",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from "@faker-js/faker";
|
||||||
import type { DynamicTemplateFunctionArg, PluginDefinition } from '@yaakapp/api';
|
import type { DynamicTemplateFunctionArg, PluginDefinition } from "@yaakapp/api";
|
||||||
|
|
||||||
const modules = [
|
const modules = [
|
||||||
'airline',
|
"airline",
|
||||||
'animal',
|
"animal",
|
||||||
'color',
|
"color",
|
||||||
'commerce',
|
"commerce",
|
||||||
'company',
|
"company",
|
||||||
'database',
|
"database",
|
||||||
'date',
|
"date",
|
||||||
'finance',
|
"finance",
|
||||||
'git',
|
"git",
|
||||||
'hacker',
|
"hacker",
|
||||||
'image',
|
"image",
|
||||||
'internet',
|
"internet",
|
||||||
'location',
|
"location",
|
||||||
'lorem',
|
"lorem",
|
||||||
'person',
|
"person",
|
||||||
'music',
|
"music",
|
||||||
'number',
|
"number",
|
||||||
'phone',
|
"phone",
|
||||||
'science',
|
"science",
|
||||||
'string',
|
"string",
|
||||||
'system',
|
"system",
|
||||||
'vehicle',
|
"vehicle",
|
||||||
'word',
|
"word",
|
||||||
];
|
];
|
||||||
|
|
||||||
function normalizeResult(result: unknown): string {
|
function normalizeResult(result: unknown): string {
|
||||||
if (typeof result === 'string') return result;
|
if (typeof result === "string") return result;
|
||||||
if (result instanceof Date) return result.toISOString();
|
if (result instanceof Date) return result.toISOString();
|
||||||
return JSON.stringify(result);
|
return JSON.stringify(result);
|
||||||
}
|
}
|
||||||
@@ -37,20 +37,20 @@ function normalizeResult(result: unknown): string {
|
|||||||
function args(modName: string, fnName: string): DynamicTemplateFunctionArg[] {
|
function args(modName: string, fnName: string): DynamicTemplateFunctionArg[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: 'banner',
|
type: "banner",
|
||||||
color: 'info',
|
color: "info",
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: 'markdown',
|
type: "markdown",
|
||||||
content: `Need help? View documentation for [\`${modName}.${fnName}(…)\`](https://fakerjs.dev/api/${encodeURIComponent(modName)}.html#${encodeURIComponent(fnName)})`,
|
content: `Need help? View documentation for [\`${modName}.${fnName}(…)\`](https://fakerjs.dev/api/${encodeURIComponent(modName)}.html#${encodeURIComponent(fnName)})`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'options',
|
name: "options",
|
||||||
label: 'Arguments',
|
label: "Arguments",
|
||||||
type: 'editor',
|
type: "editor",
|
||||||
language: 'json',
|
language: "json",
|
||||||
optional: true,
|
optional: true,
|
||||||
placeholder: 'e.g. { "min": 1, "max": 10 } or 10 or ["en","US"]',
|
placeholder: 'e.g. { "min": 1, "max": 10 } or 10 or ["en","US"]',
|
||||||
},
|
},
|
||||||
@@ -61,22 +61,22 @@ export const plugin: PluginDefinition = {
|
|||||||
templateFunctions: modules.flatMap((modName) => {
|
templateFunctions: modules.flatMap((modName) => {
|
||||||
const mod = faker[modName as keyof typeof faker];
|
const mod = faker[modName as keyof typeof faker];
|
||||||
return Object.keys(mod)
|
return Object.keys(mod)
|
||||||
.filter((n) => n !== 'faker')
|
.filter((n) => n !== "faker")
|
||||||
.map((fnName) => ({
|
.map((fnName) => ({
|
||||||
name: ['faker', modName, fnName].join('.'),
|
name: ["faker", modName, fnName].join("."),
|
||||||
args: args(modName, fnName),
|
args: args(modName, fnName),
|
||||||
async onRender(_ctx, args) {
|
async onRender(_ctx, args) {
|
||||||
const fn = mod[fnName as keyof typeof mod] as (...a: unknown[]) => unknown;
|
const fn = mod[fnName as keyof typeof mod] as (...a: unknown[]) => unknown;
|
||||||
const options = args.values.options;
|
const options = args.values.options;
|
||||||
|
|
||||||
// No options supplied
|
// No options supplied
|
||||||
if (options == null || options === '') {
|
if (options == null || options === "") {
|
||||||
return normalizeResult(fn());
|
return normalizeResult(fn());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try JSON first
|
// Try JSON first
|
||||||
let parsed: unknown = options;
|
let parsed: unknown = options;
|
||||||
if (typeof options === 'string') {
|
if (typeof options === "string") {
|
||||||
try {
|
try {
|
||||||
parsed = JSON.parse(options);
|
parsed = JSON.parse(options);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, expect, it } from 'vite-plus/test';
|
import { describe, expect, it } from "vite-plus/test";
|
||||||
|
|
||||||
describe('template-function-faker', () => {
|
describe("template-function-faker", () => {
|
||||||
it('exports all expected template functions', async () => {
|
it("exports all expected template functions", async () => {
|
||||||
const { plugin } = await import('../src/index');
|
const { plugin } = await import("../src/index");
|
||||||
const names = plugin.templateFunctions?.map((fn) => fn.name).sort() ?? [];
|
const names = plugin.templateFunctions?.map((fn) => fn.name).sort() ?? [];
|
||||||
|
|
||||||
// Snapshot the full list of exported function names so we catch any
|
// Snapshot the full list of exported function names so we catch any
|
||||||
@@ -10,13 +10,13 @@ describe('template-function-faker', () => {
|
|||||||
expect(names).toMatchSnapshot();
|
expect(names).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders date results as unquoted ISO strings', async () => {
|
it("renders date results as unquoted ISO strings", async () => {
|
||||||
const { plugin } = await import('../src/index');
|
const { plugin } = await import("../src/index");
|
||||||
const fn = plugin.templateFunctions?.find((fn) => fn.name === 'faker.date.future');
|
const fn = plugin.templateFunctions?.find((fn) => fn.name === "faker.date.future");
|
||||||
// oxlint-disable-next-line unbound-method
|
// oxlint-disable-next-line unbound-method
|
||||||
const onRender = fn?.onRender;
|
const onRender = fn?.onRender;
|
||||||
|
|
||||||
expect(onRender).toBeTypeOf('function');
|
expect(onRender).toBeTypeOf("function");
|
||||||
if (onRender == null) {
|
if (onRender == null) {
|
||||||
throw new Error("Expected template function 'faker.date.future' to define onRender");
|
throw new Error("Expected template function 'faker.date.future' to define onRender");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,26 +16,26 @@ remembered for next time.
|
|||||||
|
|
||||||
Each language supports one or more libraries:
|
Each language supports one or more libraries:
|
||||||
|
|
||||||
| Language | Libraries |
|
| Language | Libraries |
|
||||||
|---|---|
|
| ----------- | ------------------------------------ |
|
||||||
| C | libcurl |
|
| C | libcurl |
|
||||||
| Clojure | clj-http |
|
| Clojure | clj-http |
|
||||||
| C# | HttpClient, RestSharp |
|
| C# | HttpClient, RestSharp |
|
||||||
| Go | Native |
|
| Go | Native |
|
||||||
| HTTP | HTTP/1.1 |
|
| HTTP | HTTP/1.1 |
|
||||||
| Java | AsyncHttp, NetHttp, OkHttp, Unirest |
|
| Java | AsyncHttp, NetHttp, OkHttp, Unirest |
|
||||||
| JavaScript | Axios, fetch, jQuery, XHR |
|
| JavaScript | Axios, fetch, jQuery, XHR |
|
||||||
| Kotlin | OkHttp |
|
| Kotlin | OkHttp |
|
||||||
| Node.js | Axios, fetch, HTTP, Request, Unirest |
|
| Node.js | Axios, fetch, HTTP, Request, Unirest |
|
||||||
| Objective-C | NSURLSession |
|
| Objective-C | NSURLSession |
|
||||||
| OCaml | CoHTTP |
|
| OCaml | CoHTTP |
|
||||||
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
|
| PHP | cURL, Guzzle, HTTP v1, HTTP v2 |
|
||||||
| PowerShell | Invoke-WebRequest, RestMethod |
|
| PowerShell | Invoke-WebRequest, RestMethod |
|
||||||
| Python | http.client, Requests |
|
| Python | http.client, Requests |
|
||||||
| R | httr |
|
| R | httr |
|
||||||
| Ruby | Native |
|
| Ruby | Native |
|
||||||
| Shell | cURL, HTTPie, Wget |
|
| Shell | cURL, HTTPie, Wget |
|
||||||
| Swift | URLSession |
|
| Swift | URLSession |
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaak/httpsnippet",
|
"name": "@yaak/httpsnippet",
|
||||||
"private": true,
|
|
||||||
"version": "1.0.3",
|
|
||||||
"displayName": "HTTP Snippet",
|
"displayName": "HTTP Snippet",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"private": true,
|
||||||
"description": "Generate code snippets for HTTP requests in various languages and frameworks",
|
"description": "Generate code snippets for HTTP requests in various languages and frameworks",
|
||||||
"minYaakVersion": "2026.2.0-beta.10",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mountain-loop/yaak.git",
|
"url": "https://github.com/mountain-loop/yaak.git",
|
||||||
@@ -20,5 +19,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
},
|
||||||
|
"minYaakVersion": "2026.2.0-beta.10"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { availableTargets, type HarRequest, HTTPSnippet } from '@readme/httpsnippet';
|
import { availableTargets, type HarRequest, HTTPSnippet } from "@readme/httpsnippet";
|
||||||
import type { EditorLanguage, HttpRequest, PluginDefinition } from '@yaakapp/api';
|
import type { EditorLanguage, HttpRequest, PluginDefinition } from "@yaakapp/api";
|
||||||
|
|
||||||
// Get all available targets and build select options
|
// Get all available targets and build select options
|
||||||
const targets = availableTargets();
|
const targets = availableTargets();
|
||||||
|
|
||||||
// Targets to exclude from the language list
|
// Targets to exclude from the language list
|
||||||
const excludedTargets = new Set(['json']);
|
const excludedTargets = new Set(["json"]);
|
||||||
|
|
||||||
// Build language (target) options
|
// Build language (target) options
|
||||||
const languageOptions = targets
|
const languageOptions = targets
|
||||||
@@ -17,8 +17,8 @@ const languageOptions = targets
|
|||||||
|
|
||||||
// Preferred clients per target (shown first in the list)
|
// Preferred clients per target (shown first in the list)
|
||||||
const preferredClients: Record<string, string> = {
|
const preferredClients: Record<string, string> = {
|
||||||
javascript: 'fetch',
|
javascript: "fetch",
|
||||||
node: 'fetch',
|
node: "fetch",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get client options for a given target key
|
// Get client options for a given target key
|
||||||
@@ -41,50 +41,50 @@ function getClientOptions(targetKey: string) {
|
|||||||
// Get default client for a target
|
// Get default client for a target
|
||||||
function getDefaultClient(targetKey: string): string {
|
function getDefaultClient(targetKey: string): string {
|
||||||
const options = getClientOptions(targetKey);
|
const options = getClientOptions(targetKey);
|
||||||
return options[0]?.value ?? '';
|
return options[0]?.value ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
const defaultTarget = 'javascript';
|
const defaultTarget = "javascript";
|
||||||
|
|
||||||
// Map httpsnippet target key to editor language for syntax highlighting
|
// Map httpsnippet target key to editor language for syntax highlighting
|
||||||
const editorLanguageMap: Record<string, EditorLanguage> = {
|
const editorLanguageMap: Record<string, EditorLanguage> = {
|
||||||
c: 'c',
|
c: "c",
|
||||||
clojure: 'clojure',
|
clojure: "clojure",
|
||||||
csharp: 'csharp',
|
csharp: "csharp",
|
||||||
go: 'go',
|
go: "go",
|
||||||
http: 'http',
|
http: "http",
|
||||||
java: 'java',
|
java: "java",
|
||||||
javascript: 'javascript',
|
javascript: "javascript",
|
||||||
kotlin: 'kotlin',
|
kotlin: "kotlin",
|
||||||
node: 'javascript',
|
node: "javascript",
|
||||||
objc: 'objective_c',
|
objc: "objective_c",
|
||||||
ocaml: 'ocaml',
|
ocaml: "ocaml",
|
||||||
php: 'php',
|
php: "php",
|
||||||
powershell: 'powershell',
|
powershell: "powershell",
|
||||||
python: 'python',
|
python: "python",
|
||||||
r: 'r',
|
r: "r",
|
||||||
ruby: 'ruby',
|
ruby: "ruby",
|
||||||
shell: 'shell',
|
shell: "shell",
|
||||||
swift: 'swift',
|
swift: "swift",
|
||||||
};
|
};
|
||||||
|
|
||||||
function getEditorLanguage(targetKey: string): EditorLanguage {
|
function getEditorLanguage(targetKey: string): EditorLanguage {
|
||||||
return editorLanguageMap[targetKey] ?? 'text';
|
return editorLanguageMap[targetKey] ?? "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Yaak HttpRequest to HAR format
|
// Convert Yaak HttpRequest to HAR format
|
||||||
function toHarRequest(request: Partial<HttpRequest>) {
|
function toHarRequest(request: Partial<HttpRequest>) {
|
||||||
// Build URL with query parameters
|
// Build URL with query parameters
|
||||||
let finalUrl = request.url || '';
|
let finalUrl = request.url || "";
|
||||||
const urlParams = (request.urlParameters ?? []).filter((p) => p.enabled !== false && !!p.name);
|
const urlParams = (request.urlParameters ?? []).filter((p) => p.enabled !== false && !!p.name);
|
||||||
if (urlParams.length > 0) {
|
if (urlParams.length > 0) {
|
||||||
const [base, hash] = finalUrl.split('#');
|
const [base, hash] = finalUrl.split("#");
|
||||||
const separator = base?.includes('?') ? '&' : '?';
|
const separator = base?.includes("?") ? "&" : "?";
|
||||||
const queryString = urlParams
|
const queryString = urlParams
|
||||||
.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
|
.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
|
||||||
.join('&');
|
.join("&");
|
||||||
finalUrl = base + separator + queryString + (hash ? `#${hash}` : '');
|
finalUrl = base + separator + queryString + (hash ? `#${hash}` : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build headers array
|
// Build headers array
|
||||||
@@ -94,75 +94,75 @@ function toHarRequest(request: Partial<HttpRequest>) {
|
|||||||
|
|
||||||
// Handle authentication
|
// Handle authentication
|
||||||
if (request.authentication?.disabled !== true) {
|
if (request.authentication?.disabled !== true) {
|
||||||
if (request.authenticationType === 'basic') {
|
if (request.authenticationType === "basic") {
|
||||||
const credentials = btoa(
|
const credentials = btoa(
|
||||||
`${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`,
|
`${request.authentication?.username ?? ""}:${request.authentication?.password ?? ""}`,
|
||||||
);
|
);
|
||||||
headers.push({ name: 'Authorization', value: `Basic ${credentials}` });
|
headers.push({ name: "Authorization", value: `Basic ${credentials}` });
|
||||||
} else if (request.authenticationType === 'bearer') {
|
} else if (request.authenticationType === "bearer") {
|
||||||
const prefix = request.authentication?.prefix ?? 'Bearer';
|
const prefix = request.authentication?.prefix ?? "Bearer";
|
||||||
const token = request.authentication?.token ?? '';
|
const token = request.authentication?.token ?? "";
|
||||||
headers.push({ name: 'Authorization', value: `${prefix} ${token}`.trim() });
|
headers.push({ name: "Authorization", value: `${prefix} ${token}`.trim() });
|
||||||
} else if (request.authenticationType === 'apikey') {
|
} else if (request.authenticationType === "apikey") {
|
||||||
if (request.authentication?.location === 'header') {
|
if (request.authentication?.location === "header") {
|
||||||
headers.push({
|
headers.push({
|
||||||
name: request.authentication?.key ?? 'X-Api-Key',
|
name: request.authentication?.key ?? "X-Api-Key",
|
||||||
value: request.authentication?.value ?? '',
|
value: request.authentication?.value ?? "",
|
||||||
});
|
});
|
||||||
} else if (request.authentication?.location === 'query') {
|
} else if (request.authentication?.location === "query") {
|
||||||
const sep = finalUrl.includes('?') ? '&' : '?';
|
const sep = finalUrl.includes("?") ? "&" : "?";
|
||||||
finalUrl = [
|
finalUrl = [
|
||||||
finalUrl,
|
finalUrl,
|
||||||
sep,
|
sep,
|
||||||
encodeURIComponent(request.authentication?.key ?? 'token'),
|
encodeURIComponent(request.authentication?.key ?? "token"),
|
||||||
'=',
|
"=",
|
||||||
encodeURIComponent(request.authentication?.value ?? ''),
|
encodeURIComponent(request.authentication?.value ?? ""),
|
||||||
].join('');
|
].join("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build HAR request object
|
// Build HAR request object
|
||||||
const har: Record<string, unknown> = {
|
const har: Record<string, unknown> = {
|
||||||
method: request.method || 'GET',
|
method: request.method || "GET",
|
||||||
url: finalUrl,
|
url: finalUrl,
|
||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle request body
|
// Handle request body
|
||||||
const bodyType = request.bodyType ?? 'none';
|
const bodyType = request.bodyType ?? "none";
|
||||||
if (bodyType !== 'none' && request.body) {
|
if (bodyType !== "none" && request.body) {
|
||||||
if (bodyType === 'application/x-www-form-urlencoded' && Array.isArray(request.body.form)) {
|
if (bodyType === "application/x-www-form-urlencoded" && Array.isArray(request.body.form)) {
|
||||||
const params = request.body.form
|
const params = request.body.form
|
||||||
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
|
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
|
||||||
.map((p: { name: string; value: string }) => ({ name: p.name, value: p.value }));
|
.map((p: { name: string; value: string }) => ({ name: p.name, value: p.value }));
|
||||||
har.postData = {
|
har.postData = {
|
||||||
mimeType: 'application/x-www-form-urlencoded',
|
mimeType: "application/x-www-form-urlencoded",
|
||||||
params,
|
params,
|
||||||
};
|
};
|
||||||
} else if (bodyType === 'multipart/form-data' && Array.isArray(request.body.form)) {
|
} else if (bodyType === "multipart/form-data" && Array.isArray(request.body.form)) {
|
||||||
const params = request.body.form
|
const params = request.body.form
|
||||||
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
|
.filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name)
|
||||||
.map((p: { name: string; value: string; file?: string; contentType?: string }) => {
|
.map((p: { name: string; value: string; file?: string; contentType?: string }) => {
|
||||||
const param: Record<string, string> = { name: p.name, value: p.value || '' };
|
const param: Record<string, string> = { name: p.name, value: p.value || "" };
|
||||||
if (p.file) param.fileName = p.file;
|
if (p.file) param.fileName = p.file;
|
||||||
if (p.contentType) param.contentType = p.contentType;
|
if (p.contentType) param.contentType = p.contentType;
|
||||||
return param;
|
return param;
|
||||||
});
|
});
|
||||||
har.postData = {
|
har.postData = {
|
||||||
mimeType: 'multipart/form-data',
|
mimeType: "multipart/form-data",
|
||||||
params,
|
params,
|
||||||
};
|
};
|
||||||
} else if (bodyType === 'graphql' && typeof request.body.query === 'string') {
|
} else if (bodyType === "graphql" && typeof request.body.query === "string") {
|
||||||
const body = {
|
const body = {
|
||||||
query: request.body.query || '',
|
query: request.body.query || "",
|
||||||
variables: maybeParseJSON(request.body.variables, undefined),
|
variables: maybeParseJSON(request.body.variables, undefined),
|
||||||
};
|
};
|
||||||
har.postData = {
|
har.postData = {
|
||||||
mimeType: 'application/json',
|
mimeType: "application/json",
|
||||||
text: JSON.stringify(body),
|
text: JSON.stringify(body),
|
||||||
};
|
};
|
||||||
} else if (typeof request.body.text === 'string') {
|
} else if (typeof request.body.text === "string") {
|
||||||
har.postData = {
|
har.postData = {
|
||||||
mimeType: bodyType,
|
mimeType: bodyType,
|
||||||
text: request.body.text,
|
text: request.body.text,
|
||||||
@@ -174,7 +174,7 @@ function toHarRequest(request: Partial<HttpRequest>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
|
function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
|
||||||
if (typeof v !== 'string') return fallback;
|
if (typeof v !== "string") return fallback;
|
||||||
try {
|
try {
|
||||||
return JSON.parse(v);
|
return JSON.parse(v);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -185,20 +185,20 @@ function maybeParseJSON<T>(v: unknown, fallback: T): unknown {
|
|||||||
export const plugin: PluginDefinition = {
|
export const plugin: PluginDefinition = {
|
||||||
httpRequestActions: [
|
httpRequestActions: [
|
||||||
{
|
{
|
||||||
label: 'Generate Code Snippet',
|
label: "Generate Code Snippet",
|
||||||
icon: 'copy',
|
icon: "copy",
|
||||||
async onSelect(ctx, args) {
|
async onSelect(ctx, args) {
|
||||||
// Render the request with variables resolved
|
// Render the request with variables resolved
|
||||||
const renderedRequest = await ctx.httpRequest.render({
|
const renderedRequest = await ctx.httpRequest.render({
|
||||||
httpRequest: args.httpRequest,
|
httpRequest: args.httpRequest,
|
||||||
purpose: 'send',
|
purpose: "send",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert to HAR format
|
// Convert to HAR format
|
||||||
const harRequest = toHarRequest(renderedRequest) as HarRequest;
|
const harRequest = toHarRequest(renderedRequest) as HarRequest;
|
||||||
|
|
||||||
// Get previously selected language or use defaults
|
// Get previously selected language or use defaults
|
||||||
const storedTarget = await ctx.store.get<string>('selectedTarget');
|
const storedTarget = await ctx.store.get<string>("selectedTarget");
|
||||||
const initialTarget = storedTarget || defaultTarget;
|
const initialTarget = storedTarget || defaultTarget;
|
||||||
const storedClient = await ctx.store.get<string>(`selectedClient:${initialTarget}`);
|
const storedClient = await ctx.store.get<string>(`selectedClient:${initialTarget}`);
|
||||||
const initialClient = storedClient || getDefaultClient(initialTarget);
|
const initialClient = storedClient || getDefaultClient(initialTarget);
|
||||||
@@ -210,39 +210,39 @@ export const plugin: PluginDefinition = {
|
|||||||
target as Parameters<typeof snippet.convert>[0],
|
target as Parameters<typeof snippet.convert>[0],
|
||||||
client as Parameters<typeof snippet.convert>[1],
|
client as Parameters<typeof snippet.convert>[1],
|
||||||
);
|
);
|
||||||
return (Array.isArray(result) ? result.join('\n') : result || '').replace(/\r\n/g, '\n');
|
return (Array.isArray(result) ? result.join("\n") : result || "").replace(/\r\n/g, "\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate initial code preview
|
// Generate initial code preview
|
||||||
let initialCode = '';
|
let initialCode = "";
|
||||||
try {
|
try {
|
||||||
initialCode = generateSnippet(initialTarget, initialClient);
|
initialCode = generateSnippet(initialTarget, initialClient);
|
||||||
} catch {
|
} catch {
|
||||||
initialCode = '// Error generating snippet';
|
initialCode = "// Error generating snippet";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show dialog with language/library selectors and code preview
|
// Show dialog with language/library selectors and code preview
|
||||||
const result = await ctx.prompt.form({
|
const result = await ctx.prompt.form({
|
||||||
id: 'httpsnippet',
|
id: "httpsnippet",
|
||||||
title: 'Generate Code Snippet',
|
title: "Generate Code Snippet",
|
||||||
confirmText: 'Copy to Clipboard',
|
confirmText: "Copy to Clipboard",
|
||||||
cancelText: 'Cancel',
|
cancelText: "Cancel",
|
||||||
size: 'md',
|
size: "md",
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: 'h_stack',
|
type: "h_stack",
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: 'target',
|
name: "target",
|
||||||
label: 'Language',
|
label: "Language",
|
||||||
defaultValue: initialTarget,
|
defaultValue: initialTarget,
|
||||||
options: languageOptions,
|
options: languageOptions,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: "select",
|
||||||
name: `client-${initialTarget}`,
|
name: `client-${initialTarget}`,
|
||||||
label: 'Library',
|
label: "Library",
|
||||||
defaultValue: initialClient,
|
defaultValue: initialClient,
|
||||||
options: getClientOptions(initialTarget),
|
options: getClientOptions(initialTarget),
|
||||||
dynamic(_ctx, { values }) {
|
dynamic(_ctx, { values }) {
|
||||||
@@ -251,16 +251,16 @@ export const plugin: PluginDefinition = {
|
|||||||
return {
|
return {
|
||||||
name: `client-${targetKey}`,
|
name: `client-${targetKey}`,
|
||||||
options,
|
options,
|
||||||
defaultValue: options[0]?.value ?? '',
|
defaultValue: options[0]?.value ?? "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'editor',
|
type: "editor",
|
||||||
name: 'code',
|
name: "code",
|
||||||
label: 'Preview',
|
label: "Preview",
|
||||||
language: getEditorLanguage(initialTarget),
|
language: getEditorLanguage(initialTarget),
|
||||||
defaultValue: initialCode,
|
defaultValue: initialCode,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@@ -274,7 +274,7 @@ export const plugin: PluginDefinition = {
|
|||||||
try {
|
try {
|
||||||
code = generateSnippet(targetKey, clientKey);
|
code = generateSnippet(targetKey, clientKey);
|
||||||
} catch {
|
} catch {
|
||||||
code = '// Error generating snippet';
|
code = "// Error generating snippet";
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
defaultValue: code,
|
defaultValue: code,
|
||||||
@@ -291,7 +291,7 @@ export const plugin: PluginDefinition = {
|
|||||||
const selectedClient = String(
|
const selectedClient = String(
|
||||||
result[`client-${selectedTarget}`] || getDefaultClient(selectedTarget),
|
result[`client-${selectedTarget}`] || getDefaultClient(selectedTarget),
|
||||||
);
|
);
|
||||||
await ctx.store.set('selectedTarget', selectedTarget);
|
await ctx.store.set("selectedTarget", selectedTarget);
|
||||||
await ctx.store.set(`selectedClient:${selectedTarget}`, selectedClient);
|
await ctx.store.set(`selectedClient:${selectedTarget}`, selectedClient);
|
||||||
|
|
||||||
// Generate snippet for the selected language
|
// Generate snippet for the selected language
|
||||||
@@ -299,15 +299,15 @@ export const plugin: PluginDefinition = {
|
|||||||
const codeText = generateSnippet(selectedTarget, selectedClient);
|
const codeText = generateSnippet(selectedTarget, selectedClient);
|
||||||
await ctx.clipboard.copyText(codeText);
|
await ctx.clipboard.copyText(codeText);
|
||||||
await ctx.toast.show({
|
await ctx.toast.show({
|
||||||
message: 'Code snippet copied to clipboard',
|
message: "Code snippet copied to clipboard",
|
||||||
icon: 'copy',
|
icon: "copy",
|
||||||
color: 'success',
|
color: "success",
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await ctx.toast.show({
|
await ctx.toast.show({
|
||||||
message: `Failed to generate snippet: ${err instanceof Error ? err.message : String(err)}`,
|
message: `Failed to generate snippet: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
icon: 'alert_triangle',
|
icon: "alert_triangle",
|
||||||
color: 'danger',
|
color: "danger",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaak/mcp-server",
|
"name": "@yaak/mcp-server",
|
||||||
"private": true,
|
|
||||||
"version": "0.2.1",
|
|
||||||
"displayName": "MCP Server",
|
"displayName": "MCP Server",
|
||||||
|
"version": "0.2.1",
|
||||||
|
"private": true,
|
||||||
"description": "Expose Yaak functionality via Model Context Protocol",
|
"description": "Expose Yaak functionality via Model Context Protocol",
|
||||||
"minYaakVersion": "2026.1.0",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mountain-loop/yaak.git",
|
"url": "https://github.com/mountain-loop/yaak.git",
|
||||||
@@ -24,5 +23,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
},
|
||||||
|
"minYaakVersion": "2026.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Context, PluginDefinition } from '@yaakapp/api';
|
import type { Context, PluginDefinition } from "@yaakapp/api";
|
||||||
import { createMcpServer } from './server.js';
|
import { createMcpServer } from "./server.js";
|
||||||
|
|
||||||
const serverPort = parseInt(process.env.YAAK_PLUGIN_MCP_SERVER_PORT ?? '64343', 10);
|
const serverPort = parseInt(process.env.YAAK_PLUGIN_MCP_SERVER_PORT ?? "64343", 10);
|
||||||
|
|
||||||
let mcpServer: Awaited<ReturnType<typeof createMcpServer>> | null = null;
|
let mcpServer: Awaited<ReturnType<typeof createMcpServer>> | null = null;
|
||||||
|
|
||||||
@@ -9,16 +9,16 @@ export const plugin: PluginDefinition = {
|
|||||||
async init(ctx: Context) {
|
async init(ctx: Context) {
|
||||||
// Start the server after waiting, so there's an active window open to do things
|
// Start the server after waiting, so there's an active window open to do things
|
||||||
// like show the startup toast.
|
// like show the startup toast.
|
||||||
console.log('Initializing MCP Server plugin');
|
console.log("Initializing MCP Server plugin");
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
mcpServer = createMcpServer({ yaak: ctx }, serverPort);
|
mcpServer = createMcpServer({ yaak: ctx }, serverPort);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to start MCP server:', err);
|
console.error("Failed to start MCP server:", err);
|
||||||
void ctx.toast.show({
|
void ctx.toast.show({
|
||||||
message: `Failed to start MCP Server: ${err instanceof Error ? err.message : String(err)}`,
|
message: `Failed to start MCP Server: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
icon: 'alert_triangle',
|
icon: "alert_triangle",
|
||||||
color: 'danger',
|
color: "danger",
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ export const plugin: PluginDefinition = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async dispose() {
|
async dispose() {
|
||||||
console.log('Disposing MCP Server plugin');
|
console.log("Disposing MCP Server plugin");
|
||||||
|
|
||||||
if (mcpServer) {
|
if (mcpServer) {
|
||||||
await mcpServer.close();
|
await mcpServer.close();
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { StreamableHTTPTransport } from '@hono/mcp';
|
import { StreamableHTTPTransport } from "@hono/mcp";
|
||||||
import { serve } from '@hono/node-server';
|
import { serve } from "@hono/node-server";
|
||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { Hono } from 'hono';
|
import { Hono } from "hono";
|
||||||
import { registerFolderTools } from './tools/folder.js';
|
import { registerFolderTools } from "./tools/folder.js";
|
||||||
import { registerHttpRequestTools } from './tools/httpRequest.js';
|
import { registerHttpRequestTools } from "./tools/httpRequest.js";
|
||||||
import { registerToastTools } from './tools/toast.js';
|
import { registerToastTools } from "./tools/toast.js";
|
||||||
import { registerWindowTools } from './tools/window.js';
|
import { registerWindowTools } from "./tools/window.js";
|
||||||
import { registerWorkspaceTools } from './tools/workspace.js';
|
import { registerWorkspaceTools } from "./tools/workspace.js";
|
||||||
import type { McpServerContext } from './types.js';
|
import type { McpServerContext } from "./types.js";
|
||||||
|
|
||||||
export function createMcpServer(ctx: McpServerContext, port: number) {
|
export function createMcpServer(ctx: McpServerContext, port: number) {
|
||||||
console.log('Creating MCP server on port', port);
|
console.log("Creating MCP server on port", port);
|
||||||
const mcpServer = new McpServer({
|
const mcpServer = new McpServer({
|
||||||
name: 'yaak-mcp-server',
|
name: "yaak-mcp-server",
|
||||||
version: '0.1.0',
|
version: "0.1.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register all tools
|
// Register all tools
|
||||||
@@ -26,14 +26,14 @@ export function createMcpServer(ctx: McpServerContext, port: number) {
|
|||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
const transport = new StreamableHTTPTransport();
|
const transport = new StreamableHTTPTransport();
|
||||||
|
|
||||||
app.all('/mcp', async (c) => {
|
app.all("/mcp", async (c) => {
|
||||||
if (!mcpServer.isConnected()) {
|
if (!mcpServer.isConnected()) {
|
||||||
// Connect the mcp with the transport
|
// Connect the mcp with the transport
|
||||||
await mcpServer.connect(transport);
|
await mcpServer.connect(transport);
|
||||||
void ctx.yaak.toast.show({
|
void ctx.yaak.toast.show({
|
||||||
message: `MCP Server connected`,
|
message: `MCP Server connected`,
|
||||||
icon: 'info',
|
icon: "info",
|
||||||
color: 'info',
|
color: "info",
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,15 +43,15 @@ export function createMcpServer(ctx: McpServerContext, port: number) {
|
|||||||
const honoServer = serve(
|
const honoServer = serve(
|
||||||
{
|
{
|
||||||
port,
|
port,
|
||||||
hostname: '127.0.0.1',
|
hostname: "127.0.0.1",
|
||||||
fetch: app.fetch,
|
fetch: app.fetch,
|
||||||
},
|
},
|
||||||
(info) => {
|
(info) => {
|
||||||
console.log('Started MCP server on ', info.address);
|
console.log("Started MCP server on ", info.address);
|
||||||
void ctx.yaak.toast.show({
|
void ctx.yaak.toast.show({
|
||||||
message: `MCP Server running on http://127.0.0.1:${info.port}`,
|
message: `MCP Server running on http://127.0.0.1:${info.port}`,
|
||||||
icon: 'info',
|
icon: "info",
|
||||||
color: 'secondary',
|
color: "secondary",
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import * as z from 'zod';
|
import * as z from "zod";
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from "../types.js";
|
||||||
import { getWorkspaceContext } from './helpers.js';
|
import { getWorkspaceContext } from "./helpers.js";
|
||||||
import {
|
import {
|
||||||
authenticationSchema,
|
authenticationSchema,
|
||||||
authenticationTypeSchema,
|
authenticationTypeSchema,
|
||||||
headersSchema,
|
headersSchema,
|
||||||
workspaceIdSchema,
|
workspaceIdSchema,
|
||||||
} from './schemas.js';
|
} from "./schemas.js";
|
||||||
|
|
||||||
export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'list_folders',
|
"list_folders",
|
||||||
{
|
{
|
||||||
title: 'List Folders',
|
title: "List Folders",
|
||||||
description: 'List all folders in a workspace',
|
description: "List all folders in a workspace",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text' as const,
|
type: "text" as const,
|
||||||
text: JSON.stringify(folders, null, 2),
|
text: JSON.stringify(folders, null, 2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -35,12 +35,12 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'get_folder',
|
"get_folder",
|
||||||
{
|
{
|
||||||
title: 'Get Folder',
|
title: "Get Folder",
|
||||||
description: 'Get details of a specific folder by ID',
|
description: "Get details of a specific folder by ID",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('The folder ID'),
|
id: z.string().describe("The folder ID"),
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -51,7 +51,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text' as const,
|
type: "text" as const,
|
||||||
text: JSON.stringify(folder, null, 2),
|
text: JSON.stringify(folder, null, 2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -60,17 +60,17 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'create_folder',
|
"create_folder",
|
||||||
{
|
{
|
||||||
title: 'Create Folder',
|
title: "Create Folder",
|
||||||
description: 'Create a new folder in a workspace',
|
description: "Create a new folder in a workspace",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
name: z.string().describe('Folder name'),
|
name: z.string().describe("Folder name"),
|
||||||
folderId: z.string().optional().describe('Parent folder ID (for nested folders)'),
|
folderId: z.string().optional().describe("Parent folder ID (for nested folders)"),
|
||||||
description: z.string().optional().describe('Folder description'),
|
description: z.string().optional().describe("Folder description"),
|
||||||
sortPriority: z.number().optional().describe('Sort priority for ordering'),
|
sortPriority: z.number().optional().describe("Sort priority for ordering"),
|
||||||
headers: headersSchema.describe('Default headers to apply to requests in this folder'),
|
headers: headersSchema.describe("Default headers to apply to requests in this folder"),
|
||||||
authenticationType: authenticationTypeSchema,
|
authenticationType: authenticationTypeSchema,
|
||||||
authentication: authenticationSchema,
|
authentication: authenticationSchema,
|
||||||
},
|
},
|
||||||
@@ -79,7 +79,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId);
|
const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId);
|
||||||
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
|
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
|
||||||
if (!workspaceId) {
|
if (!workspaceId) {
|
||||||
throw new Error('No workspace is open');
|
throw new Error("No workspace is open");
|
||||||
}
|
}
|
||||||
|
|
||||||
const folder = await workspaceCtx.yaak.folder.create({
|
const folder = await workspaceCtx.yaak.folder.create({
|
||||||
@@ -88,24 +88,24 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: JSON.stringify(folder, null, 2) }],
|
content: [{ type: "text" as const, text: JSON.stringify(folder, null, 2) }],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'update_folder',
|
"update_folder",
|
||||||
{
|
{
|
||||||
title: 'Update Folder',
|
title: "Update Folder",
|
||||||
description: 'Update an existing folder',
|
description: "Update an existing folder",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('Folder ID to update'),
|
id: z.string().describe("Folder ID to update"),
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
name: z.string().optional().describe('Folder name'),
|
name: z.string().optional().describe("Folder name"),
|
||||||
folderId: z.string().optional().describe('Parent folder ID (for nested folders)'),
|
folderId: z.string().optional().describe("Parent folder ID (for nested folders)"),
|
||||||
description: z.string().optional().describe('Folder description'),
|
description: z.string().optional().describe("Folder description"),
|
||||||
sortPriority: z.number().optional().describe('Sort priority for ordering'),
|
sortPriority: z.number().optional().describe("Sort priority for ordering"),
|
||||||
headers: headersSchema.describe('Default headers to apply to requests in this folder'),
|
headers: headersSchema.describe("Default headers to apply to requests in this folder"),
|
||||||
authenticationType: authenticationTypeSchema,
|
authenticationType: authenticationTypeSchema,
|
||||||
authentication: authenticationSchema,
|
authentication: authenticationSchema,
|
||||||
},
|
},
|
||||||
@@ -124,24 +124,24 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
|
|||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: JSON.stringify(folder, null, 2) }],
|
content: [{ type: "text" as const, text: JSON.stringify(folder, null, 2) }],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'delete_folder',
|
"delete_folder",
|
||||||
{
|
{
|
||||||
title: 'Delete Folder',
|
title: "Delete Folder",
|
||||||
description: 'Delete a folder by ID',
|
description: "Delete a folder by ID",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('Folder ID to delete'),
|
id: z.string().describe("Folder ID to delete"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ id }) => {
|
async ({ id }) => {
|
||||||
const folder = await ctx.yaak.folder.delete({ id });
|
const folder = await ctx.yaak.folder.delete({ id });
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: `Deleted: ${folder.name} (${folder.id})` }],
|
content: [{ type: "text" as const, text: `Deleted: ${folder.name} (${folder.id})` }],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from "../types.js";
|
||||||
|
|
||||||
export async function getWorkspaceContext(
|
export async function getWorkspaceContext(
|
||||||
ctx: McpServerContext,
|
ctx: McpServerContext,
|
||||||
@@ -7,7 +7,7 @@ export async function getWorkspaceContext(
|
|||||||
const workspaces = await ctx.yaak.workspace.list();
|
const workspaces = await ctx.yaak.workspace.list();
|
||||||
|
|
||||||
if (!workspaceId && workspaces.length > 1) {
|
if (!workspaceId && workspaces.length > 1) {
|
||||||
const workspaceList = workspaces.map((w, i) => `${i + 1}. ${w.name} (ID: ${w.id})`).join('\n');
|
const workspaceList = workspaces.map((w, i) => `${i + 1}. ${w.name} (ID: ${w.id})`).join("\n");
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Multiple workspaces are open. Please specify which workspace to use.\n\n` +
|
`Multiple workspaces are open. Please specify which workspace to use.\n\n` +
|
||||||
`Currently open workspaces:\n${workspaceList}\n\n` +
|
`Currently open workspaces:\n${workspaceList}\n\n` +
|
||||||
@@ -19,7 +19,7 @@ export async function getWorkspaceContext(
|
|||||||
|
|
||||||
const workspace = workspaceId ? workspaces.find((w) => w.id === workspaceId) : workspaces[0];
|
const workspace = workspaceId ? workspaces.find((w) => w.id === workspaceId) : workspaces[0];
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
const workspaceList = workspaces.map((w) => `- ${w.name} (ID: ${w.id})`).join('\n');
|
const workspaceList = workspaces.map((w) => `- ${w.name} (ID: ${w.id})`).join("\n");
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Workspace with ID "${workspaceId}" not found.\n\n` +
|
`Workspace with ID "${workspaceId}" not found.\n\n` +
|
||||||
`Available workspaces:\n${workspaceList}`,
|
`Available workspaces:\n${workspaceList}`,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import * as z from 'zod';
|
import * as z from "zod";
|
||||||
import type { McpServerContext } from '../types.js';
|
import type { McpServerContext } from "../types.js";
|
||||||
import { getWorkspaceContext } from './helpers.js';
|
import { getWorkspaceContext } from "./helpers.js";
|
||||||
import {
|
import {
|
||||||
authenticationSchema,
|
authenticationSchema,
|
||||||
authenticationTypeSchema,
|
authenticationTypeSchema,
|
||||||
@@ -10,14 +10,14 @@ import {
|
|||||||
headersSchema,
|
headersSchema,
|
||||||
urlParametersSchema,
|
urlParametersSchema,
|
||||||
workspaceIdSchema,
|
workspaceIdSchema,
|
||||||
} from './schemas.js';
|
} from "./schemas.js";
|
||||||
|
|
||||||
export function registerHttpRequestTools(server: McpServer, ctx: McpServerContext) {
|
export function registerHttpRequestTools(server: McpServer, ctx: McpServerContext) {
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'list_http_requests',
|
"list_http_requests",
|
||||||
{
|
{
|
||||||
title: 'List HTTP Requests',
|
title: "List HTTP Requests",
|
||||||
description: 'List all HTTP requests in a workspace',
|
description: "List all HTTP requests in a workspace",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
},
|
},
|
||||||
@@ -29,7 +29,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text' as const,
|
type: "text" as const,
|
||||||
text: JSON.stringify(requests, null, 2),
|
text: JSON.stringify(requests, null, 2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -38,12 +38,12 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'get_http_request',
|
"get_http_request",
|
||||||
{
|
{
|
||||||
title: 'Get HTTP Request',
|
title: "Get HTTP Request",
|
||||||
description: 'Get details of a specific HTTP request by ID',
|
description: "Get details of a specific HTTP request by ID",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('The HTTP request ID'),
|
id: z.string().describe("The HTTP request ID"),
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -54,7 +54,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text' as const,
|
type: "text" as const,
|
||||||
text: JSON.stringify(request, null, 2),
|
text: JSON.stringify(request, null, 2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -63,13 +63,13 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'send_http_request',
|
"send_http_request",
|
||||||
{
|
{
|
||||||
title: 'Send HTTP Request',
|
title: "Send HTTP Request",
|
||||||
description: 'Send an HTTP request and get the response',
|
description: "Send an HTTP request and get the response",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('The HTTP request ID to send'),
|
id: z.string().describe("The HTTP request ID to send"),
|
||||||
environmentId: z.string().optional().describe('Optional environment ID to use'),
|
environmentId: z.string().optional().describe("Optional environment ID to use"),
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -85,7 +85,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text' as const,
|
type: "text" as const,
|
||||||
text: JSON.stringify(response, null, 2),
|
text: JSON.stringify(response, null, 2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -94,21 +94,21 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'create_http_request',
|
"create_http_request",
|
||||||
{
|
{
|
||||||
title: 'Create HTTP Request',
|
title: "Create HTTP Request",
|
||||||
description: 'Create a new HTTP request',
|
description: "Create a new HTTP request",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe('Request name (empty string to auto-generate from URL)'),
|
.describe("Request name (empty string to auto-generate from URL)"),
|
||||||
url: z.string().describe('Request URL'),
|
url: z.string().describe("Request URL"),
|
||||||
method: z.string().optional().describe('HTTP method (defaults to GET)'),
|
method: z.string().optional().describe("HTTP method (defaults to GET)"),
|
||||||
folderId: z.string().optional().describe('Parent folder ID'),
|
folderId: z.string().optional().describe("Parent folder ID"),
|
||||||
description: z.string().optional().describe('Request description'),
|
description: z.string().optional().describe("Request description"),
|
||||||
headers: headersSchema.describe('Request headers'),
|
headers: headersSchema.describe("Request headers"),
|
||||||
urlParameters: urlParametersSchema,
|
urlParameters: urlParametersSchema,
|
||||||
bodyType: bodyTypeSchema,
|
bodyType: bodyTypeSchema,
|
||||||
body: bodySchema,
|
body: bodySchema,
|
||||||
@@ -120,7 +120,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId);
|
const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId);
|
||||||
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
|
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
|
||||||
if (!workspaceId) {
|
if (!workspaceId) {
|
||||||
throw new Error('No workspace is open');
|
throw new Error("No workspace is open");
|
||||||
}
|
}
|
||||||
|
|
||||||
const httpRequest = await workspaceCtx.yaak.httpRequest.create({
|
const httpRequest = await workspaceCtx.yaak.httpRequest.create({
|
||||||
@@ -129,25 +129,25 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
|
content: [{ type: "text" as const, text: JSON.stringify(httpRequest, null, 2) }],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'update_http_request',
|
"update_http_request",
|
||||||
{
|
{
|
||||||
title: 'Update HTTP Request',
|
title: "Update HTTP Request",
|
||||||
description: 'Update an existing HTTP request',
|
description: "Update an existing HTTP request",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('HTTP request ID to update'),
|
id: z.string().describe("HTTP request ID to update"),
|
||||||
workspaceId: workspaceIdSchema,
|
workspaceId: workspaceIdSchema,
|
||||||
name: z.string().optional().describe('Request name'),
|
name: z.string().optional().describe("Request name"),
|
||||||
url: z.string().optional().describe('Request URL'),
|
url: z.string().optional().describe("Request URL"),
|
||||||
method: z.string().optional().describe('HTTP method'),
|
method: z.string().optional().describe("HTTP method"),
|
||||||
folderId: z.string().optional().describe('Parent folder ID'),
|
folderId: z.string().optional().describe("Parent folder ID"),
|
||||||
description: z.string().optional().describe('Request description'),
|
description: z.string().optional().describe("Request description"),
|
||||||
headers: headersSchema.describe('Request headers'),
|
headers: headersSchema.describe("Request headers"),
|
||||||
urlParameters: urlParametersSchema,
|
urlParameters: urlParametersSchema,
|
||||||
bodyType: bodyTypeSchema,
|
bodyType: bodyTypeSchema,
|
||||||
body: bodySchema,
|
body: bodySchema,
|
||||||
@@ -169,25 +169,25 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex
|
|||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
|
content: [{ type: "text" as const, text: JSON.stringify(httpRequest, null, 2) }],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'delete_http_request',
|
"delete_http_request",
|
||||||
{
|
{
|
||||||
title: 'Delete HTTP Request',
|
title: "Delete HTTP Request",
|
||||||
description: 'Delete an HTTP request by ID',
|
description: "Delete an HTTP request by ID",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
id: z.string().describe('HTTP request ID to delete'),
|
id: z.string().describe("HTTP request ID to delete"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ id }) => {
|
async ({ id }) => {
|
||||||
const httpRequest = await ctx.yaak.httpRequest.delete({ id });
|
const httpRequest = await ctx.yaak.httpRequest.delete({ id });
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{ type: 'text' as const, text: `Deleted: ${httpRequest.name} (${httpRequest.id})` },
|
{ type: "text" as const, text: `Deleted: ${httpRequest.name} (${httpRequest.id})` },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user