mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 16:02:11 +00:00
Merge branch 'main' into rf/snapshotMigrations
This commit is contained in:
@@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## [1.535.0](https://github.com/windmill-labs/windmill/compare/v1.534.1...v1.535.0) (2025-08-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **aichat:** show diff mode on inline scripts changes ([#6454](https://github.com/windmill-labs/windmill/issues/6454)) ([eca3109](https://github.com/windmill-labs/windmill/commit/eca3109ec63967e3041521bf74d34a12c70f5ff8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix opening advanced popup for run resetting tag to default ([d328894](https://github.com/windmill-labs/windmill/commit/d3288947b2d2539b2f3302059a9aad2841275a28))
|
||||
|
||||
## [1.534.1](https://github.com/windmill-labs/windmill/compare/v1.534.0...v1.534.1) (2025-08-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add alias to subquery for older postgres versions ([#6455](https://github.com/windmill-labs/windmill/issues/6455)) ([16d233b](https://github.com/windmill-labs/windmill/commit/16d233bf466fd818ec1f9235377e4c1a8239d98c))
|
||||
* **frontend:** fix test step behavior ([#6427](https://github.com/windmill-labs/windmill/issues/6427)) ([fc20b7b](https://github.com/windmill-labs/windmill/commit/fc20b7bd91d33115aacb38cc46394f9c6465aa0f))
|
||||
|
||||
## [1.534.0](https://github.com/windmill-labs/windmill/compare/v1.533.1...v1.534.0) (2025-08-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **backend:** support unencrypted connection to mssql ([#6453](https://github.com/windmill-labs/windmill/issues/6453)) ([8d31c2a](https://github.com/windmill-labs/windmill/commit/8d31c2ab0d34036dc8057611857a5d72aad8598f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aichat:** fix wrong current model logic ([#6451](https://github.com/windmill-labs/windmill/issues/6451)) ([e951c89](https://github.com/windmill-labs/windmill/commit/e951c896b865df48d331968953c9e44848236516))
|
||||
* **flow:** test this step preload step input evaluation ([1073eb0](https://github.com/windmill-labs/windmill/commit/1073eb0e682e7bd253c6d62225361b487d7f6d2f))
|
||||
|
||||
## [1.533.1](https://github.com/windmill-labs/windmill/compare/v1.533.0...v1.533.1) (2025-08-23)
|
||||
|
||||
|
||||
|
||||
@@ -332,40 +332,40 @@ you to have it being synced automatically everyday.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Environment Variable name | Default | Description | Api Server/Worker/All |
|
||||
| ----------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| DATABASE_URL | | The Postgres database url. | All |
|
||||
| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker |
|
||||
| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All |
|
||||
| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All |
|
||||
| JSON_FMT | false | Output the logs in json format instead of logfmt | All |
|
||||
| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server |
|
||||
| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server |
|
||||
| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server |
|
||||
| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker |
|
||||
| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker |
|
||||
| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker |
|
||||
| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server |
|
||||
| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server |
|
||||
| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker |
|
||||
| PYTHON_PATH | | The path to the python binary if wanting to not have it managed by uv. | Worker |
|
||||
| GO_PATH | /usr/bin/go | The path to the go binary. | Worker |
|
||||
| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker |
|
||||
| GOPROXY | | The GOPROXY env variable to use | Worker |
|
||||
| NETRC | | The netrc content to use a private go registry | Worker |
|
||||
| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker |
|
||||
| PATH | None | The path environment variable, usually inherited | Worker |
|
||||
| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker |
|
||||
| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All |
|
||||
| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server |
|
||||
| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker |
|
||||
| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker |
|
||||
| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker |
|
||||
| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server |
|
||||
| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server |
|
||||
| MIN_FREE_DISK_SPACE_MB | 15000 | Minimum amount of free space on worker. Sends critical alert if worker has less free space. | Worker |
|
||||
| RUN_UPDATE_CA_CERTIFICATE_AT_START | false | If true, runs CA certificate update command at startup before other initialization | All |
|
||||
| RUN_UPDATE_CA_CERTIFICATE_PATH | /usr/sbin/update-ca-certificates | Path to the CA certificate update command/script to run when RUN_UPDATE_CA_CERTIFICATE_AT_START is true | All |
|
||||
| Environment Variable name | Default | Description | Api Server/Worker/All |
|
||||
| ----------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
|
||||
| DATABASE_URL | | The Postgres database url. | All |
|
||||
| WORKER_GROUP | default | The worker group the worker belongs to and get its configuration pulled from | Worker |
|
||||
| MODE | standalone | The mode if the binary. Possible values: standalone, worker, server, agent | All |
|
||||
| METRICS_ADDR | None | (ee only) The socket addr at which to expose Prometheus metrics at the /metrics path. Set to "true" to expose it on port 8001 | All |
|
||||
| JSON_FMT | false | Output the logs in json format instead of logfmt | All |
|
||||
| BASE_URL | http://localhost:8000 | The base url that is exposed publicly to access your instance. Is overriden by the instance settings if any. | Server |
|
||||
| ZOMBIE_JOB_TIMEOUT | 30 | The timeout after which a job is considered to be zombie if the worker did not send pings about processing the job (every server check for zombie jobs every 30s) | Server |
|
||||
| RESTART_ZOMBIE_JOBS | true | If true then a zombie job is restarted (in-place with the same uuid and some logs), if false the zombie job is failed | Server |
|
||||
| SLEEP_QUEUE | 50 | The number of ms to sleep in between the last check for new jobs in the DB. It is multiplied by NUM_WORKERS such that in average, for one worker instance, there is one pull every SLEEP_QUEUE ms. | Worker |
|
||||
| KEEP_JOB_DIR | false | Keep the job directory after the job is done. Useful for debugging. | Worker |
|
||||
| LICENSE_KEY (EE only) | None | License key checked at startup for the Enterprise Edition of Windmill | Worker |
|
||||
| SLACK_SIGNING_SECRET | None | The signing secret of your Slack app. See [Slack documentation](https://api.slack.com/authentication/verifying-requests-from-slack) | Server |
|
||||
| COOKIE_DOMAIN | None | The domain of the cookie. If not set, the cookie will be set by the browser based on the full origin | Server |
|
||||
| DENO_PATH | /usr/bin/deno | The path to the deno binary. | Worker |
|
||||
| PYTHON_PATH | | The path to the python binary if wanting to not have it managed by uv. | Worker |
|
||||
| GO_PATH | /usr/bin/go | The path to the go binary. | Worker |
|
||||
| GOPRIVATE | | The GOPRIVATE env variable to use private go modules | Worker |
|
||||
| GOPROXY | | The GOPROXY env variable to use | Worker |
|
||||
| NETRC | | The netrc content to use a private go registry | Worker |
|
||||
| PY_CONCURRENT_DOWNLOADS | 20 | Sets the maximum number of in-flight concurrent python downloads that windmill will perform at any given time. | Worker |
|
||||
| PATH | None | The path environment variable, usually inherited | Worker |
|
||||
| HOME | None | The home directory to use for Go and Bash , usually inherited | Worker |
|
||||
| DATABASE_CONNECTIONS | 50 (Server)/3 (Worker) | The max number of connections in the database connection pool | All |
|
||||
| SUPERADMIN_SECRET | None | A token that would let the caller act as a virtual superadmin superadmin@windmill.dev | Server |
|
||||
| TIMEOUT_WAIT_RESULT | 20 | The number of seconds to wait before timeout on the 'run_wait_result' endpoint | Worker |
|
||||
| QUEUE_LIMIT_WAIT_RESULT | None | The number of max jobs in the queue before rejecting immediately the request in 'run_wait_result' endpoint. Takes precedence on the query arg. If none is specified, there are no limit. | Worker |
|
||||
| DENO_AUTH_TOKENS | None | Custom DENO_AUTH_TOKENS to pass to worker to allow the use of private modules | Worker |
|
||||
| DISABLE_RESPONSE_LOGS | false | Disable response logs | Server |
|
||||
| CREATE_WORKSPACE_REQUIRE_SUPERADMIN | true | If true, only superadmins can create new workspaces | Server |
|
||||
| MIN_FREE_DISK_SPACE_MB | 15000 | Minimum amount of free space on worker. Sends critical alert if worker has less free space. | Worker |
|
||||
| RUN_UPDATE_CA_CERTIFICATE_AT_START | false | If true, runs CA certificate update command at startup before other initialization | All |
|
||||
| RUN_UPDATE_CA_CERTIFICATE_PATH | /usr/sbin/update-ca-certificates | Path to the CA certificate update command/script to run when RUN_UPDATE_CA_CERTIFICATE_AT_START is true | All |
|
||||
|
||||
## Run a local dev setup
|
||||
|
||||
@@ -374,7 +374,6 @@ Using [Nix](./frontend/README_DEV.md#nix) (Recommended).
|
||||
See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all
|
||||
running options.
|
||||
|
||||
|
||||
### only Frontend
|
||||
|
||||
This will use the backend of <https://app.windmill.dev> but your own frontend
|
||||
@@ -400,29 +399,27 @@ npm run generate-backend-client-mac
|
||||
See the [./frontend/README_DEV.md](./frontend/README_DEV.md) file for all
|
||||
running options.
|
||||
|
||||
1. Create a Postgres Database for Windmill and create an admin role inside your
|
||||
Postgres setup. The easiest way to get a working db is to run
|
||||
1. Start a local Postgres database using for instance the `start-dev-db.sh` script which will make a database available at `postgres://postgres:changeme@localhost:5432/windmill`
|
||||
Then run the migrations using the following command:
|
||||
```
|
||||
cargo install sqlx-cli
|
||||
env DATABASE_URL=<YOUR_DATABASE_URL> sqlx migrate run
|
||||
```
|
||||
This will also avoid compile time issue with sqlx's `query!` macro
|
||||
2. Install [nsjail](https://github.com/google/nsjail) and have it accessible in
|
||||
This will also avoid compile time issue with sqlx's `query!` macro.
|
||||
2. (optional, linux only) Install [nsjail](https://github.com/google/nsjail) and have it accessible in
|
||||
your PATH
|
||||
3. Install deno and python3, have the bins at `/usr/bin/deno` and
|
||||
`/usr/local/bin/python3`
|
||||
4. Install [caddy](https://caddyserver.com)
|
||||
5. Install the [lld linker](https://lld.llvm.org/)
|
||||
6. Go to `frontend/`:
|
||||
1. `npm install`, `npm run generate-backend-client` then `npm run dev`
|
||||
3. Install bun, deno and python3 (+ any languages you want to use), have the bins at `/usr/bin/bun`,`/usr/bin/deno`, and
|
||||
`/usr/local/bin/python3` or set the corresponding environment variables.
|
||||
4. (optional) Install the [lld linker](https://lld.llvm.org/)
|
||||
5. Go to `frontend/`:
|
||||
1. `npm install`, `npm run generate-backend-client` then `REMOTE=http://localhost:8000 npm run dev`
|
||||
2. You might need to set some extra heap space for the node runtime
|
||||
`export NODE_OPTIONS="--max-old-space-size=4096"`
|
||||
3. In another shell `npm run build` otherwise the backend will not find the
|
||||
`frontend/build` folder and will not compile.
|
||||
4. In another shell `sudo caddy run --config Caddyfile`
|
||||
7. Go to `backend/`:
|
||||
`env DATABASE_URL=<DATABASE_URL_TO_YOUR_WINDMILL_DB> RUST_LOG=info cargo run`
|
||||
8. Et voilà, windmill should be available at `http://localhost/`
|
||||
3. Create an empty `frontend/build` folder using `mkdir frontend/build`
|
||||
6. Go to `backend/`:
|
||||
1. `env DATABASE_URL=<YOUR_DATABASE_URL> RUST_LOG=info cargo run`
|
||||
2. You can specify any feature flag you want to enable, for example `cargo run --features python` to enable the python executor.
|
||||
7. Et voilà, windmill should be available at `http://localhost:3000`
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT elem FROM (SELECT unnest($1::TEXT[]) AS elem)\n WHERE elem NOT IN (SELECT datname FROM pg_catalog.pg_database);",
|
||||
"query": "SELECT elem FROM (SELECT unnest($1::TEXT[]) AS elem) AS e\n WHERE elem NOT IN (SELECT datname FROM pg_catalog.pg_database);",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -18,5 +18,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "543859cf1c8d9e3bf2c2b23d21d096d01fe7a72d749229f21634b549c6b1241a"
|
||||
"hash": "298f8609319a2928257fd5be60bb37f292c786d2348efe11d19868e5dc8fba11"
|
||||
}
|
||||
Generated
+54
-54
@@ -496,7 +496,7 @@ dependencies = [
|
||||
"memchr",
|
||||
"num",
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3132,7 +3132,7 @@ dependencies = [
|
||||
"log",
|
||||
"recursive",
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5130,8 +5130,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
|
||||
dependencies = [
|
||||
"bit-set 0.5.3",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5141,8 +5141,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
|
||||
dependencies = [
|
||||
"bit-set 0.8.0",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5929,8 +5929,8 @@ dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6891,7 +6891,7 @@ dependencies = [
|
||||
"globset",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-automata 0.4.10",
|
||||
"same-file",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
@@ -7150,9 +7150,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"libc",
|
||||
@@ -10607,14 +10607,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-automata 0.4.10",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10628,20 +10628,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-lite"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
|
||||
checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
@@ -10657,9 +10657,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
@@ -13263,7 +13263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax 0.8.6",
|
||||
"utf8-ranges",
|
||||
]
|
||||
|
||||
@@ -14182,7 +14182,7 @@ checksum = "0203df02a3b6dd63575cc1d6e609edc2181c9a11867a271b25cfd2abff3ec5ca"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax 0.8.6",
|
||||
"tree-sitter-language",
|
||||
]
|
||||
|
||||
@@ -14596,9 +14596,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.6"
|
||||
version = "2.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "137a3c834eaf7139b73688502f3f1141a0337c5d8e4d9b536f9b8c796e26a7c4"
|
||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@@ -15129,7 +15129,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -15183,7 +15183,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15300,7 +15300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
@@ -15315,7 +15315,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -15328,7 +15328,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -15347,7 +15347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -15427,7 +15427,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -15442,7 +15442,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -15466,7 +15466,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -15478,7 +15478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -15487,7 +15487,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15499,7 +15499,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -15511,7 +15511,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -15523,7 +15523,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15535,7 +15535,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -15547,7 +15547,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -15558,7 +15558,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -15569,7 +15569,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -15581,7 +15581,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -15604,7 +15604,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15618,7 +15618,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -15635,7 +15635,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15649,7 +15649,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15667,7 +15667,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.16",
|
||||
@@ -15692,7 +15692,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -15702,7 +15702,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -15735,7 +15735,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
@@ -15745,7 +15745,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -33,7 +33,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.533.1
|
||||
version: 1.535.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -575,7 +575,7 @@ async fn databases_exist(
|
||||
Json(database_names): Json<Vec<String>>,
|
||||
) -> JsonResult<Vec<String>> {
|
||||
let result = sqlx::query_scalar!(
|
||||
r#"SELECT elem FROM (SELECT unnest($1::TEXT[]) AS elem)
|
||||
r#"SELECT elem FROM (SELECT unnest($1::TEXT[]) AS elem) AS e
|
||||
WHERE elem NOT IN (SELECT datname FROM pg_catalog.pg_database);"#,
|
||||
database_names.as_slice()
|
||||
)
|
||||
|
||||
@@ -5,7 +5,9 @@ use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::RawValue;
|
||||
use serde_json::{Map, Value};
|
||||
use tiberius::{AuthMethod, Client, ColumnData, Config, FromSqlOwned, Query, Row, SqlBrowser};
|
||||
use tiberius::{
|
||||
AuthMethod, Client, ColumnData, Config, EncryptionLevel, FromSqlOwned, Query, Row, SqlBrowser,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
use uuid::Uuid;
|
||||
@@ -39,6 +41,7 @@ struct MssqlDatabase {
|
||||
trust_cert: Option<bool>,
|
||||
#[serde(default, deserialize_with = "empty_as_none")]
|
||||
ca_cert: Option<String>,
|
||||
encrypt: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -146,6 +149,12 @@ pub async fn do_mssql(
|
||||
tracing::info!("MSSQL: using provided CA certificate for trust");
|
||||
}
|
||||
|
||||
config.encryption(if database.encrypt.unwrap_or(true) {
|
||||
EncryptionLevel::Required
|
||||
} else {
|
||||
EncryptionLevel::NotSupported
|
||||
});
|
||||
|
||||
let tcp = if use_instance_name {
|
||||
TcpStream::connect_named(&config).await.map_err(to_anyhow)? // named instance
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.533.1";
|
||||
export const VERSION = "v1.535.0";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.533.1";
|
||||
export const VERSION = "1.535.0";
|
||||
|
||||
const command = new Command()
|
||||
.name("wmill")
|
||||
|
||||
+14
-11
@@ -15,44 +15,45 @@ That's it! You are ready to go.
|
||||
> Using **direnv** is highly recommended, since it can load shell automatically based on your CWD. It also can give you hints.
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# enter a dev shell containing all necessary packages. `direnv allow` if direnv is installed.
|
||||
nix develop
|
||||
nix develop
|
||||
## or ignore if you have `direnv`
|
||||
|
||||
# Start db (if not started already)
|
||||
sudo docker compose up db -d
|
||||
./start-dev-db.sh
|
||||
|
||||
# run the frontend.
|
||||
wm
|
||||
|
||||
# In an other shell:
|
||||
#
|
||||
nix develop
|
||||
nix develop
|
||||
## or ignore if you have `direnv`
|
||||
|
||||
cd backend
|
||||
# You don't need to install anything extra. All dependencies are already in place!
|
||||
cargo run --features all_languages
|
||||
cargo run --features all_languages
|
||||
```
|
||||
|
||||
The default proxy is setup to use the local backend: <http://localhost:8000>.
|
||||
|
||||
### wm-* Commands
|
||||
### wm-\* Commands
|
||||
|
||||
Nix shell provides you with several helper commands prefixed with `wm-`
|
||||
|
||||
```bash
|
||||
# Start minio server (implements S3)
|
||||
wm-minio
|
||||
# Note: You will need access to EE private repo in order to compile, don't forget "enterprise" and "parquet" freatures as well.
|
||||
wm-minio
|
||||
# Note: You will need access to EE private repo in order to compile, don't forget "enterprise" and "parquet" freatures as well.
|
||||
|
||||
# Generate keys for local dev.
|
||||
wm-minio-keys
|
||||
wm-minio-keys
|
||||
# Minio data as well as generated keys are stored in `backend/.minio-data`
|
||||
```
|
||||
|
||||
You can read about all others commands individually in [flake.nix](../flake.nix).
|
||||
You can read about all others commands individually in [flake.nix](../flake.nix).
|
||||
|
||||
### dev.nu
|
||||
|
||||
@@ -106,7 +107,7 @@ REMOTE=http://localhost REMOTE_LSP=http://localhost npm run dev
|
||||
|
||||
Sometimes it is important to build docker image for your branch locally. It is crucial part of testing, since local environment may differ from the containerized one.
|
||||
|
||||
That's why we provide [docker/dev.nu](../docker/dev.nu). It is helper that can build images locally and execute them.
|
||||
That's why we provide [docker/dev.nu](../docker/dev.nu). It is helper that can build images locally and execute them.
|
||||
|
||||
it can build the image and run on local repository.
|
||||
|
||||
@@ -150,7 +151,7 @@ If you develop wasm parser for new language you can also pass `--wasm-pkg <langu
|
||||
In the root folder:
|
||||
|
||||
```bash
|
||||
docker-compose up db
|
||||
./start-dev-db.sh
|
||||
```
|
||||
|
||||
In the backend folder:
|
||||
@@ -218,6 +219,7 @@ nix flake update # update the lock file.
|
||||
```
|
||||
|
||||
Some cargo dependencies use fixed git revisions, which are also fixed in `flake.nix`:
|
||||
|
||||
```nix
|
||||
outputHashes = {
|
||||
"php-parser-rs-0.1.3" = "sha256-ZeI3KgUPmtjlRfq6eAYveqt8Ay35gwj6B9iOQRjQa9A=";
|
||||
@@ -226,6 +228,7 @@ outputHashes = {
|
||||
```
|
||||
|
||||
When updating a revision, replace the incorrect `sha256` with `pkgs.lib.fakeHash`:
|
||||
|
||||
```diff
|
||||
- "php-parser-rs-0.1.3" = "sha256-ZeI3KgUPmtjlRfq6eAYveqt8Ay35gwj6B9iOQRjQa9A=";
|
||||
+ "php-parser-rs-0.1.3" = pkgs.lib.fakeHash;
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.533.1",
|
||||
"version": "1.535.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.533.1",
|
||||
"version": "1.535.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.533.1",
|
||||
"version": "1.535.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
import type { FlowPropPickerConfig, PropPickerContext } from './prop_picker'
|
||||
import type { PickableProperties } from './flows/previousResults'
|
||||
import { Triggers } from './triggers/triggers.svelte'
|
||||
import { TestSteps } from './flows/testSteps.svelte'
|
||||
import { StepsInputArgs } from './flows/stepsInputArgs.svelte'
|
||||
import { ModulesTestStates } from './modulesTest.svelte'
|
||||
import type { GraphModuleState } from './graph'
|
||||
|
||||
@@ -457,7 +457,7 @@
|
||||
const scriptEditorDrawer = writable(undefined)
|
||||
const moving = writable<{ id: string } | undefined>(undefined)
|
||||
const history = initHistory(flowStore.val)
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
const modulesTestStates = new ModulesTestStates((moduleId) => {
|
||||
@@ -481,7 +481,7 @@
|
||||
pathStore: writable(''),
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft: () => {},
|
||||
initialPathStore: writable(''),
|
||||
fakeInitialPath: '',
|
||||
@@ -806,7 +806,7 @@
|
||||
noEditor
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
} else {
|
||||
previewArgsStore.val = ev.detail.args ?? {}
|
||||
@@ -818,6 +818,7 @@
|
||||
isOwner={flowPreviewContent?.getIsOwner()}
|
||||
{suspendStatus}
|
||||
onOpenDetails={flowPreviewButtons?.openPreview}
|
||||
previewOpen={flowPreviewButtons?.getPreviewOpen()}
|
||||
/>
|
||||
{/key}
|
||||
</Pane>
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
<Module.default
|
||||
open={true}
|
||||
automaticLayout
|
||||
class="h-full"
|
||||
className="h-full"
|
||||
defaultLang={lang}
|
||||
defaultModifiedLang={data.current.lang}
|
||||
defaultOriginal={content}
|
||||
@@ -227,7 +227,7 @@
|
||||
<Module.default
|
||||
open={true}
|
||||
automaticLayout
|
||||
class="h-full"
|
||||
className="h-full"
|
||||
defaultLang="yaml"
|
||||
defaultOriginal={metadata}
|
||||
defaultModified={data.current.metadata}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { BROWSER } from 'esm-env'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
import '@codingame/monaco-vscode-standalone-languages'
|
||||
import '@codingame/monaco-vscode-standalone-json-language-features'
|
||||
@@ -10,24 +10,47 @@
|
||||
import { initializeVscode } from './vscode'
|
||||
import EditorTheme from './EditorTheme.svelte'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { ButtonType } from './common'
|
||||
|
||||
const SIDE_BY_SIDE_MIN_WIDTH = 700
|
||||
|
||||
export let automaticLayout = true
|
||||
export let fixedOverflowWidgets = true
|
||||
export let defaultLang: string | undefined = undefined
|
||||
export let defaultModifiedLang: string | undefined = undefined
|
||||
export let defaultOriginal: string | undefined = undefined
|
||||
export let defaultModified: string | undefined = undefined
|
||||
export let readOnly = false
|
||||
export let showButtons = false
|
||||
export let showHistoryButton: boolean = true
|
||||
export interface ButtonProp {
|
||||
text: string
|
||||
color?: ButtonType.Color
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
let diffEditor: meditor.IStandaloneDiffEditor | undefined
|
||||
let diffDivEl: HTMLDivElement | null = null
|
||||
let editorWidth: number = SIDE_BY_SIDE_MIN_WIDTH
|
||||
interface Props {
|
||||
open?: boolean
|
||||
className?: string
|
||||
automaticLayout?: boolean
|
||||
fixedOverflowWidgets?: boolean
|
||||
defaultLang?: string
|
||||
defaultModifiedLang?: string
|
||||
defaultOriginal?: string
|
||||
defaultModified?: string
|
||||
readOnly?: boolean
|
||||
buttons?: ButtonProp[]
|
||||
}
|
||||
|
||||
let {
|
||||
open = false,
|
||||
className = '',
|
||||
automaticLayout = true,
|
||||
fixedOverflowWidgets = true,
|
||||
defaultLang,
|
||||
defaultModifiedLang,
|
||||
defaultOriginal = undefined,
|
||||
defaultModified = undefined,
|
||||
readOnly = false,
|
||||
buttons = []
|
||||
}: Props = $props()
|
||||
|
||||
let diffEditor: meditor.IStandaloneDiffEditor | undefined = $state(undefined)
|
||||
let diffDivEl: HTMLDivElement | null = $state(null)
|
||||
let editorWidth: number = $state(SIDE_BY_SIDE_MIN_WIDTH)
|
||||
|
||||
export let open = false
|
||||
async function loadDiffEditor() {
|
||||
await initializeVscode()
|
||||
|
||||
@@ -105,9 +128,15 @@
|
||||
diffEditor?.updateOptions({ renderSideBySide: editorWidth >= SIDE_BY_SIDE_MIN_WIDTH })
|
||||
}
|
||||
|
||||
$: onWidthChange(editorWidth)
|
||||
$effect(() => {
|
||||
if (open && diffDivEl) {
|
||||
loadDiffEditor()
|
||||
}
|
||||
})
|
||||
|
||||
$: open && diffDivEl && loadDiffEditor()
|
||||
$effect(() => {
|
||||
onWidthChange(editorWidth)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
if (BROWSER) {
|
||||
@@ -116,32 +145,24 @@
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
hideDiffMode: void
|
||||
seeHistory: void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<EditorTheme />
|
||||
<div
|
||||
bind:this={diffDivEl}
|
||||
class="{$$props.class} editor nonmain-editor"
|
||||
class={twMerge('editor nonmain-editor', className)}
|
||||
bind:clientWidth={editorWidth}
|
||||
></div>
|
||||
{#if showButtons}
|
||||
{#if buttons.length > 0}
|
||||
<div
|
||||
class="absolute flex flex-row gap-2 bottom-10 left-1/2 z-10 -translate-x-1/2 rounded-md p-1 w-full justify-center"
|
||||
>
|
||||
{#if showHistoryButton}
|
||||
<Button on:click={() => dispatch('seeHistory')} variant="contained" size="sm"
|
||||
>See changes history</Button
|
||||
{#each buttons as button}
|
||||
<Button on:click={button.onClick} variant="contained" size="sm" color={button.color}
|
||||
>{button.text}</Button
|
||||
>
|
||||
{/if}
|
||||
<Button on:click={() => dispatch('hideDiffMode')} variant="contained" size="sm" color="red"
|
||||
>Quit diff mode</Button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
} from './triggers/utils'
|
||||
import DraftTriggersConfirmationModal from './common/confirmationModal/DraftTriggersConfirmationModal.svelte'
|
||||
import { Triggers } from './triggers/triggers.svelte'
|
||||
import { TestSteps } from './flows/testSteps.svelte'
|
||||
import { StepsInputArgs } from './flows/stepsInputArgs.svelte'
|
||||
import { aiChatManager } from './copilot/chat/AIChatManager.svelte'
|
||||
import type { GraphModuleState } from './graph'
|
||||
import {
|
||||
@@ -571,7 +571,7 @@
|
||||
payloadData: undefined
|
||||
})
|
||||
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
|
||||
function select(selectedId: string) {
|
||||
selectedIdStore.set(selectedId)
|
||||
@@ -592,7 +592,7 @@
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
pathStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft,
|
||||
initialPathStore,
|
||||
fakeInitialPath,
|
||||
@@ -1129,6 +1129,8 @@
|
||||
bind:this={flowPreviewButtons}
|
||||
{loading}
|
||||
onRunPreview={() => {
|
||||
// Reset manually edited args inputs when running a preview
|
||||
stepsInputArgs.resetManuallyEditedArgs()
|
||||
modulesTestStates.hideJobsInGraph()
|
||||
localModuleStates = {}
|
||||
showJobStatus = true
|
||||
@@ -1170,7 +1172,7 @@
|
||||
{newFlow}
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
}
|
||||
}}
|
||||
@@ -1218,6 +1220,7 @@
|
||||
delete modulesTestStates.states[id]
|
||||
}}
|
||||
{flowHasChanged}
|
||||
previewOpen={flowPreviewButtons?.getPreviewOpen()}
|
||||
/>
|
||||
{:else}
|
||||
<CenteredPage>Loading...</CenteredPage>
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
jobId = await runFlowPreview(args, newFlow, $pathStore, restartedFrom)
|
||||
isRunning = true
|
||||
if (inputSelected) {
|
||||
savedArgs = previewArgs.val
|
||||
savedArgs = $state.snapshot(previewArgs.val)
|
||||
inputSelected = undefined
|
||||
}
|
||||
onRunPreview?.()
|
||||
@@ -166,7 +166,7 @@
|
||||
if (preventEscape) {
|
||||
selectInput(undefined)
|
||||
event.preventDefault()
|
||||
event.stopPropagation
|
||||
event.stopPropagation()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -506,7 +506,7 @@
|
||||
schema={flowStore.val.schema}
|
||||
bind:args={previewArgs.val}
|
||||
on:change={() => {
|
||||
savedArgs = previewArgs.val
|
||||
savedArgs = $state.snapshot(previewArgs.val)
|
||||
}}
|
||||
bind:isValid
|
||||
helperScript={flowStore.val.schema?.['x-windmill-dyn-select-code'] &&
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
noEditor?: boolean
|
||||
scriptProgress?: any
|
||||
focusArg?: string
|
||||
onJobDone?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -28,7 +29,8 @@
|
||||
testIsLoading = $bindable(false),
|
||||
noEditor = false,
|
||||
scriptProgress = $bindable(undefined),
|
||||
focusArg = undefined
|
||||
focusArg = undefined,
|
||||
onJobDone
|
||||
}: Props = $props()
|
||||
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
@@ -46,6 +48,7 @@
|
||||
bind:testIsLoading
|
||||
bind:scriptProgress
|
||||
bind:this={moduleTest}
|
||||
{onJobDone}
|
||||
/>
|
||||
|
||||
<div class="p-4">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { Schema } from '$lib/common'
|
||||
|
||||
import { allTrue, sendUserToast } from '$lib/utils'
|
||||
import { allTrue } from '$lib/utils'
|
||||
import { RefreshCw } from 'lucide-svelte'
|
||||
import ArgInput from './ArgInput.svelte'
|
||||
import { Button } from './common'
|
||||
import { getContext, onMount, untrack } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import { evalValue } from './flows/utils'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
@@ -32,7 +32,7 @@
|
||||
focusArg = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const { testSteps, flowStateStore, flowStore, previewArgs } =
|
||||
const { stepsInputArgs, flowStateStore, flowStore, previewArgs } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let inputCheck: { [id: string]: boolean } = $state({})
|
||||
@@ -45,12 +45,12 @@
|
||||
let lkeys = Object.keys(schema?.properties ?? {})
|
||||
if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
|
||||
keys = lkeys
|
||||
untrack(() => testSteps?.removeExtraKey(mod.id, keys))
|
||||
untrack(() => stepsInputArgs?.removeExtraKey(mod.id, keys))
|
||||
}
|
||||
})
|
||||
|
||||
function plugIt(argName: string) {
|
||||
testSteps?.setEvaluatedStepArg(
|
||||
stepsInputArgs?.setEvaluatedStepArg(
|
||||
mod.id,
|
||||
argName,
|
||||
$state.snapshot(evalValue(argName, mod, pickableProperties, true))
|
||||
@@ -98,70 +98,78 @@
|
||||
|
||||
loadResourceTypes()
|
||||
|
||||
onMount(() => {
|
||||
if (!testSteps) {
|
||||
sendUserToast('testSteps module not initialized. Preview will not work.', true)
|
||||
let initialized = $state(false)
|
||||
|
||||
$effect.pre(() => {
|
||||
if (!initialized) {
|
||||
if (stepsInputArgs) {
|
||||
stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="w-full pt-2" data-popover>
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if Object.keys(schema.properties ?? {}).includes(argName)}
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex gap-2',
|
||||
animateArg === argName && 'animate-pulse ring-2 ring-offset-2 ring-blue-500 rounded'
|
||||
)}
|
||||
data-arg={argName}
|
||||
>
|
||||
{#if schema?.properties?.[argName]}
|
||||
<ArgInput
|
||||
{resourceTypes}
|
||||
minW={false}
|
||||
autofocus={autofocus && !focusArg && i == 0}
|
||||
label={argName}
|
||||
description={schema.properties[argName].description}
|
||||
bind:value={
|
||||
() => testSteps?.getStepInputArgs(mod.id, argName),
|
||||
(v) => testSteps?.setStepInputArgs(mod.id, argName, v)
|
||||
}
|
||||
type={schema.properties[argName].type}
|
||||
oneOf={schema.properties[argName].oneOf}
|
||||
required={schema?.required?.includes(argName)}
|
||||
pattern={schema.properties[argName].pattern}
|
||||
bind:editor={editor[argName]}
|
||||
bind:valid={inputCheck[argName]}
|
||||
defaultValue={schema.properties[argName].default}
|
||||
enum_={schema.properties[argName].enum}
|
||||
format={schema.properties[argName].format}
|
||||
contentEncoding={schema.properties[argName].contentEncoding}
|
||||
properties={schema.properties[argName].properties}
|
||||
nestedRequired={schema.properties[argName].required}
|
||||
itemsType={schema.properties[argName].items}
|
||||
extra={schema.properties[argName]}
|
||||
nullable={schema.properties[argName].nullable}
|
||||
title={schema.properties[argName].title}
|
||||
placeholder={schema.properties[argName].placeholder}
|
||||
/>
|
||||
{/if}
|
||||
{#if testSteps?.isArgManuallySet(mod.id, argName)}
|
||||
<div class="pt-6 mt-0.5">
|
||||
<Button
|
||||
on:click={() => {
|
||||
plugIt(argName)
|
||||
}}
|
||||
size="sm"
|
||||
variant="border"
|
||||
color="light"
|
||||
title="Re-evaluate input step"><RefreshCw size={14} /></Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if initialized}
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, i (argName)}
|
||||
{#if Object.keys(schema.properties ?? {}).includes(argName)}
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex gap-2',
|
||||
animateArg === argName && 'animate-pulse ring-2 ring-offset-2 ring-blue-500 rounded'
|
||||
)}
|
||||
data-arg={argName}
|
||||
>
|
||||
{#if schema?.properties?.[argName]}
|
||||
<ArgInput
|
||||
{resourceTypes}
|
||||
minW={false}
|
||||
autofocus={autofocus && !focusArg && i == 0}
|
||||
label={argName}
|
||||
description={schema.properties[argName].description}
|
||||
bind:value={
|
||||
() => stepsInputArgs?.getStepInputArgs(mod.id, argName),
|
||||
(v) => stepsInputArgs?.setStepInputArgs(mod.id, argName, v)
|
||||
}
|
||||
type={schema.properties[argName].type}
|
||||
oneOf={schema.properties[argName].oneOf}
|
||||
required={schema?.required?.includes(argName)}
|
||||
pattern={schema.properties[argName].pattern}
|
||||
bind:editor={editor[argName]}
|
||||
bind:valid={inputCheck[argName]}
|
||||
defaultValue={schema.properties[argName].default}
|
||||
enum_={schema.properties[argName].enum}
|
||||
format={schema.properties[argName].format}
|
||||
contentEncoding={schema.properties[argName].contentEncoding}
|
||||
properties={schema.properties[argName].properties}
|
||||
nestedRequired={schema.properties[argName].required}
|
||||
itemsType={schema.properties[argName].items}
|
||||
extra={schema.properties[argName]}
|
||||
nullable={schema.properties[argName].nullable}
|
||||
title={schema.properties[argName].title}
|
||||
placeholder={schema.properties[argName].placeholder}
|
||||
/>
|
||||
{/if}
|
||||
{#if stepsInputArgs?.isArgManuallySet(mod.id, argName)}
|
||||
<div class="pt-6 mt-0.5">
|
||||
<Button
|
||||
on:click={() => {
|
||||
plugIt(argName)
|
||||
}}
|
||||
size="sm"
|
||||
variant="border"
|
||||
color="light"
|
||||
title="Re-evaluate input step"><RefreshCw size={14} /></Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-center text-sm text-tertiary"> Loading test step arguments... </div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { type Script, type Job, type FlowModule } from '$lib/gen'
|
||||
import OutputPickerInner from '$lib/components/flows/propPicker/OutputPickerInner.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import type { FlowEditorContext, OutputViewerJob } from './flows/types'
|
||||
import { getContext } from 'svelte'
|
||||
import { getStringError } from './copilot/chat/utils'
|
||||
import AiAgentLogViewer from './AIAgentLogViewer.svelte'
|
||||
@@ -17,9 +17,8 @@
|
||||
editor: Editor | undefined
|
||||
diffEditor: DiffEditor | undefined
|
||||
loopStatus?: { type: 'inside' | 'self'; flow: 'forloopflow' | 'whileloopflow' } | undefined
|
||||
lastJob?: Job | undefined
|
||||
testJob?: Job & { result_stream?: string }
|
||||
scriptProgress?: number | undefined
|
||||
testJob?: Job | undefined
|
||||
mod: FlowModule
|
||||
testIsLoading?: boolean
|
||||
disableMock?: boolean
|
||||
@@ -34,7 +33,6 @@
|
||||
editor,
|
||||
diffEditor,
|
||||
loopStatus = undefined,
|
||||
lastJob = undefined,
|
||||
scriptProgress = $bindable(undefined),
|
||||
testJob = undefined,
|
||||
mod,
|
||||
@@ -46,21 +44,20 @@
|
||||
tagLabel = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const { testSteps } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { stepsInputArgs } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let selectedJob: Job | undefined = $state(undefined)
|
||||
let preview: 'mock' | 'job' | undefined = $state(undefined)
|
||||
let jobProgressReset: () => void = $state(() => {})
|
||||
|
||||
$effect(() => {
|
||||
if (preview != undefined && testJob) {
|
||||
preview = undefined
|
||||
}
|
||||
})
|
||||
|
||||
let forceJson = $state(false)
|
||||
let outputPickerInner: OutputPickerInner | undefined = $state(undefined)
|
||||
export function getOutputPickerInner() {
|
||||
return outputPickerInner
|
||||
}
|
||||
|
||||
const selectedJob: OutputViewerJob = $derived.by(
|
||||
() => outputPickerInner?.getSelectedJob?.() ?? undefined
|
||||
)
|
||||
const logJob = $derived(testJob ?? selectedJob)
|
||||
const preview = $derived.by(() => outputPickerInner?.getPreview?.())
|
||||
</script>
|
||||
|
||||
<Splitpanes horizontal>
|
||||
@@ -75,7 +72,6 @@
|
||||
{/if}
|
||||
|
||||
<OutputPickerInner
|
||||
{lastJob}
|
||||
{testJob}
|
||||
fullResult
|
||||
moduleId={mod.id}
|
||||
@@ -83,24 +79,22 @@
|
||||
getLogs
|
||||
{onUpdateMock}
|
||||
mock={mod.mock}
|
||||
bind:forceJson
|
||||
bind:selectedJob
|
||||
isLoading={testIsLoading || loadingJob}
|
||||
bind:preview
|
||||
path={`path` in mod.value ? mod.value.path : ''}
|
||||
{loopStatus}
|
||||
{disableMock}
|
||||
{disableHistory}
|
||||
bind:this={outputPickerInner}
|
||||
>
|
||||
{#snippet copilot_fix()}
|
||||
{#if lang && editor && diffEditor && testSteps.getStepArgs(mod.id) && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)}
|
||||
{#if lang && editor && diffEditor && stepsInputArgs.getStepArgs(mod.id) && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)}
|
||||
<ScriptFix {lang} />
|
||||
{/if}
|
||||
{/snippet}
|
||||
</OutputPickerInner>
|
||||
</Pane>
|
||||
<Pane size={35} minSize={10}>
|
||||
{#if (mod.mock?.enabled && preview != 'job') || preview == 'mock'}
|
||||
{#if (mod.mock?.enabled && preview !== 'job' && testJob?.type !== 'QueuedJob') || preview === 'mock'}
|
||||
<LogViewer
|
||||
small
|
||||
content={undefined}
|
||||
@@ -124,9 +118,9 @@
|
||||
jobId={logJob?.id}
|
||||
duration={logJob?.['duration_ms']}
|
||||
mem={logJob?.['mem_peak']}
|
||||
content={logJob?.logs}
|
||||
content={logJob?.['logs']}
|
||||
isLoading={(testIsLoading && logJob?.['running'] == false) || loadingJob}
|
||||
tag={logJob?.tag}
|
||||
tag={logJob?.['tag']}
|
||||
{tagLabel}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
testIsLoading?: boolean
|
||||
noEditor?: boolean
|
||||
scriptProgress?: any
|
||||
onJobDone?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -21,10 +22,11 @@
|
||||
testJob = $bindable(undefined),
|
||||
testIsLoading = $bindable(false),
|
||||
noEditor = false,
|
||||
scriptProgress = $bindable(undefined)
|
||||
scriptProgress = $bindable(undefined),
|
||||
onJobDone
|
||||
}: Props = $props()
|
||||
|
||||
const { flowStore, flowStateStore, pathStore, testSteps, previewArgs, modulesTestStates } =
|
||||
const { flowStore, flowStateStore, pathStore, stepsInputArgs, previewArgs, modulesTestStates } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let jobLoader: JobLoader | undefined = $state(undefined)
|
||||
@@ -32,12 +34,12 @@
|
||||
let stepHistoryLoader = getStepHistoryLoaderContext()
|
||||
|
||||
export function runTestWithStepArgs() {
|
||||
runTest(testSteps.getStepArgs(mod.id))
|
||||
runTest(stepsInputArgs.getStepArgs(mod.id))
|
||||
}
|
||||
|
||||
export function loadArgsAndRunTest() {
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
runTest(testSteps.getStepArgs(mod.id))
|
||||
stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
runTest(stepsInputArgs.getStepArgs(mod.id))
|
||||
}
|
||||
|
||||
export async function runTest(args: any) {
|
||||
@@ -138,6 +140,7 @@
|
||||
if (modulesTestStates.states[mod.id]) {
|
||||
modulesTestStates.states[mod.id].testJob = testJob
|
||||
}
|
||||
onJobDone?.()
|
||||
}
|
||||
|
||||
export function cancelJob() {
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
let {
|
||||
runnable = $bindable(),
|
||||
runnable,
|
||||
runAction,
|
||||
buttonText = 'Run',
|
||||
schedulable = true,
|
||||
@@ -309,7 +309,7 @@
|
||||
bind:scheduledForStr
|
||||
bind:invisible_to_owner
|
||||
bind:overrideTag
|
||||
bind:runnable
|
||||
{runnable}
|
||||
/>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
|
||||
@@ -70,7 +70,19 @@
|
||||
{#if !$userStore?.operator}
|
||||
{#if $workerTags && $workerTags?.length > 0}
|
||||
<div class="w-full">
|
||||
<select placeholder="Worker group" bind:value={overrideTag}>
|
||||
<select
|
||||
placeholder="Worker group"
|
||||
bind:value={
|
||||
() => overrideTag ?? '',
|
||||
(v) => {
|
||||
if (v == '') {
|
||||
overrideTag = undefined
|
||||
} else {
|
||||
overrideTag = v
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
{#if overrideTag}
|
||||
<option value="">reset to default</option>
|
||||
{:else}
|
||||
|
||||
@@ -1128,7 +1128,7 @@
|
||||
>
|
||||
<LanguageIcon {lang} />
|
||||
<span class="ml-2 py-2 truncate">{label}</span>
|
||||
{#if lang === 'nu' || lang === 'ruby'}
|
||||
{#if lang === 'ruby'}
|
||||
<span class="text-tertiary !text-xs"> BETA </span>
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
@@ -626,16 +626,28 @@
|
||||
{args}
|
||||
/>
|
||||
<DiffEditor
|
||||
class="h-full"
|
||||
className="h-full"
|
||||
bind:this={diffEditor}
|
||||
automaticLayout
|
||||
defaultLang={scriptLangToEditorLang(lang)}
|
||||
{fixedOverflowWidgets}
|
||||
showButtons={diffMode}
|
||||
on:hideDiffMode={hideDiffMode}
|
||||
on:seeHistory={() => {
|
||||
showHistoryDrawer = true
|
||||
}}
|
||||
buttons={diffMode
|
||||
? [
|
||||
{
|
||||
text: 'See changes history',
|
||||
onClick: () => {
|
||||
showHistoryDrawer = true
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Quit diff mode',
|
||||
onClick: () => {
|
||||
hideDiffMode()
|
||||
},
|
||||
color: 'red'
|
||||
}
|
||||
]
|
||||
: []}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -423,7 +423,7 @@
|
||||
<DiffEditor
|
||||
open={false}
|
||||
bind:this={diffEditor}
|
||||
class="h-full"
|
||||
className="h-full"
|
||||
automaticLayout
|
||||
fixedOverflowWidgets
|
||||
defaultLang={scriptLangToEditorLang(inlineScript?.language)}
|
||||
|
||||
@@ -2,7 +2,16 @@ export const BUTTON_COLORS = ['blue', 'red', 'dark', 'light', 'green', 'gray', '
|
||||
|
||||
export namespace ButtonType {
|
||||
export type Size = 'xs3' | 'xs2' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
export type Color = string
|
||||
export type Color =
|
||||
| 'blue'
|
||||
| 'red'
|
||||
| 'dark'
|
||||
| 'light'
|
||||
| 'green'
|
||||
| 'gray'
|
||||
| 'none'
|
||||
| 'marine'
|
||||
| 'nord'
|
||||
export type Variant = 'contained' | 'border' | 'divider'
|
||||
export type Target = '_self' | '_blank'
|
||||
export type Element = HTMLButtonElement | HTMLAnchorElement
|
||||
|
||||
@@ -40,13 +40,12 @@ import { getStringError } from './utils'
|
||||
import type { FlowModuleState, FlowState } from '$lib/components/flows/flowState'
|
||||
import type { CurrentEditor, ExtendedOpenFlow } from '$lib/components/flows/types'
|
||||
import { untrack } from 'svelte'
|
||||
import { copilotSessionModel, type DBSchemas } from '$lib/stores'
|
||||
import { getCurrentModel, type DBSchemas } from '$lib/stores'
|
||||
import { askTools, prepareAskSystemMessage, prepareAskUserMessage } from './ask/core'
|
||||
import { chatState, DEFAULT_SIZE, triggerablesByAi } from './sharedChatState.svelte'
|
||||
import type { ContextElement } from './context'
|
||||
import type { Selection } from 'monaco-editor'
|
||||
import type AIChatInput from './AIChatInput.svelte'
|
||||
import { get } from 'svelte/store'
|
||||
import { prepareApiSystemMessage, prepareApiUserMessage } from './api/core'
|
||||
|
||||
// If the estimated token usage is greater than the model context window - the threshold, we delete the oldest message
|
||||
@@ -124,7 +123,8 @@ class AIChatManager {
|
||||
}
|
||||
return acc
|
||||
}, 0)
|
||||
const modelContextWindow = getModelContextWindow(get(copilotSessionModel)?.model ?? '')
|
||||
const model = getCurrentModel()
|
||||
const modelContextWindow = getModelContextWindow(model.model)
|
||||
return (
|
||||
estimatedTokens >
|
||||
modelContextWindow -
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { dfs } from '$lib/components/flows/previousResults'
|
||||
import { dfs as dfsApply } from '$lib/components/flows/dfs'
|
||||
import { getSubModules } from '$lib/components/flows/flowExplorer'
|
||||
import type { FlowModule, OpenFlow } from '$lib/gen'
|
||||
import type { FlowModule, OpenFlow, RawScript } from '$lib/gen'
|
||||
import { getIndexInNestedModules, getNestedModules } from './utils'
|
||||
import type { AIModuleAction, FlowAIChatHelpers } from './core'
|
||||
import {
|
||||
@@ -19,7 +19,7 @@
|
||||
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
|
||||
|
||||
let {
|
||||
flowModuleSchemaMap,
|
||||
flowModuleSchemaMap
|
||||
}: {
|
||||
flowModuleSchemaMap: FlowModuleSchemaMap | undefined
|
||||
} = $props()
|
||||
@@ -174,6 +174,17 @@
|
||||
if (!newModule) {
|
||||
throw new Error('Module not found')
|
||||
}
|
||||
|
||||
// Apply the old code to the editor and hide diff editor if the reverted module is a rawscript
|
||||
if (
|
||||
newModule.value.type === 'rawscript' &&
|
||||
$currentEditor?.type === 'script' &&
|
||||
$currentEditor.stepId === id
|
||||
) {
|
||||
$currentEditor.editor.setCode((oldModule.value as RawScript).content)
|
||||
$currentEditor.hideDiffMode()
|
||||
}
|
||||
|
||||
newModule.value = oldModule.value
|
||||
}
|
||||
|
||||
@@ -525,6 +536,47 @@
|
||||
return cleanup
|
||||
})
|
||||
|
||||
// Automatically show diff mode when selecting a rawscript module with pending changes
|
||||
$effect(() => {
|
||||
if (
|
||||
$currentEditor?.type === 'script' &&
|
||||
$selectedId &&
|
||||
affectedModules[$selectedId] &&
|
||||
lastSnapshot
|
||||
) {
|
||||
const moduleLastSnapshot = getModule($selectedId, lastSnapshot)
|
||||
const currentModule = getModule($selectedId)
|
||||
|
||||
if (
|
||||
moduleLastSnapshot &&
|
||||
currentModule &&
|
||||
currentModule.value.type === 'rawscript' &&
|
||||
moduleLastSnapshot.value.type === 'rawscript'
|
||||
) {
|
||||
// Show diff mode automatically
|
||||
$currentEditor.setDiffOriginal?.(moduleLastSnapshot.value.content ?? '')
|
||||
$currentEditor.showDiffMode()
|
||||
$currentEditor.setDiffButtons?.([
|
||||
{
|
||||
text: 'Accept Changes',
|
||||
color: 'green',
|
||||
onClick: () => {
|
||||
flowHelpers.acceptModuleAction($selectedId)
|
||||
$currentEditor?.hideDiffMode()
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Reject Changes',
|
||||
onClick: () => {
|
||||
flowHelpers.revertModuleAction($selectedId)
|
||||
$currentEditor?.hideDiffMode()
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let diffDrawer: DiffDrawer | undefined = $state(undefined)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
ChatCompletionTool,
|
||||
ChatCompletionUserMessageParam
|
||||
} from 'openai/resources/index.mjs'
|
||||
import { copilotSessionModel, type DBSchema, dbSchemas } from '$lib/stores'
|
||||
import { type DBSchema, dbSchemas, getCurrentModel } from '$lib/stores'
|
||||
import { getDbSchemas } from '$lib/components/apps/components/display/dbtable/utils'
|
||||
import type { ContextElement } from '../context'
|
||||
import { PYTHON_PREPROCESSOR_MODULE_CODE, TS_PREPROCESSOR_MODULE_CODE } from '$lib/script_helpers'
|
||||
@@ -655,7 +655,8 @@ export async function searchExternalIntegrationResources(args: { query: string }
|
||||
(r: PackageSearchQuery) => r.searchScore >= SCORE_THRESHOLD
|
||||
)
|
||||
|
||||
const modelContextWindow = getModelContextWindow(get(copilotSessionModel)?.model ?? '')
|
||||
const model = getCurrentModel()
|
||||
const modelContextWindow = getModelContextWindow(model.model)
|
||||
const results: PackageSearchResult[] = await Promise.all(
|
||||
filtered.map(async (r: PackageSearchQuery) => {
|
||||
let documentation = ''
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
} from 'openai/resources/chat/completions.mjs'
|
||||
import { get } from 'svelte/store'
|
||||
import type { CodePieceElement, ContextElement, FlowModuleCodePieceElement } from './context'
|
||||
import { copilotSessionModel, workspaceStore } from '$lib/stores'
|
||||
import { workspaceStore, getCurrentModel } from '$lib/stores'
|
||||
import type { ExtendedOpenFlow } from '$lib/components/flows/types'
|
||||
import type { FunctionParameters } from 'openai/resources/shared.mjs'
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema'
|
||||
@@ -455,8 +455,8 @@ export async function buildSchemaForTool(
|
||||
|
||||
toolDef.function.parameters = { ...schema, additionalProperties: false }
|
||||
// OPEN AI models don't support strict mode well with schema with complex properties, so we disable it
|
||||
const model = get(copilotSessionModel)?.provider
|
||||
if (model === 'openai' || model === 'azure_openai') {
|
||||
const model = getCurrentModel()
|
||||
if (model.provider === 'openai' || model.provider === 'azure_openai') {
|
||||
toolDef.function.strict = false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { AIProvider, AIProviderModel } from '$lib/gen'
|
||||
import {
|
||||
copilotInfo,
|
||||
copilotSessionModel,
|
||||
getCurrentModel,
|
||||
workspaceStore,
|
||||
type DBSchema,
|
||||
type GraphqlSchema,
|
||||
@@ -23,7 +22,16 @@ import { z } from 'zod'
|
||||
|
||||
export const SUPPORTED_LANGUAGES = new Set(Object.keys(GEN_CONFIG.prompts))
|
||||
|
||||
const OPENAI_MODELS = ['gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4o', 'gpt-4o-mini', 'o4-mini', 'o3', 'o3-mini']
|
||||
const OPENAI_MODELS = [
|
||||
'gpt-5',
|
||||
'gpt-5-mini',
|
||||
'gpt-5-nano',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
'o4-mini',
|
||||
'o3',
|
||||
'o3-mini'
|
||||
]
|
||||
|
||||
// need at least one model for each provider except customai
|
||||
export const AI_DEFAULT_MODELS: Record<AIProvider, string[]> = {
|
||||
@@ -468,18 +476,9 @@ function getProviderAndCompletionConfig<K extends boolean>({
|
||||
? ChatCompletionCreateParamsStreaming
|
||||
: ChatCompletionCreateParamsNonStreaming
|
||||
} {
|
||||
let info = get(copilotInfo)
|
||||
const modelProvider =
|
||||
forceModelProvider ?? get(copilotSessionModel) ?? info.defaultModel ?? info.aiModels[0]
|
||||
|
||||
if (!modelProvider) {
|
||||
throw new Error('No model selected')
|
||||
}
|
||||
|
||||
const modelProvider = forceModelProvider ?? getCurrentModel()
|
||||
const providerConfig = PROVIDER_COMPLETION_CONFIG_MAP[modelProvider.provider]
|
||||
|
||||
const processedMessages = prepareMessages(modelProvider.provider, messages)
|
||||
|
||||
return {
|
||||
provider: modelProvider.provider,
|
||||
config: {
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onDelete?: (id: string) => void
|
||||
flowHasChanged?: boolean
|
||||
previewOpen: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -91,7 +92,8 @@
|
||||
job,
|
||||
suspendStatus,
|
||||
onDelete,
|
||||
flowHasChanged
|
||||
flowHasChanged,
|
||||
previewOpen
|
||||
}: Props = $props()
|
||||
|
||||
let flowModuleSchemaMap: FlowModuleSchemaMap | undefined = $state()
|
||||
@@ -206,6 +208,7 @@
|
||||
{isOwner}
|
||||
{suspendStatus}
|
||||
onOpenDetails={onOpenPreview}
|
||||
{previewOpen}
|
||||
/>
|
||||
{/if}
|
||||
</Pane>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
isOwner?: boolean
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onOpenDetails?: () => void
|
||||
previewOpen?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -49,7 +50,8 @@
|
||||
job,
|
||||
isOwner,
|
||||
suspendStatus,
|
||||
onOpenDetails
|
||||
onOpenDetails,
|
||||
previewOpen = false
|
||||
}: Props = $props()
|
||||
|
||||
const {
|
||||
@@ -95,6 +97,7 @@
|
||||
}}
|
||||
on:applyArgs
|
||||
{onTestFlow}
|
||||
{previewOpen}
|
||||
/>
|
||||
{:else if $selectedId === 'Result'}
|
||||
<FlowResult {noEditor} {job} {isOwner} {suspendStatus} {onOpenDetails} />
|
||||
|
||||
@@ -48,9 +48,10 @@
|
||||
noEditor: boolean
|
||||
disabled: boolean
|
||||
onTestFlow?: () => void
|
||||
previewOpen: boolean
|
||||
}
|
||||
|
||||
let { noEditor, disabled, onTestFlow }: Props = $props()
|
||||
let { noEditor, disabled, onTestFlow, previewOpen }: Props = $props()
|
||||
const {
|
||||
flowStore,
|
||||
previewArgs,
|
||||
@@ -63,7 +64,6 @@
|
||||
let addPropertyV2: AddPropertyV2 | undefined = $state(undefined)
|
||||
let previewSchema: Record<string, any> | undefined = $state(undefined)
|
||||
let payloadData: Record<string, any> | undefined = undefined
|
||||
let previewArguments: Record<string, any> | undefined = $state(previewArgs.val)
|
||||
let dropdownItems: Array<{
|
||||
label: string
|
||||
onClick: () => void
|
||||
@@ -195,7 +195,9 @@
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
||||
runPreview()
|
||||
if (!previewOpen) {
|
||||
runPreview()
|
||||
}
|
||||
} else if (event.key === 'Enter' && previewSchema && !preventEnter) {
|
||||
applySchemaAndArgs()
|
||||
connectFirstNode()
|
||||
@@ -205,9 +207,6 @@
|
||||
}
|
||||
|
||||
function runPreview() {
|
||||
if (previewArguments) {
|
||||
previewArgs.val = structuredClone($state.snapshot(previewArguments))
|
||||
}
|
||||
onTestFlow?.()
|
||||
}
|
||||
|
||||
@@ -248,8 +247,8 @@
|
||||
|
||||
async function applySchemaAndArgs() {
|
||||
flowStore.val.schema = applyDiff(flowStore.val.schema, diff)
|
||||
if (previewArguments) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
if (previewArgs.val) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
}
|
||||
updatePreviewSchemaAndArgs(undefined)
|
||||
if ($flowInputEditorState) {
|
||||
@@ -259,11 +258,13 @@
|
||||
|
||||
function updatePreviewArguments(payloadData: Record<string, any> | undefined) {
|
||||
if (!payloadData) {
|
||||
previewArguments = savedPreviewArgs
|
||||
if (savedPreviewArgs) {
|
||||
previewArgs.val = savedPreviewArgs
|
||||
}
|
||||
return
|
||||
}
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
previewArguments = structuredClone($state.snapshot(payloadData))
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
previewArgs.val = structuredClone($state.snapshot(payloadData))
|
||||
}
|
||||
|
||||
let tabButtonWidth = 0
|
||||
@@ -372,7 +373,7 @@
|
||||
displayWebhookWarning
|
||||
editTab={$flowInputEditorState?.selectedTab}
|
||||
{previewSchema}
|
||||
bind:args={previewArguments}
|
||||
bind:args={previewArgs.val}
|
||||
bind:editPanelSize={
|
||||
() => {
|
||||
return editPanelSize
|
||||
@@ -399,9 +400,8 @@
|
||||
}}
|
||||
shouldDispatchChanges={true}
|
||||
on:change={() => {
|
||||
previewArguments = previewArguments
|
||||
if (!previewSchema) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
}
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
@@ -560,7 +560,7 @@
|
||||
on:isEditing={(e) => {
|
||||
preventEnter = e.detail
|
||||
}}
|
||||
previewArgs={previewArguments}
|
||||
previewArgs={previewArgs.val}
|
||||
{isValid}
|
||||
limitPayloadSize
|
||||
bind:this={savedInputsPicker}
|
||||
@@ -583,7 +583,7 @@
|
||||
on:select={(e) => {
|
||||
updatePreviewSchemaAndArgs(e.detail ?? undefined)
|
||||
}}
|
||||
selected={!!previewArguments}
|
||||
selected={!!previewArgs.val}
|
||||
bind:this={jsonInputs}
|
||||
/>
|
||||
</FlowInputEditor>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
import FlowModuleMockTransitionMessage from './FlowModuleMockTransitionMessage.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { SecondsInput } from '$lib/components/common'
|
||||
import DiffEditor from '$lib/components/DiffEditor.svelte'
|
||||
import DiffEditor, { type ButtonProp } from '$lib/components/DiffEditor.svelte'
|
||||
import FlowModuleTimeout from './FlowModuleTimeout.svelte'
|
||||
import HighlightCode from '$lib/components/HighlightCode.svelte'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
@@ -47,7 +47,7 @@
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { loadSchemaFromModule } from '../flowInfers'
|
||||
import FlowModuleSkip from './FlowModuleSkip.svelte'
|
||||
import { type Job, JobService } from '$lib/gen'
|
||||
import { type Job } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { checkIfParentLoop } from '../utils'
|
||||
import ModulePreviewResultViewer from '$lib/components/ModulePreviewResultViewer.svelte'
|
||||
@@ -102,6 +102,15 @@
|
||||
let workspaceScriptTag: string | undefined = $state(undefined)
|
||||
let workspaceScriptLang: ScriptLang | undefined = $state(undefined)
|
||||
let diffMode = $state(false)
|
||||
let diffButtons = $state<ButtonProp[]>([
|
||||
{
|
||||
text: 'Quit diff mode',
|
||||
color: 'red',
|
||||
onClick: () => {
|
||||
hideDiffMode()
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
let editor: Editor | undefined = $state()
|
||||
let diffEditor: DiffEditor | undefined = $state()
|
||||
@@ -120,7 +129,6 @@
|
||||
let s3Kind = $state('s3_client')
|
||||
let validCode = $state(true)
|
||||
let width = $state(1200)
|
||||
let lastJob: Job | undefined = $state(undefined)
|
||||
let testJob: Job | undefined = $state(undefined)
|
||||
let testIsLoading = $state(false)
|
||||
let scriptProgress = $state(undefined)
|
||||
@@ -197,43 +205,10 @@
|
||||
let editorSettingsPanelSize = $state(100 - untrack(() => editorPanelSize))
|
||||
let stepHistoryLoader = getStepHistoryLoaderContext()
|
||||
|
||||
let lastJobId: string | undefined = undefined
|
||||
|
||||
function onSelectedIdChange() {
|
||||
if (!flowStateStore?.val?.[$selectedId]?.schema && flowModule) {
|
||||
reload(flowModule)
|
||||
}
|
||||
lastJobId = undefined
|
||||
}
|
||||
|
||||
async function getLastJob() {
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!flowModule.id ||
|
||||
flowStateStore.val[flowModule.id]?.previewResult === 'never tested this far' ||
|
||||
!flowStateStore.val[flowModule.id]?.previewJobId
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
lastJobId == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
lastJob?.id == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
flowStateStore.val[flowModule.id]?.previewSuccess == undefined
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
|
||||
const job = await JobService.getJob({
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: flowStateStore.val[flowModule.id]?.previewJobId ?? '',
|
||||
noCode: true
|
||||
})
|
||||
if (job && job.type === 'CompletedJob') {
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
lastJob = job
|
||||
}
|
||||
}
|
||||
|
||||
let leftPanelSize = $state(0)
|
||||
@@ -270,13 +245,6 @@
|
||||
$effect.pre(() => {
|
||||
$selectedId && untrack(() => onSelectedIdChange())
|
||||
})
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if ($workspaceStore && $pathStore && flowModule?.id && flowStateStore) {
|
||||
untrack(() => getLastJob())
|
||||
}
|
||||
})
|
||||
let parentLoop = $derived(
|
||||
flowStore.val && flowModule ? checkIfParentLoop(flowStore.val, flowModule.id) : undefined
|
||||
)
|
||||
@@ -297,7 +265,13 @@
|
||||
showDiffMode,
|
||||
hideDiffMode,
|
||||
diffMode,
|
||||
lastDeployedCode
|
||||
lastDeployedCode,
|
||||
setDiffOriginal: (code: string) => {
|
||||
diffEditor?.setOriginal(code ?? '')
|
||||
},
|
||||
setDiffButtons: (buttons: ButtonProp[]) => {
|
||||
diffButtons = buttons
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -322,6 +296,12 @@
|
||||
let rawScriptLang = $derived(
|
||||
flowModule.value.type == 'rawscript' ? flowModule.value.language : undefined
|
||||
)
|
||||
|
||||
let modulePreviewResultViewer: ModulePreviewResultViewer | undefined = $state(undefined)
|
||||
|
||||
function onJobDone() {
|
||||
modulePreviewResultViewer?.getOutputPickerInner()?.setJobPreview()
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={onKeyDown} />
|
||||
@@ -486,10 +466,8 @@
|
||||
automaticLayout
|
||||
fixedOverflowWidgets
|
||||
defaultLang={scriptLangToEditorLang(flowModule.value.language)}
|
||||
class="h-full"
|
||||
showButtons={diffMode}
|
||||
showHistoryButton={false}
|
||||
on:hideDiffMode={hideDiffMode}
|
||||
className="h-full"
|
||||
buttons={diffMode ? diffButtons : []}
|
||||
/>
|
||||
{/key}
|
||||
{/if}
|
||||
@@ -596,6 +574,7 @@
|
||||
bind:testIsLoading
|
||||
bind:scriptProgress
|
||||
focusArg={highlightArg}
|
||||
{onJobDone}
|
||||
/>
|
||||
{:else if selected === 'advanced'}
|
||||
<Tabs bind:selected={advancedSelected}>
|
||||
@@ -900,15 +879,15 @@
|
||||
flowModule = flowModule
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
{lastJob}
|
||||
{scriptProgress}
|
||||
{testJob}
|
||||
{scriptProgress}
|
||||
mod={flowModule}
|
||||
{testIsLoading}
|
||||
disableMock={preprocessorModule || failureModule}
|
||||
disableHistory={failureModule}
|
||||
loadingJob={stepHistoryLoader?.stepStates[flowModule.id]?.loadingJobs}
|
||||
tagLabel={customUi?.tagLabel}
|
||||
bind:this={modulePreviewResultViewer}
|
||||
/>
|
||||
</Pane>
|
||||
{/if}
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
{:then Module}
|
||||
<Module.default
|
||||
open={true}
|
||||
class="h-screen"
|
||||
className="h-screen"
|
||||
readOnly
|
||||
automaticLayout
|
||||
defaultLang={scriptLangToEditorLang(language)}
|
||||
|
||||
@@ -144,9 +144,7 @@
|
||||
let testIsLoading = $state(false)
|
||||
let hover = $state(false)
|
||||
let connectingData: any | undefined = $state(undefined)
|
||||
let lastJob: any | undefined = $state(undefined)
|
||||
let outputPicker: OutputPicker | undefined = $state(undefined)
|
||||
let historyOpen = $state(false)
|
||||
let testJob: any | undefined = $state(undefined)
|
||||
let outputPickerBarOpen = $state(false)
|
||||
|
||||
@@ -170,30 +168,6 @@
|
||||
updateConnectingData(id, pickableIds, $flowPropPickerConfig, flowStateStore)
|
||||
})
|
||||
|
||||
function updateLastJob(flowStateStore: any | undefined) {
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!id ||
|
||||
flowStateStore.val[id]?.previewResult === 'never tested this far'
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJob = {
|
||||
id: flowStateStore.val[id]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[id]?.previewResult,
|
||||
type: 'CompletedJob' as const,
|
||||
success: flowStateStore.val[id]?.previewSuccess ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if (id) {
|
||||
updateLastJob(flowStateStore)
|
||||
}
|
||||
})
|
||||
|
||||
let isConnectingCandidate = $derived(
|
||||
!!id && !!$flowPropPickerConfig && !!pickableIds && Object.keys(pickableIds).includes(id)
|
||||
)
|
||||
@@ -207,6 +181,9 @@
|
||||
const action = $derived(getAiModuleAction(id))
|
||||
|
||||
let testRunDropdownOpen = $state(false)
|
||||
|
||||
let outputPickerInner: OutputPickerInner | undefined = $state(undefined)
|
||||
let historyOpen = $derived.by(() => outputPickerInner?.getHistoryOpen?.() ?? false)
|
||||
</script>
|
||||
|
||||
{#if deletable && id && editId}
|
||||
@@ -271,7 +248,15 @@
|
||||
{@const flowStore = flowEditorContext?.flowStore.val}
|
||||
{@const mod = flowStore?.value ? dfsPreviousResults(id, flowStore, false)[0] : undefined}
|
||||
{#if mod && flowStateStore?.val?.[id]}
|
||||
<ModuleTest bind:this={moduleTest} {mod} bind:testIsLoading bind:testJob />
|
||||
<ModuleTest
|
||||
bind:this={moduleTest}
|
||||
{mod}
|
||||
bind:testIsLoading
|
||||
bind:testJob
|
||||
onJobDone={() => {
|
||||
outputPickerInner?.setJobPreview?.()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -455,7 +440,6 @@
|
||||
prefix={'results'}
|
||||
connectingData={isConnecting ? connectingData : undefined}
|
||||
{mock}
|
||||
{lastJob}
|
||||
{testJob}
|
||||
moduleId={id}
|
||||
onSelect={selectConnection}
|
||||
@@ -463,12 +447,12 @@
|
||||
{path}
|
||||
{loopStatus}
|
||||
rightMargin
|
||||
bind:derivedHistoryOpen={historyOpen}
|
||||
historyOffset={{ mainAxis: 12, crossAxis: -9 }}
|
||||
clazz="p-1"
|
||||
isLoading={testIsLoading ||
|
||||
(id ? stepHistoryLoader?.stepStates[id]?.loadingJobs : false)}
|
||||
initial={id ? stepHistoryLoader?.stepStates[id]?.initial : undefined}
|
||||
bind:this={outputPickerInner}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputPicker>
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
{selected}
|
||||
{hover}
|
||||
id={id ?? ''}
|
||||
isConnectingCandidate={true}
|
||||
isConnectingCandidate={nodeKind !== 'result'}
|
||||
variant="virtual"
|
||||
type={outputType}
|
||||
{darkMode}
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
|
||||
let { inputTransform, id, onEditInput }: Props = $props()
|
||||
|
||||
const { flowStore, flowStateStore, testSteps, previewArgs } =
|
||||
const { flowStore, flowStateStore, stepsInputArgs, previewArgs } =
|
||||
getContext<FlowEditorContext | undefined>('FlowEditorContext') || {}
|
||||
|
||||
onMount(() => {
|
||||
testSteps?.updateStepArgs(id, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
stepsInputArgs?.updateStepArgs(id, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
})
|
||||
|
||||
const input = $derived(testSteps?.getStepArgs(id))
|
||||
const input = $derived(stepsInputArgs?.getStepArgs(id))
|
||||
</script>
|
||||
|
||||
<div class="p-4 pr-6 h-full overflow-y-auto">
|
||||
@@ -42,10 +42,10 @@
|
||||
{:else if inputTransform[key].type === 'static'}
|
||||
<DollarSign size={12} class="text-tertiary font-mono -my-1" />
|
||||
{/if}
|
||||
{#if testSteps?.isArgManuallySet(id, key)}
|
||||
{#if stepsInputArgs?.isArgManuallySet(id, key)}
|
||||
<button
|
||||
onclick={() => {
|
||||
testSteps?.evalArg(id, key, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
stepsInputArgs?.evalArg(id, key, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
}}
|
||||
title="Re-evaluate input"
|
||||
class="-my-1 ml-0.5 hover:text-primary dark:hover:text-primary dark:text-gray-500 text-gray-300"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
|
||||
import StepHistory from './StepHistory.svelte'
|
||||
import { Popover } from '$lib/components/meltComponents'
|
||||
import { untrack } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import { Tooltip } from '$lib/components/meltComponents'
|
||||
import type { Job } from '$lib/gen'
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
@@ -29,6 +29,7 @@
|
||||
import DisplayResultControlBar from '$lib/components/DisplayResultControlBar.svelte'
|
||||
import { base } from '$lib/base'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { FlowEditorContext, OutputViewerJob } from '../types'
|
||||
|
||||
interface Props {
|
||||
prefix?: string
|
||||
@@ -44,10 +45,7 @@
|
||||
fullResult?: boolean
|
||||
closeOnOutsideClick?: boolean
|
||||
getLogs?: boolean
|
||||
selectedJob?: SelectedJob
|
||||
forceJson?: boolean
|
||||
isLoading?: boolean
|
||||
preview?: 'mock' | 'job' | undefined
|
||||
hideHeaderBar?: boolean
|
||||
simpleViewer?: any | undefined
|
||||
path?: string
|
||||
@@ -56,7 +54,6 @@
|
||||
rightMargin?: boolean
|
||||
disableMock?: boolean
|
||||
disableHistory?: boolean
|
||||
lastJob?: Job
|
||||
testJob?: Job & { result_stream?: string }
|
||||
derivedHistoryOpen?: boolean // derived from historyOpen
|
||||
historyOffset?: any
|
||||
@@ -72,8 +69,7 @@
|
||||
}
|
||||
|
||||
let {
|
||||
lastJob = undefined,
|
||||
testJob = undefined,
|
||||
testJob,
|
||||
prefix = '',
|
||||
allowCopy = false,
|
||||
connectingData = undefined,
|
||||
@@ -82,10 +78,7 @@
|
||||
fullResult = false,
|
||||
closeOnOutsideClick = false,
|
||||
getLogs = false,
|
||||
selectedJob = $bindable(undefined),
|
||||
forceJson = $bindable(false),
|
||||
isLoading = $bindable(false),
|
||||
preview = $bindable(undefined),
|
||||
hideHeaderBar = false,
|
||||
simpleViewer = undefined,
|
||||
path = '',
|
||||
@@ -94,7 +87,6 @@
|
||||
rightMargin = false,
|
||||
disableMock = false,
|
||||
disableHistory = false,
|
||||
derivedHistoryOpen = $bindable(false),
|
||||
historyOffset = { mainAxis: 8, crossAxis: -4.5 },
|
||||
clazz,
|
||||
copilot_fix,
|
||||
@@ -106,19 +98,6 @@
|
||||
customEmptyJobMessage
|
||||
}: Props = $props()
|
||||
|
||||
type SelectedJob =
|
||||
| ((
|
||||
| Job
|
||||
| {
|
||||
id: string
|
||||
result: unknown
|
||||
type: 'CompletedJob'
|
||||
workspace_id: string
|
||||
success: boolean
|
||||
}
|
||||
) & { preview?: boolean; result_stream?: string; result?: unknown })
|
||||
| undefined
|
||||
|
||||
let jsonView = $state(false)
|
||||
let clientHeight: number = $state(0)
|
||||
let tmpMock: { enabled: boolean; return_value?: unknown } | undefined = $state(undefined)
|
||||
@@ -127,6 +106,8 @@
|
||||
let historyOpen = $state(false)
|
||||
let contentEl: HTMLDivElement | undefined = $state(undefined)
|
||||
let hasOverflow = $state(false)
|
||||
let preview: 'mock' | 'job' | undefined = $state(undefined)
|
||||
let selectedJob: OutputViewerJob = $state(undefined)
|
||||
|
||||
function checkOverflow() {
|
||||
if (contentEl) {
|
||||
@@ -134,26 +115,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function selectJob(nJob: SelectedJob | undefined) {
|
||||
function selectJob(nJob: OutputViewerJob | undefined) {
|
||||
if (nJob && (nJob.result_stream || nJob.type == 'CompletedJob')) {
|
||||
selectedJob = nJob
|
||||
} else if (job && (job.result_stream || job.type == 'CompletedJob')) {
|
||||
selectedJob = job
|
||||
} else if (lastJob && (lastJob.result_stream || lastJob.type == 'CompletedJob')) {
|
||||
selectedJob = lastJob
|
||||
} else {
|
||||
selectedJob = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (!job || !(job.result_stream || job.type == 'CompletedJob')) {
|
||||
if (!lastJob || !(lastJob.result_stream || lastJob.type == 'CompletedJob')) {
|
||||
return
|
||||
}
|
||||
selectJob(job)
|
||||
|
||||
if (job.preview && mock?.enabled) {
|
||||
preview = 'job'
|
||||
job.preview = false
|
||||
}
|
||||
selectJob(lastJob)
|
||||
})
|
||||
|
||||
function togglePreview(nPrev: 'mock' | 'job' | undefined) {
|
||||
@@ -174,6 +150,9 @@
|
||||
let toolbarLocationJob: 'self' | 'external' | undefined = $state(undefined)
|
||||
let toolbarLocationMock: 'self' | 'external' | undefined = $state(undefined)
|
||||
|
||||
const flowEditorContext = getContext<FlowEditorContext | undefined>('FlowEditorContext')
|
||||
const flowStateStore = flowEditorContext?.flowStateStore
|
||||
|
||||
function updateCanEditWithDblClick(newValue: boolean) {
|
||||
canEditWithDblClick = newValue
|
||||
if (debounceTimeout) {
|
||||
@@ -224,16 +203,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
let mockUpdateStatus = $derived(
|
||||
function updateLastJob() {
|
||||
if (testJob) {
|
||||
return testJob
|
||||
}
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!moduleId ||
|
||||
flowStateStore.val[moduleId]?.previewResult === 'never tested this far'
|
||||
) {
|
||||
return
|
||||
}
|
||||
return {
|
||||
id: flowStateStore.val[moduleId]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[moduleId]?.previewResult,
|
||||
type: 'CompletedJob' as const,
|
||||
success: flowStateStore.val[moduleId]?.previewSuccess ?? undefined
|
||||
} as Job & { result_stream?: string } & { preview?: boolean }
|
||||
}
|
||||
|
||||
const lastJob = $derived.by(updateLastJob)
|
||||
|
||||
export function setJobPreview() {
|
||||
if (mock?.enabled) {
|
||||
preview = 'job'
|
||||
}
|
||||
}
|
||||
|
||||
const mockUpdateStatus = $derived(
|
||||
preview === 'mock' && !mock?.enabled
|
||||
? 'restore'
|
||||
: preview === 'job' && mock?.enabled && selectedJob?.type === 'CompletedJob'
|
||||
: preview === 'job' && mock?.enabled && selectedJob?.['type'] === 'CompletedJob'
|
||||
? 'override'
|
||||
: undefined
|
||||
)
|
||||
$effect(() => {
|
||||
derivedHistoryOpen = historyOpen
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (displayResultJob && typeof displayResultJob.getToolbarLocation === 'function') {
|
||||
toolbarLocationJob = displayResultJob.getToolbarLocation()
|
||||
@@ -258,23 +262,67 @@
|
||||
}
|
||||
})
|
||||
|
||||
let job = $derived.by(() => {
|
||||
if (testJob) {
|
||||
return { ...testJob, preview: testJob.type === 'CompletedJob' }
|
||||
}
|
||||
if (lastJob) {
|
||||
return { ...lastJob, preview: false }
|
||||
}
|
||||
return undefined
|
||||
}) as SelectedJob | undefined
|
||||
|
||||
let popoverHeight = $derived(customHeight ?? (clientHeight > 0 ? clientHeight : 0))
|
||||
|
||||
// If a test job is running, we override the preview to show the test job
|
||||
const executingTestJob = $derived(testJob && testJob.type === 'QueuedJob')
|
||||
|
||||
const isLoadingAndNotMock = $derived(
|
||||
isLoading && job?.result_stream === undefined && !mock?.enabled
|
||||
isLoading && lastJob?.result_stream === undefined && (!mock?.enabled || executingTestJob)
|
||||
)
|
||||
|
||||
const copilot_fix_render = $derived(copilot_fix)
|
||||
|
||||
export function getPreview() {
|
||||
return preview
|
||||
}
|
||||
|
||||
async function selectJobFromHistory({
|
||||
detail
|
||||
}: {
|
||||
detail: 'mock' | (Job & { getFullJob: () => Promise<Job> })
|
||||
}) {
|
||||
if (!detail) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
if (detail === 'mock') {
|
||||
if (mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
togglePreview('mock')
|
||||
return
|
||||
}
|
||||
if (detail.id === lastJob?.id && !mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
|
||||
try {
|
||||
const fullJob = await detail.getFullJob()
|
||||
if (fullJob) {
|
||||
selectJob(fullJob)
|
||||
togglePreview('job')
|
||||
}
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
export function getHistoryOpen() {
|
||||
return historyOpen
|
||||
}
|
||||
|
||||
export function getSelectedJob() {
|
||||
return selectedJob
|
||||
}
|
||||
|
||||
const noHistory = $derived(
|
||||
loopStatus ? (loopStatus.type === 'self' ? 'isLoop' : 'isInsideLoop') : undefined
|
||||
)
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -282,6 +330,7 @@
|
||||
bind:clientHeight
|
||||
style={canEditWithDblClick ? 'cursor: text;' : ''}
|
||||
>
|
||||
<!-- Top toolbar -->
|
||||
<div
|
||||
class={twMerge(
|
||||
'text-xs px-1',
|
||||
@@ -297,89 +346,24 @@
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-0.5">
|
||||
<!-- History picker -->
|
||||
{#if !disableHistory}
|
||||
<Popover
|
||||
bind:this={stepHistoryPopover}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-start',
|
||||
offset: historyOffset,
|
||||
gutter: 0 // hack to make offset effective, see https://github.com/melt-ui/melt-ui/issues/528
|
||||
}}
|
||||
contentClasses="w-[225px] overflow-hidden"
|
||||
{closeOnOutsideClick}
|
||||
usePointerDownOutside={closeOnOutsideClick}
|
||||
disablePopup={!!connectingData || jsonView}
|
||||
bind:isOpen={historyOpen}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="contained"
|
||||
btnClasses="bg-surface h-[27px]"
|
||||
startIcon={{ icon: History }}
|
||||
nonCaptureEvent
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="rounded-[inherit]" style={`height: ${popoverHeight}px`}>
|
||||
<StepHistory
|
||||
{moduleId}
|
||||
{getLogs}
|
||||
on:select={async ({ detail }) => {
|
||||
if (!detail) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
if (detail === 'mock') {
|
||||
if (mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
togglePreview('mock')
|
||||
return
|
||||
}
|
||||
if (detail.id === job?.id && !mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a timeout to show loading state after 200ms
|
||||
const loadingTimeout = setTimeout(() => {
|
||||
isLoading = true
|
||||
}, 200)
|
||||
|
||||
try {
|
||||
const fullJob = await detail.getFullJob()
|
||||
if (fullJob) {
|
||||
selectJob(fullJob)
|
||||
togglePreview('job')
|
||||
}
|
||||
} finally {
|
||||
// Clear the timeout if operation completed before 200ms
|
||||
clearTimeout(loadingTimeout)
|
||||
isLoading = false
|
||||
}
|
||||
}}
|
||||
mockValue={mock?.return_value}
|
||||
mockEnabled={mock?.enabled}
|
||||
{path}
|
||||
noHistory={loopStatus
|
||||
? loopStatus.type === 'self'
|
||||
? 'isLoop'
|
||||
: 'isInsideLoop'
|
||||
: undefined}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{@render historyPicker()}
|
||||
{/if}
|
||||
{#if !isLoadingAndNotMock || mock?.enabled}
|
||||
<!-- Badge-->
|
||||
{#if executingTestJob}
|
||||
<OutputBadge
|
||||
job={testJob}
|
||||
class={twMerge(
|
||||
'min-w-16 min-h-[23px] text-secondary',
|
||||
preview ? 'bg-surface shadow-sm' : ''
|
||||
)}
|
||||
/>
|
||||
{:else if !isLoadingAndNotMock || mock?.enabled}
|
||||
<div
|
||||
class={twMerge(
|
||||
'w-grow min-w-0 flex gap-1 items-center h-[27px] rounded-md group',
|
||||
preview || selectedJob?.id !== job?.id ? 'p-[2px] bg-surface-secondary' : ''
|
||||
preview || selectedJob?.id !== lastJob?.id ? 'p-[2px] bg-surface-secondary' : ''
|
||||
)}
|
||||
>
|
||||
{#if loopStatus?.type === 'self'}
|
||||
@@ -415,7 +399,7 @@
|
||||
job={selectedJob}
|
||||
class={twMerge(
|
||||
'min-w-16 text-secondary',
|
||||
preview || selectedJob?.id !== job?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
preview || selectedJob?.id !== lastJob?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
@@ -433,17 +417,9 @@
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if testJob}
|
||||
<!-- {JSON.stringify(testJob)} -->
|
||||
<OutputBadge
|
||||
job={testJob}
|
||||
class={twMerge(
|
||||
'min-w-16 text-secondary',
|
||||
preview || testJob?.id !== job?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Pin button -->
|
||||
{#if !disableMock && !isLoadingAndNotMock}
|
||||
<Tooltip disablePopup={mock?.enabled}>
|
||||
<Button
|
||||
@@ -472,6 +448,7 @@
|
||||
{/if}
|
||||
|
||||
{#if jsonView}
|
||||
<!-- Save button -->
|
||||
<Button
|
||||
size="xs2"
|
||||
color="green"
|
||||
@@ -492,6 +469,7 @@
|
||||
}}
|
||||
disabled={!!error || !tmpMock}
|
||||
/>
|
||||
<!-- Cancel button -->
|
||||
<Button
|
||||
size="xs2"
|
||||
color="red"
|
||||
@@ -503,7 +481,8 @@
|
||||
tmpMock = undefined
|
||||
}}
|
||||
/>
|
||||
{:else if mock?.enabled && !preview}
|
||||
{:else if mock?.enabled && !preview && !executingTestJob}
|
||||
<!-- Edit pin button -->
|
||||
<Tooltip disablePopup={mock?.enabled}>
|
||||
<Button
|
||||
size="xs2"
|
||||
@@ -614,6 +593,7 @@
|
||||
<Loader2 class="animate-spin" />
|
||||
</div>
|
||||
{:else if connectingData !== undefined || simpleViewer}
|
||||
<!-- Display data for input connections -->
|
||||
<ObjectViewer
|
||||
json={moduleId
|
||||
? {
|
||||
@@ -630,6 +610,7 @@
|
||||
{editKey}
|
||||
/>
|
||||
{:else if jsonView}
|
||||
<!-- Json editor for editing mock data -->
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:then Module}
|
||||
@@ -653,12 +634,12 @@
|
||||
class="h-full"
|
||||
/>
|
||||
{/await}
|
||||
{:else if (mock?.enabled || preview == 'mock') && preview != 'job'}
|
||||
{:else if (mock?.enabled || preview == 'mock') && preview != 'job' && !executingTestJob}
|
||||
<!-- Display data for mock data -->
|
||||
{#if fullResult}
|
||||
<div class="break-words relative h-full">
|
||||
<DisplayResult
|
||||
bind:this={displayResultMock}
|
||||
bind:forceJson
|
||||
workspaceId={undefined}
|
||||
jobId={undefined}
|
||||
result_stream={undefined}
|
||||
@@ -682,12 +663,12 @@
|
||||
/>
|
||||
{/if}
|
||||
{:else if selectedJob != undefined && (selectedJob.result_stream || selectedJob.type == 'CompletedJob')}
|
||||
<!-- Display data for job data -->
|
||||
{#if fullResult}
|
||||
<div class="break-words relative h-full">
|
||||
{#key selectedJob}
|
||||
<DisplayResult
|
||||
bind:this={displayResultJob}
|
||||
bind:forceJson
|
||||
workspaceId={selectedJob?.workspace_id}
|
||||
jobId={selectedJob?.id}
|
||||
result={selectedJob?.result}
|
||||
@@ -715,7 +696,7 @@
|
||||
pureViewer={false}
|
||||
/>
|
||||
{/if}
|
||||
{:else if !job}
|
||||
{:else if !lastJob}
|
||||
<div class="flex flex-col items-center justify-center h-full">
|
||||
<p class="text-xs text-secondary">
|
||||
{customEmptyJobMessage ?? 'Test this step to see results'}{#if !disableMock}
|
||||
@@ -753,6 +734,47 @@
|
||||
</button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet historyPicker()}
|
||||
<Popover
|
||||
bind:this={stepHistoryPopover}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-start',
|
||||
offset: historyOffset,
|
||||
gutter: 0 // hack to make offset effective, see https://github.com/melt-ui/melt-ui/issues/528
|
||||
}}
|
||||
contentClasses="w-[225px] overflow-hidden"
|
||||
{closeOnOutsideClick}
|
||||
usePointerDownOutside={closeOnOutsideClick}
|
||||
disablePopup={!!connectingData || jsonView}
|
||||
bind:isOpen={historyOpen}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="contained"
|
||||
btnClasses="bg-surface h-[27px]"
|
||||
startIcon={{ icon: History }}
|
||||
nonCaptureEvent
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="rounded-[inherit]" style={`height: ${popoverHeight}px`}>
|
||||
<StepHistory
|
||||
{moduleId}
|
||||
{getLogs}
|
||||
on:select={selectJobFromHistory}
|
||||
mockValue={mock?.return_value}
|
||||
mockEnabled={mock?.enabled}
|
||||
{path}
|
||||
{noHistory}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{/snippet}
|
||||
|
||||
<style>
|
||||
.dbl-click-editable {
|
||||
cursor: text;
|
||||
|
||||
+20
-6
@@ -8,11 +8,11 @@ import {
|
||||
} from './previousResults'
|
||||
import { evalValue } from './utils'
|
||||
|
||||
export class TestSteps {
|
||||
export class StepsInputArgs {
|
||||
#stepsEvaluated = $state<Record<string, Record<string, any>>>({})
|
||||
#steps = $state<Record<string, Record<string, any>>>({})
|
||||
|
||||
constructor() {}
|
||||
constructor() { }
|
||||
|
||||
setStepArgsManually(moduleId: string, args: Record<string, any>) {
|
||||
this.#steps[moduleId] = args
|
||||
@@ -53,10 +53,20 @@ export class TestSteps {
|
||||
}
|
||||
|
||||
isArgManuallySet(moduleId: string, argName: string): boolean {
|
||||
return (
|
||||
JSON.stringify(this.#steps[moduleId]?.[argName]) !==
|
||||
JSON.stringify(this.#stepsEvaluated[moduleId]?.[argName])
|
||||
)
|
||||
try {
|
||||
const stepsValue = this.#steps[moduleId]?.[argName]
|
||||
const evaluatedValue = this.#stepsEvaluated[moduleId]?.[argName]
|
||||
|
||||
const stepsStr = JSON.stringify(stepsValue)
|
||||
const evaluatedStr = JSON.stringify(evaluatedValue)
|
||||
return stepsStr !== evaluatedStr
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Error in isArgManuallySet for moduleId: ${moduleId}, argName: ${argName}`,
|
||||
error
|
||||
)
|
||||
return this.#steps[moduleId]?.[argName] !== this.#stepsEvaluated[moduleId]?.[argName]
|
||||
}
|
||||
}
|
||||
|
||||
getManuallyEditedArgs(moduleId: string): string[] {
|
||||
@@ -185,4 +195,8 @@ export class TestSteps {
|
||||
})
|
||||
this.#stepsEvaluated[moduleId] = nargs
|
||||
}
|
||||
|
||||
resetManuallyEditedArgs() {
|
||||
this.#steps = $state.snapshot(this.#stepsEvaluated)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OpenFlow } from '$lib/gen'
|
||||
import type { Job, OpenFlow } from '$lib/gen'
|
||||
import type { History } from '$lib/history.svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type ScriptEditorDrawer from './content/ScriptEditorDrawer.svelte'
|
||||
@@ -7,12 +7,13 @@ import type { FlowBuilderWhitelabelCustomUi } from '../custom_ui'
|
||||
import type Editor from '../Editor.svelte'
|
||||
import type SimpleEditor from '../SimpleEditor.svelte'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
import type { TestSteps } from './testSteps.svelte'
|
||||
import type { StepsInputArgs } from './stepsInputArgs.svelte'
|
||||
import type { Asset, AssetWithAccessType } from '../assets/lib'
|
||||
import type S3FilePicker from '../S3FilePicker.svelte'
|
||||
import type DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
import type ResourceEditorDrawer from '../ResourceEditorDrawer.svelte'
|
||||
import type { ModulesTestStates } from '../modulesTest.svelte'
|
||||
import type { ButtonProp } from '$lib/components/DiffEditor.svelte'
|
||||
|
||||
export type FlowInput = Record<
|
||||
string,
|
||||
@@ -37,31 +38,33 @@ export type ExtendedOpenFlow = OpenFlow & {
|
||||
|
||||
export type FlowInputEditorState = {
|
||||
selectedTab:
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
editPanelSize: number | undefined
|
||||
payloadData: Record<string, any> | undefined
|
||||
}
|
||||
|
||||
export type CurrentEditor =
|
||||
| ((
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
setDiffOriginal?: (code: string) => void
|
||||
setDiffButtons?: (buttons: ButtonProp[]) => void
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| undefined
|
||||
|
||||
export type FlowEditorContext = {
|
||||
@@ -75,7 +78,7 @@ export type FlowEditorContext = {
|
||||
flowStore: StateStore<ExtendedOpenFlow>
|
||||
flowInputEditorState: Writable<FlowInputEditorState>
|
||||
flowStateStore: StateStore<FlowState>
|
||||
testSteps: TestSteps
|
||||
stepsInputArgs: StepsInputArgs
|
||||
saveDraft: () => void
|
||||
initialPathStore: Writable<string>
|
||||
fakeInitialPath: string
|
||||
@@ -97,3 +100,16 @@ export type FlowGraphAssetContext = StateStore<{
|
||||
additionalAssetsMap: Record<string, AssetWithAccessType[]>
|
||||
computeAssetsCount: (asset: Asset) => number
|
||||
}>
|
||||
|
||||
export type OutputViewerJob =
|
||||
| ((
|
||||
| Job
|
||||
| {
|
||||
id: string
|
||||
result: unknown
|
||||
type: 'CompletedJob'
|
||||
workspace_id: string
|
||||
success: boolean
|
||||
}
|
||||
) & { result_stream?: string; result?: unknown })
|
||||
| undefined
|
||||
|
||||
@@ -22,11 +22,10 @@ function create_context_function_template(eval_string: string, context: Record<s
|
||||
return `
|
||||
return function (context) {
|
||||
"use strict";
|
||||
${
|
||||
Object.keys(context).length > 0
|
||||
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
|
||||
: ``
|
||||
}
|
||||
${Object.keys(context).length > 0
|
||||
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
|
||||
: ``
|
||||
}
|
||||
return ${eval_string}
|
||||
}`
|
||||
}
|
||||
@@ -63,6 +62,9 @@ export function evalValue(
|
||||
v = undefined
|
||||
}
|
||||
}
|
||||
if (v === NEVER_TESTED_THIS_FAR) {
|
||||
v = undefined
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
<DiffEditor
|
||||
open={false}
|
||||
bind:this={diffEditor}
|
||||
class="h-full"
|
||||
className="h-full"
|
||||
automaticLayout
|
||||
fixedOverflowWidgets
|
||||
defaultLang={scriptLangToEditorLang(inlineScript?.language)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BROWSER } from 'esm-env'
|
||||
import { derived, type Readable, writable } from 'svelte/store'
|
||||
import { derived, get, type Readable, writable } from 'svelte/store'
|
||||
|
||||
import type { IntrospectionQuery } from 'graphql'
|
||||
import {
|
||||
@@ -153,6 +153,15 @@ export function setCopilotInfo(aiConfig: AIConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentModel() {
|
||||
const model =
|
||||
get(copilotSessionModel) ?? get(copilotInfo).defaultModel ?? get(copilotInfo).aiModels[0]
|
||||
if (!model) {
|
||||
throw new Error('No model selected')
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
export const codeCompletionLoading = writable<boolean>(false)
|
||||
export const metadataCompletionEnabled = writable<boolean>(true)
|
||||
export const stepInputCompletionEnabled = writable<boolean>(true)
|
||||
@@ -166,7 +175,9 @@ export const formatOnSave = writable<boolean>(
|
||||
getLocalSetting(FORMAT_ON_SAVE_SETTING_NAME) != 'false'
|
||||
)
|
||||
export const vimMode = writable<boolean>(getLocalSetting(VIM_MODE_SETTING_NAME) == 'true')
|
||||
export const relativeLineNumbers = writable<boolean>(getLocalSetting(RELATIVE_LINE_NUMBERS_SETTING_NAME) == 'true')
|
||||
export const relativeLineNumbers = writable<boolean>(
|
||||
getLocalSetting(RELATIVE_LINE_NUMBERS_SETTING_NAME) == 'true'
|
||||
)
|
||||
export const codeCompletionSessionEnabled = writable<boolean>(
|
||||
getLocalSetting(CODE_COMPLETION_SETTING_NAME) != 'false'
|
||||
)
|
||||
@@ -176,9 +187,9 @@ const sessionProvider = getLocalSetting(COPILOT_SESSION_PROVIDER_SETTING_NAME)
|
||||
export const copilotSessionModel = writable<AIProviderModel | undefined>(
|
||||
sessionModel && sessionProvider
|
||||
? {
|
||||
model: sessionModel,
|
||||
provider: sessionProvider as AIProvider
|
||||
}
|
||||
model: sessionModel,
|
||||
provider: sessionProvider as AIProvider
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
export const usedTriggerKinds = writable<string[]>([])
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import type { FlowPropPickerConfig, PropPickerContext } from '$lib/components/prop_picker'
|
||||
import type { PickableProperties } from '$lib/components/flows/previousResults'
|
||||
import { Triggers } from '$lib/components/triggers/triggers.svelte'
|
||||
import { TestSteps } from '$lib/components/flows/testSteps.svelte'
|
||||
import { StepsInputArgs } from '$lib/components/flows/stepsInputArgs.svelte'
|
||||
import { ModulesTestStates } from '$lib/components/modulesTest.svelte'
|
||||
|
||||
let token = $page.url.searchParams.get('wm_token') ?? undefined
|
||||
@@ -75,7 +75,7 @@
|
||||
const moving = writable<{ id: string } | undefined>(undefined)
|
||||
const history = initHistory(flowStore.val)
|
||||
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
setContext<TriggerContext>('TriggerContext', {
|
||||
@@ -94,7 +94,7 @@
|
||||
pathStore: writable(''),
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft: () => {},
|
||||
initialPathStore: writable(''),
|
||||
fakeInitialPath: '',
|
||||
@@ -292,7 +292,7 @@
|
||||
noEditor
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
} else {
|
||||
previewArgsStore.val = ev.detail.args ?? {}
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.533.1"
|
||||
wmill_pg = ">=1.533.1"
|
||||
wmill = ">=1.535.0"
|
||||
wmill_pg = ">=1.535.0"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.533.1
|
||||
version: 1.535.0
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.533.1'
|
||||
ModuleVersion = '1.535.0'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.533.1"
|
||||
version = "1.535.0"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
docker run --rm -d \
|
||||
--name windmill-db-dev \
|
||||
-e POSTGRES_PASSWORD=changeme \
|
||||
-e POSTGRES_DB=windmill \
|
||||
-p 5432:5432 \
|
||||
-v windmill_db_data:/var/lib/postgresql/data \
|
||||
postgres:16
|
||||
|
||||
echo "PostgreSQL database started successfully!"
|
||||
echo "Connection string: postgres://postgres:changeme@localhost:5432/windmill"
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.533.1",
|
||||
"version": "1.535.0",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.533.1",
|
||||
"version": "1.535.0",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.533.1
|
||||
1.535.0
|
||||
|
||||
Reference in New Issue
Block a user