diff --git a/.github/workflows/jsr_on_release.yml b/.github/workflows/jsr_on_release.yml index 3a797fe842..4a9d443589 100644 --- a/.github/workflows/jsr_on_release.yml +++ b/.github/workflows/jsr_on_release.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - id-token: ${{ secrets.JSR_OIDC_ID_TOKEN }} + id-token: write steps: - uses: actions/checkout@v4 - run: cd typescript-client && ./publish.jsr.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index ba7784d7d7..ecad190df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,89 @@ # Changelog +## [1.327.0](https://github.com/windmill-labs/windmill/compare/v1.326.1...v1.327.0) (2024-05-13) + + +### Features + +* nativets can use the wmill library + setClient not required anymore ([#3714](https://github.com/windmill-labs/windmill/issues/3714)) ([8b21f08](https://github.com/windmill-labs/windmill/commit/8b21f0812c939542ed84863de029b60f94526417)) + +## [1.326.1](https://github.com/windmill-labs/windmill/compare/v1.326.0...v1.326.1) (2024-05-12) + + +### Bug Fixes + +* un-inline monaco editor workers for faster initial load ([e2991ef](https://github.com/windmill-labs/windmill/commit/e2991ef9237d17de5e6ef8074387998a96785396)) + +## [1.326.0](https://github.com/windmill-labs/windmill/compare/v1.325.2...v1.326.0) (2024-05-12) + + +### Features + +* logs can be downloaded directly from server/frontend if using shared volume ([a3a66d0](https://github.com/windmill-labs/windmill/commit/a3a66d0577f57cae6e0de5a942a35d45cc8e9472)) + +## [1.325.2](https://github.com/windmill-labs/windmill/compare/v1.325.1...v1.325.2) (2024-05-11) + + +### Bug Fixes + +* improve status exit dedicated workers ([12e302a](https://github.com/windmill-labs/windmill/commit/12e302a309ee8148d2a9d4a168807b97f5fd1709)) + +## [1.325.1](https://github.com/windmill-labs/windmill/compare/v1.325.0...v1.325.1) (2024-05-11) + + +### Bug Fixes + +* **cli:** support whileloop in flow cli sync ([d0a6dda](https://github.com/windmill-labs/windmill/commit/d0a6ddab94dfae4d6fab40c4d41acb85dab18183)) +* improve nodejs mode after esm update ([87add79](https://github.com/windmill-labs/windmill/commit/87add79de2e4df38e59d2e334bba0487c8ff4ecc)) +* selectedRow for a table is set on table actions ([447e2d7](https://github.com/windmill-labs/windmill/commit/447e2d7742edcd0af7184f6e8e5e67cf99f8103b)) + +## [1.325.0](https://github.com/windmill-labs/windmill/compare/v1.324.2...v1.325.0) (2024-05-10) + + +### Features + +* worker metrics ([#3697](https://github.com/windmill-labs/windmill/issues/3697)) ([696a561](https://github.com/windmill-labs/windmill/commit/696a5612f8551066b7b833ba555bd006914faff6)) + + +### Bug Fixes + +* **backend:** return flow result if flow fails or stops before early return ([#3704](https://github.com/windmill-labs/windmill/issues/3704)) ([67f4a4a](https://github.com/windmill-labs/windmill/commit/67f4a4afeec79daa81930124594ecf3131c18133)) + +## [1.324.2](https://github.com/windmill-labs/windmill/compare/v1.324.1...v1.324.2) (2024-05-09) + + +### Bug Fixes + +* fix build ([20dfbda](https://github.com/windmill-labs/windmill/commit/20dfbda69e643753b1ff17137ce18ed23ff45dd1)) + +## [1.324.1](https://github.com/windmill-labs/windmill/compare/v1.324.0...v1.324.1) (2024-05-09) + + +### Bug Fixes + +* **cli:** narrow codebases to bun ([252ac18](https://github.com/windmill-labs/windmill/commit/252ac18d477742d93a2004a0976a809d10bff3d1)) + +## [1.324.0](https://github.com/windmill-labs/windmill/compare/v1.323.6...v1.324.0) (2024-05-09) + + +### Features + +* critical error side channel ([#3625](https://github.com/windmill-labs/windmill/issues/3625)) ([29b1e6f](https://github.com/windmill-labs/windmill/commit/29b1e6f6a28ac9d9c014e4da2e23c9f2e45e1628)) +* disable response logs based on env var ([#3685](https://github.com/windmill-labs/windmill/issues/3685)) ([cd1711c](https://github.com/windmill-labs/windmill/commit/cd1711cf03894e65e81ee74631b0e04d7dae76d2)) +* **frontend:** Add a button tocopy the path of a subflow ([#3691](https://github.com/windmill-labs/windmill/issues/3691)) ([630ae7e](https://github.com/windmill-labs/windmill/commit/630ae7ec4b243e41d841bdae41c1c87b0fda5a82)) +* **frontend:** add missing date in the previous run panel ([#3693](https://github.com/windmill-labs/windmill/issues/3693)) ([49867c0](https://github.com/windmill-labs/windmill/commit/49867c06e63b5fc7b73031d946effb88c1ad1d1e)) +* **frontend:** Improve app components ([#3672](https://github.com/windmill-labs/windmill/issues/3672)) ([ee2a193](https://github.com/windmill-labs/windmill/commit/ee2a193958b87a7557eadfd7ea8231d109b9d41c)) +* import export all worker groups config ([#3667](https://github.com/windmill-labs/windmill/issues/3667)) ([c1a4a82](https://github.com/windmill-labs/windmill/commit/c1a4a8284387f381a5882b1fb6ebd73c9ec6b1ba)) +* local typescript codebase as bundle ([#3694](https://github.com/windmill-labs/windmill/issues/3694)) ([11b3ea3](https://github.com/windmill-labs/windmill/commit/11b3ea3ac86a34ab1beaab98ea43f6a64879f0fb)) + + +### Bug Fixes + +* can cancel only if can disable schedule + stop worker on force cancel + soft cancel job parent on job cancel ([#3670](https://github.com/windmill-labs/windmill/issues/3670)) ([010662d](https://github.com/windmill-labs/windmill/commit/010662d3d0d05a7f6167ceeb592c97cec4c9e804)) +* **frontend:** Fix subflow viewer ([#3674](https://github.com/windmill-labs/windmill/issues/3674)) ([47ee6c9](https://github.com/windmill-labs/windmill/commit/47ee6c90998840889c44df28a25967cef6c8bd59)) +* improve conditional wrapper for app editor ([ea4165d](https://github.com/windmill-labs/windmill/commit/ea4165d58bb11fb4409242bb597f4b7514d1bd60)) +* load large args of previous runs dynamically ([#3688](https://github.com/windmill-labs/windmill/issues/3688)) ([d1f58f1](https://github.com/windmill-labs/windmill/commit/d1f58f10371dfce5e037f03b43aad9f530d7ae6a)) + ## [1.323.6](https://github.com/windmill-labs/windmill/compare/v1.323.5...v1.323.6) (2024-05-02) diff --git a/Dockerfile b/Dockerfile index e4a970fb17..25061e7295 100644 --- a/Dockerfile +++ b/Dockerfile @@ -171,7 +171,7 @@ COPY --from=builder /windmill/target/release/windmill ${APP}/windmill COPY --from=downloader --chmod=755 /deno /usr/bin/deno -COPY --from=oven/bun:1.1.5 /usr/local/bin/bun /usr/bin/bun +COPY --from=oven/bun:1.1.7 /usr/local/bin/bun /usr/bin/bun # add the docker client to call docker from a worker if enabled COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/ diff --git a/README.md b/README.md index da45e240a5..53fbdfde3f 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,7 @@ you to have it being synced automatically everyday. | 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 | ## Run a local dev setup ### only Frontend diff --git a/backend/.sqlx/query-021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063.json b/backend/.sqlx/query-021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063.json new file mode 100644 index 0000000000..688ab585e9 --- /dev/null +++ b/backend/.sqlx/query-021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false + ] + }, + "hash": "021be0f26ea87e587e656b24a9a94538efbf54a1447a3898e19773789cfc9063" +} diff --git a/backend/.sqlx/query-3e0e5e0076ae0f7771abd64f8c5bb7e003e3f85351c946a012037e0c874acf1a.json b/backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json similarity index 68% rename from backend/.sqlx/query-3e0e5e0076ae0f7771abd64f8c5bb7e003e3f85351c946a012037e0c874acf1a.json rename to backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json index e0ddf86e7d..99f3606bcd 100644 --- a/backend/.sqlx/query-3e0e5e0076ae0f7771abd64f8c5bb7e003e3f85351c946a012037e0c874acf1a.json +++ b/backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT tag, count(*) as count FROM queue WHERE\n scheduled_for <= now() - ('3 seconds')::interval AND running = false\n GROUP BY tag", + "query": "SELECT tag, count(*) as count FROM queue WHERE\n scheduled_for <= now() - ('3 seconds')::interval AND running = false\n GROUP BY tag", "describe": { "columns": [ { @@ -22,5 +22,5 @@ null ] }, - "hash": "3e0e5e0076ae0f7771abd64f8c5bb7e003e3f85351c946a012037e0c874acf1a" + "hash": "02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49" } diff --git a/backend/.sqlx/query-3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9.json b/backend/.sqlx/query-3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9.json new file mode 100644 index 0000000000..099583c4cc --- /dev/null +++ b/backend/.sqlx/query-3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM metrics \n WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "3667d72d23e8c35ab96d5e5d1fbfd94a8a9f74b6398e21f13c206c3f9427c6a9" +} diff --git a/backend/.sqlx/query-4887db074e9058bc0c1887428d6b2a897fe4b30c83f2aa5e9464c156f08f1c3a.json b/backend/.sqlx/query-4887db074e9058bc0c1887428d6b2a897fe4b30c83f2aa5e9464c156f08f1c3a.json new file mode 100644 index 0000000000..2988f2c0e5 --- /dev/null +++ b/backend/.sqlx/query-4887db074e9058bc0c1887428d6b2a897fe4b30c83f2aa5e9464c156f08f1c3a.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT worker, worker_instance, vcpus, memory FROM worker_ping WHERE ping_at > NOW() - INTERVAL '2 minutes'", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "worker", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "worker_instance", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "vcpus", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "memory", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + true, + true + ] + }, + "hash": "4887db074e9058bc0c1887428d6b2a897fe4b30c83f2aa5e9464c156f08f1c3a" +} diff --git a/backend/.sqlx/query-47beea5cd6324b53bfb349665fb215280f32b70a617fde87f70ea53ca9ade39f.json b/backend/.sqlx/query-54fef88cc6b9e8db7c07fccbaa845edfefa339153a32e468faad9f063008863d.json similarity index 54% rename from backend/.sqlx/query-47beea5cd6324b53bfb349665fb215280f32b70a617fde87f70ea53ca9ade39f.json rename to backend/.sqlx/query-54fef88cc6b9e8db7c07fccbaa845edfefa339153a32e468faad9f063008863d.json index 59f45509b9..3c529dbd84 100644 --- a/backend/.sqlx/query-47beea5cd6324b53bfb349665fb215280f32b70a617fde87f70ea53ca9ade39f.json +++ b/backend/.sqlx/query-54fef88cc6b9e8db7c07fccbaa845edfefa339153a32e468faad9f063008863d.json @@ -1,16 +1,17 @@ { "db_name": "PostgreSQL", - "query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2 WHERE worker = $3", + "query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2, occupancy_rate = $3, current_job_id = NULL, current_job_workspace_id = NULL WHERE worker = $4", "describe": { "columns": [], "parameters": { "Left": [ "Int4", "TextArray", + "Float4", "Text" ] }, "nullable": [] }, - "hash": "47beea5cd6324b53bfb349665fb215280f32b70a617fde87f70ea53ca9ade39f" + "hash": "54fef88cc6b9e8db7c07fccbaa845edfefa339153a32e468faad9f063008863d" } diff --git a/backend/.sqlx/query-5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296.json b/backend/.sqlx/query-5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296.json new file mode 100644 index 0000000000..f1d7a93c74 --- /dev/null +++ b/backend/.sqlx/query-5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "5ce9a9e0669b299b998bc9080d24f8d6f56eb049bec2e0745d27ca3a4fc0c296" +} diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 75b8108281..1fa370e682 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "?column?", + "name": "bool", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json b/backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json new file mode 100644 index 0000000000..0bbe880941 --- /dev/null +++ b/backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "TextArray", + "Varchar", + "Varchar", + "Varchar", + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153" +} diff --git a/backend/.sqlx/query-7b609ed87f974dfe887778746230b40b485658a75a1186c6ebb4c2de1b52a2fa.json b/backend/.sqlx/query-7b609ed87f974dfe887778746230b40b485658a75a1186c6ebb4c2de1b52a2fa.json deleted file mode 100644 index f60e074866..0000000000 --- a/backend/.sqlx/query-7b609ed87f974dfe887778746230b40b485658a75a1186c6ebb4c2de1b52a2fa.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "TextArray", - "Varchar", - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "7b609ed87f974dfe887778746230b40b485658a75a1186c6ebb4c2de1b52a2fa" -} diff --git a/backend/.sqlx/query-7c95f3652de4561b18c1a340db3e0113a4777d38de4468fc41b22ce5b6fd1db7.json b/backend/.sqlx/query-7c95f3652de4561b18c1a340db3e0113a4777d38de4468fc41b22ce5b6fd1db7.json new file mode 100644 index 0000000000..7222da038c --- /dev/null +++ b/backend/.sqlx/query-7c95f3652de4561b18c1a340db3e0113a4777d38de4468fc41b22ce5b6fd1db7.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH queue_metrics as (\n SELECT id, value, created_at\n FROM metrics\n WHERE id LIKE 'queue_%'\n AND created_at > now() - interval '14 day'\n ORDER BY created_at ASC\n )\n SELECT id, array_agg(json_build_object('value', value, 'created_at', created_at)) as \"values!\"\n FROM queue_metrics\n GROUP BY id\n ORDER BY id ASC", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "values!", + "type_info": "JsonArray" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + null + ] + }, + "hash": "7c95f3652de4561b18c1a340db3e0113a4777d38de4468fc41b22ce5b6fd1db7" +} diff --git a/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json b/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json new file mode 100644 index 0000000000..c7a6d92bbc --- /dev/null +++ b/backend/.sqlx/query-7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM metrics WHERE id LIKE 'queue_%' AND created_at < NOW() - INTERVAL '14 day'", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "7c9a464ac807051b99fe37f2078f1b17f824e6d9b1124db618855a15a98e31f6" +} diff --git a/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json b/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json new file mode 100644 index 0000000000..f98f3fdeea --- /dev/null +++ b/backend/.sqlx/query-82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT created_at FROM metrics WHERE id LIKE 'queue_count_%' ORDER BY created_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false + ] + }, + "hash": "82f6674f19e8ad51a992505a46f46fc4a48172f104e9e849f755ac041c3eef92" +} diff --git a/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json b/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json new file mode 100644 index 0000000000..124a6e36c5 --- /dev/null +++ b/backend/.sqlx/query-8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO metrics (id, value) VALUES ($1, $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "8824b382c4e98dfa17b4aa656af3a6c1ff99973e778d71bd598a50d022da8f15" +} diff --git a/backend/.sqlx/query-8c62e3bb264c7336b3b3a68677993ba3b729c7358cc3da14513d8b447ce65aaf.json b/backend/.sqlx/query-8c62e3bb264c7336b3b3a68677993ba3b729c7358cc3da14513d8b447ce65aaf.json new file mode 100644 index 0000000000..e4a08130cf --- /dev/null +++ b/backend/.sqlx/query-8c62e3bb264c7336b3b3a68677993ba3b729c7358cc3da14513d8b447ce65aaf.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2 WHERE worker = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar", + "Text" + ] + }, + "nullable": [] + }, + "hash": "8c62e3bb264c7336b3b3a68677993ba3b729c7358cc3da14513d8b447ce65aaf" +} diff --git a/backend/.sqlx/query-92d00c6a1f4c40f2a23c9ba758a59597deabd7ca93653ad72d2cdc37efefc9d4.json b/backend/.sqlx/query-8e7ff45c5378c3a3406ba94dc0653afa5d28c072203617c598caefaaf1bafcfb.json similarity index 85% rename from backend/.sqlx/query-92d00c6a1f4c40f2a23c9ba758a59597deabd7ca93653ad72d2cdc37efefc9d4.json rename to backend/.sqlx/query-8e7ff45c5378c3a3406ba94dc0653afa5d28c072203617c598caefaaf1bafcfb.json index a49e26981a..e1cdc4e0c7 100644 --- a/backend/.sqlx/query-92d00c6a1f4c40f2a23c9ba758a59597deabd7ca93653ad72d2cdc37efefc9d4.json +++ b/backend/.sqlx/query-8e7ff45c5378c3a3406ba94dc0653afa5d28c072203617c598caefaaf1bafcfb.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)", + "query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30)", "describe": { "columns": [], "parameters": { @@ -67,10 +67,11 @@ "Int4", "Varchar", "Bool", - "Bool" + "Bool", + "Varchar" ] }, "nullable": [] }, - "hash": "92d00c6a1f4c40f2a23c9ba758a59597deabd7ca93653ad72d2cdc37efefc9d4" + "hash": "8e7ff45c5378c3a3406ba94dc0653afa5d28c072203617c598caefaaf1bafcfb" } diff --git a/backend/.sqlx/query-9bf41c3161a02b7d0731c4e1d79519cef5255f5df1b759af3aa4985bb64313e5.json b/backend/.sqlx/query-9bf41c3161a02b7d0731c4e1d79519cef5255f5df1b759af3aa4985bb64313e5.json new file mode 100644 index 0000000000..2eec9fba30 --- /dev/null +++ b/backend/.sqlx/query-9bf41c3161a02b7d0731c4e1d79519cef5255f5df1b759af3aa4985bb64313e5.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO metrics (id, value)\n VALUES ($1, to_jsonb((SELECT EXTRACT(EPOCH FROM now() - scheduled_for)\n FROM queue WHERE tag = $2 AND running = false AND scheduled_for <= now() - ('3 seconds')::interval\n ORDER BY priority DESC NULLS LAST, scheduled_for, created_at LIMIT 1)))", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Text" + ] + }, + "nullable": [] + }, + "hash": "9bf41c3161a02b7d0731c4e1d79519cef5255f5df1b759af3aa4985bb64313e5" +} diff --git a/backend/.sqlx/query-bbd5f968d7b62a55a7ebf7b98cfd411678ce544fdb9472d8d223235fb2818aaf.json b/backend/.sqlx/query-bbd5f968d7b62a55a7ebf7b98cfd411678ce544fdb9472d8d223235fb2818aaf.json new file mode 100644 index 0000000000..c7d0d9b11f --- /dev/null +++ b/backend/.sqlx/query-bbd5f968d7b62a55a7ebf7b98cfd411678ce544fdb9472d8d223235fb2818aaf.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT args FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT args FROM input WHERE id = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "args", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "bbd5f968d7b62a55a7ebf7b98cfd411678ce544fdb9472d8d223235fb2818aaf" +} diff --git a/backend/.sqlx/query-c584aeec21716f7405c355d5684ff3ac1be3f70849d67b8c2d4787e809b7ea2b.json b/backend/.sqlx/query-c584aeec21716f7405c355d5684ff3ac1be3f70849d67b8c2d4787e809b7ea2b.json new file mode 100644 index 0000000000..4f2a5aaf87 --- /dev/null +++ b/backend/.sqlx/query-c584aeec21716f7405c355d5684ff3ac1be3f70849d67b8c2d4787e809b7ea2b.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH all_users as (SELECT count(*)::INT as count FROM usr WHERE disabled IS false),\n authors as (SELECT count(distinct email)::INT as count FROM usr WHERE usr.operator IS false AND disabled IS false)\n SELECT authors.count as author_count, all_users.count - authors.count as operator_count FROM all_users, authors", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "author_count", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "operator_count", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + null + ] + }, + "hash": "c584aeec21716f7405c355d5684ff3ac1be3f70849d67b8c2d4787e809b7ea2b" +} diff --git a/backend/.sqlx/query-c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4.json b/backend/.sqlx/query-c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4.json new file mode 100644 index 0000000000..370a3a60ef --- /dev/null +++ b/backend/.sqlx/query-c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "c6b2791dd109c7bf40a40b557d9d5a140e70c49718fcb3a776b77f8f2ab901d4" +} diff --git a/backend/.sqlx/query-c9d97800eb0ec87df8e8959b283dacb2c6cce422365ed394375641488ceb6b65.json b/backend/.sqlx/query-c9d97800eb0ec87df8e8959b283dacb2c6cce422365ed394375641488ceb6b65.json deleted file mode 100644 index 3ca26718b0..0000000000 --- a/backend/.sqlx/query-c9d97800eb0ec87df8e8959b283dacb2c6cce422365ed394375641488ceb6b65.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE worker_ping SET ping_at = now() WHERE worker = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "c9d97800eb0ec87df8e8959b283dacb2c6cce422365ed394375641488ceb6b65" -} diff --git a/backend/.sqlx/query-b38044d94e2ab03167c2f6fbb553ab3c19930ed11abf51763cd3ee378229443d.json b/backend/.sqlx/query-e00171cc3fc8f32922562d4fc4d3db56955fbc6ee9872d4b2e99157ebed131e0.json similarity index 61% rename from backend/.sqlx/query-b38044d94e2ab03167c2f6fbb553ab3c19930ed11abf51763cd3ee378229443d.json rename to backend/.sqlx/query-e00171cc3fc8f32922562d4fc4d3db56955fbc6ee9872d4b2e99157ebed131e0.json index 6f3b508471..67aec442e1 100644 --- a/backend/.sqlx/query-b38044d94e2ab03167c2f6fbb553ab3c19930ed11abf51763cd3ee378229443d.json +++ b/backend/.sqlx/query-e00171cc3fc8f32922562d4fc4d3db56955fbc6ee9872d4b2e99157ebed131e0.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT worker, worker_instance, EXTRACT(EPOCH FROM (now() - ping_at))::integer as last_ping, started_at, ip, jobs_executed, custom_tags, worker_group, wm_version FROM worker_ping\n WHERE ($1::integer IS NULL AND ping_at > now() - interval '5 minute') OR (ping_at > now() - ($1 || ' seconds')::interval)\n ORDER BY ping_at desc LIMIT $2 OFFSET $3", + "query": "SELECT worker, worker_instance, EXTRACT(EPOCH FROM (now() - ping_at))::integer as last_ping, started_at, ip, jobs_executed, CASE WHEN $4 IS TRUE THEN current_job_id ELSE NULL END as current_job_id, CASE WHEN $4 IS TRUE THEN current_job_workspace_id ELSE NULL END as current_job_workspace_id, custom_tags, worker_group, wm_version, occupancy_rate\n FROM worker_ping\n WHERE ($1::integer IS NULL AND ping_at > now() - interval '5 minute') OR (ping_at > now() - ($1 || ' seconds')::interval)\n ORDER BY ping_at desc LIMIT $2 OFFSET $3", "describe": { "columns": [ { @@ -35,25 +35,41 @@ }, { "ordinal": 6, - "name": "custom_tags", - "type_info": "TextArray" + "name": "current_job_id", + "type_info": "Uuid" }, { "ordinal": 7, - "name": "worker_group", + "name": "current_job_workspace_id", "type_info": "Varchar" }, { "ordinal": 8, + "name": "custom_tags", + "type_info": "TextArray" + }, + { + "ordinal": 9, + "name": "worker_group", + "type_info": "Varchar" + }, + { + "ordinal": 10, "name": "wm_version", "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "occupancy_rate", + "type_info": "Float4" } ], "parameters": { "Left": [ "Int4", "Int8", - "Int8" + "Int8", + "Bool" ] }, "nullable": [ @@ -63,10 +79,13 @@ false, false, false, + null, + null, true, false, - false + false, + true ] }, - "hash": "b38044d94e2ab03167c2f6fbb553ab3c19930ed11abf51763cd3ee378229443d" + "hash": "e00171cc3fc8f32922562d4fc4d3db56955fbc6ee9872d4b2e99157ebed131e0" } diff --git a/backend/Cargo.lock b/backend/Cargo.lock index c95e0dac69..1f38d2fdf5 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -56,7 +56,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -68,7 +68,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -121,47 +121,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -169,9 +170,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "archiver-rs" @@ -234,7 +235,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -250,9 +251,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a" dependencies = [ "concurrent-queue", "event-listener 5.3.0", @@ -280,11 +281,11 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9eabd7a98fe442131a17c316bd9349c43695e49e730c3c8e12cfb5f4da2693" +checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498" dependencies = [ - "brotli 5.0.0", + "brotli 6.0.0", "flate2", "futures-core", "memchr", @@ -311,7 +312,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.1", + "async-channel 2.3.0", "async-executor", "async-io 2.3.2", "async-lock 3.3.0", @@ -406,7 +407,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -454,7 +455,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -496,7 +497,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -550,15 +551,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaa0be6ee7d90b775ae6ccb6d2ba182b91219ec2001f92338773a094246af1d" +checksum = "40ddbfb5db93d62521f47b3f223da0884a2f02741ff54cb9cda192a0e73ba08b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -599,9 +600,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4963ac9ff2d33a4231b3806c1c69f578f221a9cabb89ad2bde62ce2b442c8a7" +checksum = "75588e7ee5e8496eed939adac2035a6dbab9f7eb2acdd9ab2d31856dab6f3955" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -622,9 +623,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3d6c4cba4e009391b72b0fcf12aff04ea3c9c3aa2ecaafa330326a8bd7e601" +checksum = "5fd6a9b38fe2dcaa2422b42e2c667112872d03a83522533f8b4165fd2d9d4bd1" dependencies = [ "aws-credential-types", "aws-runtime", @@ -644,9 +645,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.22.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73400dc239d14f63d932f4ca7b55af5e9ef1f857f7d70655249ccc287adb2570" +checksum = "c99f342a364c8490b7715387244d2f7ebfc05f07bb6a0fc6e70b8e62b7078d06" dependencies = [ "aws-credential-types", "aws-runtime", @@ -666,9 +667,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f8858308af76fba3e5ffcf1bb56af5471574d2bdfaf0159470c25bc2f760e5" +checksum = "7207ca62206c93e470457babf0512d7b6b97170fdba929a2a2f5682f9f68407c" dependencies = [ "aws-credential-types", "aws-runtime", @@ -762,9 +763,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf64e73ef8d4dac6c933230d56d136b75b252edcf82ed36e37d603090cd7348" +checksum = "c9ac79e9f3a4d576f3cd4a470a0275b138d9e7b11b1cd514a6858ae0a79dd5bb" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -788,9 +789,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c19fdae6e3d5ac9cd01f2d6e6c359c5f5a3e028c2d148a8f5b90bf3399a18a7" +checksum = "04ec42c2f5c0e7796a2848dde4d9f3bf8ce12ccbb3d5aa40c52fa0cdd61a1c47" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -805,9 +806,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729" +checksum = "baf98d97bba6ddaba180f1b1147e202d8fe04940403a95a3f826c790f931bbd1" dependencies = [ "base64-simd 0.8.0", "bytes", @@ -840,9 +841,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a43b56df2c529fe44cb4d92bd64d0479883fb9608ff62daede4df5405381814" +checksum = "a807d90cd50a969b3d95e4e7ad1491fcae13c6e83948d8728363ecc09d66343a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -872,6 +873,7 @@ dependencies = [ "matchit", "memchr", "mime", + "multer", "percent-encoding", "pin-project-lite", "rustversion", @@ -1040,13 +1042,13 @@ dependencies = [ "lazy_static", "lazycell", "log", - "prettyplease 0.2.19", + "prettyplease 0.2.20", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.60", + "syn 2.0.63", "which 4.4.2", ] @@ -1142,7 +1144,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ - "async-channel 2.2.1", + "async-channel 2.3.0", "async-lock 3.3.0", "async-task", "futures-io", @@ -1170,7 +1172,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "syn_derive", ] @@ -1187,9 +1189,9 @@ dependencies = [ [[package]] name = "brotli" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19483b140a7ac7174d34b5a581b406c64f84da5409d3e09cf4fff604f9270e67" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1280,7 +1282,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1387,7 +1389,7 @@ version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72" dependencies = [ - "semver 1.0.22", + "semver 1.0.23", "serde", "toml 0.7.8", "url", @@ -1395,9 +1397,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", @@ -1545,7 +1547,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1565,9 +1567,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "combine" @@ -1908,7 +1910,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1956,7 +1958,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -1978,7 +1980,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2163,7 +2165,7 @@ dependencies = [ "quote", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.60", + "syn 2.0.63", "thiserror", ] @@ -2412,7 +2414,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2563,7 +2565,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2575,7 +2577,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2595,7 +2597,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2606,9 +2608,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2733,9 +2735,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" @@ -2821,7 +2823,7 @@ checksum = "3a0b11eeb173ce52f84ebd943d42e58813a2ebb78a6a3ff0a243b71c5199cd7b" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2849,7 +2851,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2861,7 +2863,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2873,7 +2875,7 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -2987,7 +2989,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -3206,9 +3208,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -3240,7 +3242,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -3807,9 +3809,15 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -4332,9 +4340,9 @@ dependencies = [ [[package]] name = "monostate" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20fffcd8ca4c69d31e036a71abc400147b41f90895df4edcb36497a1f8af8bf" +checksum = "0d208407d7552cd041d8cdb69a1bc3303e029c598738177a3d87082004dc0e1e" dependencies = [ "monostate-impl", "serde", @@ -4342,13 +4350,30 @@ dependencies = [ [[package]] name = "monostate-impl" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf307cbbbd777a9c10cec88ddafee572b3484caad5cce0c9236523c3803105a6" +checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin 0.9.8", + "version_check", ] [[package]] @@ -4386,7 +4411,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "termcolor", "thiserror", ] @@ -4539,11 +4564,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", "rand 0.8.5", @@ -4569,9 +4593,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "bytemuck", "num-traits", @@ -4594,9 +4618,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -4605,9 +4629,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -4637,7 +4661,7 @@ checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" dependencies = [ "base64 0.13.1", "chrono", - "getrandom 0.2.14", + "getrandom 0.2.15", "http 0.2.12", "rand 0.8.5", "reqwest 0.11.20", @@ -4788,7 +4812,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -4938,9 +4962,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -5061,7 +5085,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5090,7 +5114,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5107,9 +5131,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -5188,7 +5212,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5197,7 +5221,7 @@ version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f01006048a264047d6cba081fed8e11adbd69c15956f9e53185a9ac4a541853c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "polars-arrow", "polars-core", "polars-error", @@ -5229,7 +5253,7 @@ dependencies = [ "fast-float", "foreign_vec", "futures", - "getrandom 0.2.14", + "getrandom 0.2.15", "hashbrown 0.14.5", "itoa", "itoap", @@ -5678,12 +5702,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5736,7 +5760,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5748,14 +5772,14 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -5809,7 +5833,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "syn 2.0.60", + "syn 2.0.63", "thiserror", "typify", "unicode-ident", @@ -5829,7 +5853,7 @@ dependencies = [ "serde_json", "serde_tokenstream", "serde_yaml", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -5840,9 +5864,9 @@ checksum = "8bccbff07d5ed689c4087d20d7307a52ab6141edeedf487c3876a55b86cf63df" [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", @@ -6037,7 +6061,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -6169,7 +6193,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -6255,7 +6279,7 @@ version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "async-compression 0.4.9", + "async-compression 0.4.10", "base64 0.21.7", "bytes", "encoding_rs", @@ -6379,7 +6403,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -6491,7 +6515,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.60", + "syn 2.0.63", "walkdir", ] @@ -6524,9 +6548,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -6549,7 +6573,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver 1.0.23", ] [[package]] @@ -6651,9 +6675,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-tokio-stream" @@ -6743,15 +6767,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "ryu-js" @@ -6822,9 +6846,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" +checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" dependencies = [ "chrono", "dyn-clone", @@ -6836,14 +6860,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" +checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -6890,11 +6914,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -6903,9 +6927,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -6922,9 +6946,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -6943,9 +6967,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -6984,13 +7008,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7001,14 +7025,14 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "indexmap 2.2.6", "itoa", @@ -7075,7 +7099,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7132,7 +7156,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7752,7 +7776,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7803,7 +7827,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7816,7 +7840,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7910,7 +7934,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -7959,7 +7983,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8044,7 +8068,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8148,7 +8172,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8159,7 +8183,7 @@ checksum = "50176cfc1cbc8bb22f41c6fe9d1ec53fbe057001219b5954961b8ad0f336fce9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8183,7 +8207,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8199,9 +8223,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -8217,7 +8241,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8240,7 +8264,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8259,9 +8283,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.11" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" dependencies = [ "cfg-if", "core-foundation-sys", @@ -8347,22 +8371,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8494,7 +8518,7 @@ dependencies = [ "clap", "derive_builder", "esaxx-rs", - "getrandom 0.2.14", + "getrandom 0.2.15", "indicatif", "itertools 0.11.0", "lazy_static", @@ -8545,7 +8569,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -8656,9 +8680,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -8666,7 +8690,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -8736,7 +8759,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -8748,7 +8771,7 @@ dependencies = [ "home", "once_cell", "regex", - "semver 1.0.22", + "semver 1.0.23", "walkdir", ] @@ -8834,7 +8857,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -9004,7 +9027,7 @@ dependencies = [ "regress", "schemars", "serde_json", - "syn 2.0.60", + "syn 2.0.63", "thiserror", "unicode-ident", ] @@ -9021,7 +9044,7 @@ dependencies = [ "serde", "serde_json", "serde_tokenstream", - "syn 2.0.60", + "syn 2.0.63", "typify-impl", ] @@ -9031,7 +9054,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "rand 0.8.5", "uuid 1.8.0", "web-time", @@ -9288,7 +9311,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -9297,7 +9320,7 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "serde", ] @@ -9345,9 +9368,9 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" @@ -9407,7 +9430,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -9441,7 +9464,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9474,7 +9497,7 @@ checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -9616,7 +9639,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "axum", @@ -9653,7 +9676,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "argon2", @@ -9729,7 +9752,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.323.6" +version = "1.327.0" dependencies = [ "base64 0.21.7", "chrono", @@ -9747,7 +9770,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.323.6" +version = "1.327.0" dependencies = [ "chrono", "serde", @@ -9760,12 +9783,13 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "aws-config", "aws-sdk-sts", "axum", + "bytes", "chrono", "cron", "git-version", @@ -9797,7 +9821,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.323.6" +version = "1.327.0" dependencies = [ "regex", "rsmq_async", @@ -9812,7 +9836,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.323.6" +version = "1.327.0" dependencies = [ "serde", "serde_json", @@ -9820,7 +9844,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "lazy_static", @@ -9831,7 +9855,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "gosyn", @@ -9843,7 +9867,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "lazy_static", @@ -9854,7 +9878,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "itertools 0.12.1", @@ -9865,7 +9889,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "async-recursion", @@ -9882,7 +9906,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "lazy_static", @@ -9893,7 +9917,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -9911,10 +9935,10 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", - "getrandom 0.2.14", + "getrandom 0.2.15", "serde_json", "wasm-bindgen", "wasm-bindgen-test", @@ -9929,7 +9953,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "async-recursion", @@ -9962,7 +9986,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.323.6" +version = "1.327.0" dependencies = [ "serde", "wasm-bindgen", @@ -9973,7 +9997,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.323.6" +version = "1.327.0" dependencies = [ "anyhow", "async-recursion", @@ -10208,9 +10232,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -10296,28 +10320,28 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", ] [[package]] @@ -10337,7 +10361,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.63", "synstructure", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index e3b09d1837..b58d5ff007 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.323.6" +version = "1.327.0" authors.workspace = true edition.workspace = true @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "1.323.6" +version = "1.327.0" authors = ["Ruben Fiszel "] edition = "2021" @@ -109,7 +109,7 @@ windmill-parser-sql = { path = "./parsers/windmill-parser-sql" } windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" } windmill-api-client = { path = "./windmill-api-client" } -axum = { version = "^0.7" } +axum = { version = "^0.7", features = ["multipart"] } headers = "^0" hyper = { version = "^1", features = ["full"] } tokio = { version = "^1", features = ["full", "tracing"] } diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 4160837664..1537671481 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -f59f9c1e55e5e93f9eb6c081847cc566611029d2 \ No newline at end of file +644b6f49f087790a728a7a0a82525997b1742738 \ No newline at end of file diff --git a/backend/migrations/20240430005030_add_codebases.down.sql b/backend/migrations/20240430005030_add_codebases.down.sql new file mode 100644 index 0000000000..81bb6123a2 --- /dev/null +++ b/backend/migrations/20240430005030_add_codebases.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE script DROP COLUMN codebase; \ No newline at end of file diff --git a/backend/migrations/20240430005030_add_codebases.up.sql b/backend/migrations/20240430005030_add_codebases.up.sql new file mode 100644 index 0000000000..13af236d16 --- /dev/null +++ b/backend/migrations/20240430005030_add_codebases.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here + +ALTER TABLE script ADD COLUMN codebase VARCHAR(255); \ No newline at end of file diff --git a/backend/migrations/20240506085935_add_worker_ping_metrics.down.sql b/backend/migrations/20240506085935_add_worker_ping_metrics.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20240506085935_add_worker_ping_metrics.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20240506085935_add_worker_ping_metrics.up.sql b/backend/migrations/20240506085935_add_worker_ping_metrics.up.sql new file mode 100644 index 0000000000..c3ca599593 --- /dev/null +++ b/backend/migrations/20240506085935_add_worker_ping_metrics.up.sql @@ -0,0 +1,7 @@ +-- Add up migration script here +ALTER TABLE worker_ping +ADD COLUMN current_job_id UUID, +ADD COLUMN current_job_workspace_id VARCHAR(50), +ADD COLUMN vcpus BIGINT, +ADD COLUMN memory BIGINT, +ADD COLUMN occupancy_rate REAL; \ No newline at end of file diff --git a/backend/migrations/20240506114033_add_metrics_table.down.sql b/backend/migrations/20240506114033_add_metrics_table.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20240506114033_add_metrics_table.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20240506114033_add_metrics_table.up.sql b/backend/migrations/20240506114033_add_metrics_table.up.sql new file mode 100644 index 0000000000..f0d40e0772 --- /dev/null +++ b/backend/migrations/20240506114033_add_metrics_table.up.sql @@ -0,0 +1,8 @@ +-- Add up migration script here +CREATE TABLE metrics ( + id VARCHAR(255) NOT NULL, + value JSONB NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL +); +CREATE INDEX metrics_key_idx ON metrics(id); +CREATE INDEX metrics_sort_idx ON metrics(created_at DESC); \ No newline at end of file diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 0fbe4375b6..98976153a8 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -8,7 +8,7 @@ use std::{ }; use rsmq_async::MultiplexedRsmq; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Serialize}; use sqlx::{Pool, Postgres}; use tokio::{ join, @@ -678,39 +678,182 @@ pub async fn monitor_db( }; let expose_queue_metrics_f = async { - if !initial_load - && METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed) - && server_mode - { + if !initial_load && server_mode { expose_queue_metrics(&db).await; } }; + + let save_usage_metrics_f = async { + if !initial_load && server_mode { + save_usage_metrics(&db).await; + } + }; + join!( expired_items_f, zombie_jobs_f, expose_queue_metrics_f, + save_usage_metrics_f, verify_license_key_f ); } pub async fn expose_queue_metrics(db: &Pool) { - let queue_counts = sqlx::query!( - "SELECT tag, count(*) as count FROM queue WHERE - scheduled_for <= now() - ('3 seconds')::interval AND running = false - GROUP BY tag" - ) - .fetch_all(db) - .await - .ok() - .unwrap_or_else(|| vec![]); - for q in queue_counts { - let count = q.count.unwrap_or(0); - let tag = q.tag; - let metric = (*QUEUE_COUNT).with_label_values(&[&tag]); - metric.set(count as i64); + let tx = db.begin().await; + if let Ok(mut tx) = tx { + let last_check = sqlx::query_scalar!( + "SELECT created_at FROM metrics WHERE id LIKE 'queue_count_%' ORDER BY created_at DESC LIMIT 1" + ) + .fetch_optional(db) + .await + .unwrap_or(Some(chrono::Utc::now())); + + let metrics_enabled = METRICS_ENABLED.load(std::sync::atomic::Ordering::Relaxed); + let save_metrics = last_check + .map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::seconds(25)) + .unwrap_or(true); + + if metrics_enabled || save_metrics { + + let queue_counts = sqlx::query!( + "SELECT tag, count(*) as count FROM queue WHERE + scheduled_for <= now() - ('3 seconds')::interval AND running = false + GROUP BY tag" + ) + .fetch_all(&mut *tx) + .await + .ok() + .unwrap_or_else(|| vec![]); + + for q in queue_counts { + let count = q.count.unwrap_or(0); + let tag = q.tag; + if metrics_enabled { + let metric = (*QUEUE_COUNT).with_label_values(&[&tag]); + metric.set(count as i64); + } + + // save queue_count and delay metrics per tag + if save_metrics { + sqlx::query!( + "INSERT INTO metrics (id, value) VALUES ($1, $2)", + format!("queue_count_{}", tag), + serde_json::json!(count) + ) + .execute(&mut *tx) + .await + .ok(); + if count > 0 { + sqlx::query!( + "INSERT INTO metrics (id, value) + VALUES ($1, to_jsonb((SELECT EXTRACT(EPOCH FROM now() - scheduled_for) + FROM queue WHERE tag = $2 AND running = false AND scheduled_for <= now() - ('3 seconds')::interval + ORDER BY priority DESC NULLS LAST, scheduled_for, created_at LIMIT 1)))", + format!("queue_delay_{}", tag), + tag + ).execute(&mut *tx).await.ok(); + } + } + } + } + + // clean queue metrics older than 14 days + sqlx::query!( + "DELETE FROM metrics WHERE id LIKE 'queue_%' AND created_at < NOW() - INTERVAL '14 day'" + ).execute(&mut *tx).await.ok(); + + tx.commit().await.ok(); } } + +#[derive(Serialize)] +struct WorkerUsage { + worker: String, + worker_instance: String, + vcpus: Option, + memory: Option, + +} + +pub async fn save_usage_metrics(db: &Pool) { + let tx = db.begin().await; + + if let Ok(mut tx) = tx { + let last_check = sqlx::query_scalar!( + "SELECT created_at FROM metrics WHERE id = 'author_count' ORDER BY created_at DESC LIMIT 1" + ) + .fetch_optional(db) + .await + .unwrap_or(Some(chrono::Utc::now())); + + let random_nb = rand::random::(); + + // save author and operator count every ~24 hours + if last_check + .map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::hours(24) - chrono::Duration::minutes(random_nb % 60)) + .unwrap_or(true) + { + let counts = sqlx::query!( + "WITH all_users as (SELECT count(*)::INT as count FROM usr WHERE disabled IS false), + authors as (SELECT count(distinct email)::INT as count FROM usr WHERE usr.operator IS false AND disabled IS false) + SELECT authors.count as author_count, all_users.count - authors.count as operator_count FROM all_users, authors" + ) + .fetch_one(&mut *tx) + .await + .ok(); + + if let Some(counts) = counts { + sqlx::query!( + "INSERT INTO metrics (id, value) VALUES ('author_count', $1), ('operator_count', $2)", + serde_json::json!(counts.author_count), + serde_json::json!(counts.operator_count) + ) + .execute(&mut *tx) + .await + .ok(); + } + + + // clean metrics older than 6 months (including worker usage) + sqlx::query!( + "DELETE FROM metrics + WHERE (id = 'author_count' OR id = 'operator_count' OR id = 'worker_usage') AND created_at < NOW() - INTERVAL '6 month'" + ) + .execute(&mut *tx) + .await + .ok(); + } + + // save worker usage every ~60 minutes + if last_check + .map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::minutes(60) - chrono::Duration::seconds(random_nb % 300)) + .unwrap_or(true) + { + let worker_usage = sqlx::query_as!( + WorkerUsage, + "SELECT worker, worker_instance, vcpus, memory FROM worker_ping WHERE ping_at > NOW() - INTERVAL '2 minutes'" + ) + .fetch_all(&mut *tx) + .await + .ok(); + + if let Some(worker_usage) = worker_usage { + sqlx::query!( + "INSERT INTO metrics (id, value) VALUES ('worker_usage', $1)", + serde_json::json!(worker_usage) + ) + .execute(&mut *tx) + .await + .ok(); + } + } + + tx.commit().await.ok(); + } + +} + pub async fn reload_server_config(db: &Pool) { let config = load_server_config(&db).await; if let Err(e) = config { diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 026e6bfd0e..84bbf97c6f 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -1663,6 +1663,7 @@ func main(derp string) (string, error) { .to_owned(); let result = RunJob::from(JobPayload::Code(RawCode { + hash: None, content, path: None, lock: None, @@ -1694,6 +1695,7 @@ echo "hello $msg" .to_owned(); let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, content, path: None, lock: None, @@ -1722,6 +1724,7 @@ def main(): .to_owned(); let job = JobPayload::Code(RawCode { + hash: None, content, path: None, language: ScriptLang::Python3, @@ -1756,6 +1759,7 @@ def main(): .to_owned(); let job = JobPayload::Code(RawCode { + hash: None, content, path: None, language: ScriptLang::Python3, @@ -1789,6 +1793,7 @@ def main(): .to_owned(); let job = JobPayload::Code(RawCode { + hash: None, content, path: None, language: ScriptLang::Python3, @@ -3165,6 +3170,7 @@ async fn run_deployed_relative_imports(db: &Pool, script_content: Stri concurrency_key: None, visible_to_runner_only: None, no_main_func: None, + codebase: None }, ).await.unwrap(); @@ -3212,6 +3218,7 @@ async fn run_preview_relative_imports(db: &Pool, script_content: Strin let db2 = db.clone(); in_test_worker(&db, async move { let job = RunJob::from(JobPayload::Code(RawCode { + hash: None, content: script_content, path: Some("f/system/test_import".to_string()), language, diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 6aa590959d..03539cf2b5 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.323.6 + version: 1.327.0 title: Windmill API contact: @@ -6959,6 +6959,40 @@ paths: schema: type: boolean + /workers/queue_metrics: + get: + summary: get queue metrics + operationId: getQueueMetrics + tags: + - worker + responses: + "200": + description: metrics + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: string + values: + type: array + items: + type: object + properties: + created_at: + type: string + value: + type: number + required: + - created_at + - value + required: + - id + - values + /configs/list_worker_groups: get: summary: list worker groups @@ -7271,6 +7305,26 @@ paths: items: $ref: "#/components/schemas/Input" + /w/{workspace}/inputs/{jobOrInputId}/args: + get: + summary: Get args from history or saved input + operationId: getArgsFromHistoryOrSavedInput + tags: + - input + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: jobOrInputId + in: path + required: true + schema: + type: string + responses: + "200": + description: args + content: + application/json: + schema: {} + /w/{workspace}/inputs/list: get: summary: List saved Inputs for a Runnable @@ -8390,6 +8444,9 @@ components: type: boolean no_main_func: type: boolean + codebase: + type: string + required: - hash - path @@ -8407,6 +8464,7 @@ components: - starred - no_main_func + NewScript: type: object properties: @@ -8481,6 +8539,8 @@ components: type: boolean no_main_func: type: boolean + codebase: + type: string required: - path - summary @@ -9678,6 +9738,12 @@ components: type: string wm_version: type: string + current_job_id: + type: string + current_job_workspace_id: + type: string + occupancy_rate: + type: number required: - worker - worker_instance diff --git a/backend/windmill-api/src/inputs.rs b/backend/windmill-api/src/inputs.rs index 3949bf3544..6427ea455e 100644 --- a/backend/windmill-api/src/inputs.rs +++ b/backend/windmill-api/src/inputs.rs @@ -25,7 +25,7 @@ use windmill_common::{ error::JsonResult, jobs::JobKind, scripts::to_i64, - utils::{paginate, Pagination}, + utils::{not_found_if_none, paginate, Pagination}, }; pub fn workspaced_service() -> Router { Router::new() @@ -34,6 +34,10 @@ pub fn workspaced_service() -> Router { .route("/create", post(create_input)) .route("/update", post(update_input)) .route("/delete/:id", post(delete_input)) + .route( + "/:job_or_input_id/args", + get(get_args_from_history_or_saved_input), + ) } #[derive(Debug, sqlx::FromRow, Serialize, Deserialize)] @@ -123,7 +127,7 @@ async fn get_input_history( let mut tx = user_db.begin(&authed).await?; let sql = &format!( - "select id, created_at, created_by, args, success from completed_job \ + "select id, created_at, created_by, CASE WHEN args is null or pg_column_size(args) < 40000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, success from completed_job \ where {} = $1 and job_kind = $2 and workspace_id = $3 \ order by created_at desc limit $4 offset $5", r.runnable_type.column_name() @@ -167,6 +171,27 @@ async fn get_input_history( Ok(Json(inputs)) } +async fn get_args_from_history_or_saved_input( + authed: ApiAuthed, + Extension(user_db): Extension, + Path((w_id, job_or_input_id)): Path<(String, Uuid)>, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + let result_o = sqlx::query_scalar!( + "SELECT args FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT args FROM input WHERE id = $1 AND workspace_id = $2", + job_or_input_id, + w_id + ) + .fetch_optional(&mut *tx) + .await?; + + tx.commit().await?; + + let result = not_found_if_none(result_o, "Input args", job_or_input_id.to_string())?; + + Ok(Json(result)) +} + async fn list_saved_inputs( authed: ApiAuthed, Extension(user_db): Extension, @@ -179,7 +204,7 @@ async fn list_saved_inputs( let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, InputRow>( - "select * from input \ + "select id, workspace_id, runnable_id, runnable_type, name, CASE WHEN pg_column_size(args) < 40000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, created_at, created_by, is_public from input \ where runnable_id = $1 and runnable_type = $2 and workspace_id = $3 \ and (is_public IS true OR created_by = $4) \ order by created_at desc limit $5 offset $6", diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 47a6f96905..fd1f6c8428 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -12,6 +12,7 @@ use serde_json::value::RawValue; use std::collections::HashMap; #[cfg(feature = "prometheus")] use std::sync::atomic::Ordering; +use tokio::io::AsyncReadExt; #[cfg(feature = "prometheus")] use tokio::time::Instant; use windmill_common::flow_status::{JobResult, RestartedFrom}; @@ -19,6 +20,10 @@ use windmill_common::jobs::{ format_completed_job_result, format_result, CompletedJobWithFormattedResult, FormattedResult, ENTRYPOINT_OVERRIDE, }; +use windmill_common::worker::TMP_DIR; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use windmill_common::scripts::PREVIEW_IS_CODEBASE_HASH; use windmill_common::variables::get_workspace_key; use crate::db::ApiAuthed; @@ -163,6 +168,7 @@ pub fn workspaced_service() -> Router { .layer(cors.clone()), ) .route("/run/preview", post(run_preview_script)) + .route("/run/preview_bundle", post(run_bundle_preview_script)) .route("/add_batch_jobs/:n", post(add_batch_jobs)) .route("/run/preview_flow", post(run_preview_flow_job)) .route( @@ -660,17 +666,17 @@ async fn get_job_internal( async fn get_logs_from_store( log_offset: i32, logs: &str, - log_file_index: Option>, + log_file_index: &Option>, ) -> Option> { if log_offset > 0 { - if let Some(file_index) = log_file_index { + if let Some(file_index) = log_file_index.clone() { tracing::debug!("Getting logs from store: {file_index:?}"); if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { tracing::debug!("object store client present, streaming from there"); let logs = logs.to_string(); let stream = async_stream::stream! { - for file_p in file_index { + for file_p in file_index.clone() { let file_p_2 = file_p.clone(); let file = os.get(&object_store::path::Path::from(file_p)).await; if let Ok(file) = file { @@ -692,6 +698,40 @@ async fn get_logs_from_store( } return None; } + +async fn get_logs_from_disk( + log_offset: i32, + logs: &str, + log_file_index: &Option>, +) -> Option> { + if log_offset > 0 { + if let Some(file_index) = log_file_index.clone() { + for file_p in &file_index { + if !tokio::fs::metadata(format!("{TMP_DIR}/{file_p}")) + .await + .is_ok() + { + return None; + } + } + + let logs = logs.to_string(); + let stream = async_stream::stream! { + for file_p in file_index.clone() { + let mut file = tokio::fs::File::open(format!("{TMP_DIR}/{file_p}")).await.map_err(to_anyhow)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).await.map_err(to_anyhow)?; + yield Ok(bytes::Bytes::from(buffer)) as anyhow::Result; + } + + yield Ok(bytes::Bytes::from(logs)) + }; + return Some(Ok(Body::from_stream(stream))); + } + } + return None; +} + async fn get_job_logs( Extension(db): Extension, Path((w_id, id)): Path<(String, Uuid)>, @@ -710,7 +750,11 @@ async fn get_job_logs( if let Some(record) = record { let logs = record.logs.unwrap_or_default(); #[cfg(all(feature = "enterprise", feature = "parquet"))] - if let Some(r) = get_logs_from_store(record.log_offset, &logs, record.log_file_index).await + if let Some(r) = get_logs_from_store(record.log_offset, &logs, &record.log_file_index).await + { + return r; + } + if let Some(r) = get_logs_from_disk(record.log_offset, &logs, &record.log_file_index).await { return r; } @@ -730,7 +774,10 @@ async fn get_job_logs( let logs = text.logs.unwrap_or_default(); #[cfg(all(feature = "enterprise", feature = "parquet"))] - if let Some(r) = get_logs_from_store(text.log_offset, &logs, text.log_file_index).await { + if let Some(r) = get_logs_from_store(text.log_offset, &logs, &text.log_file_index).await { + return r; + } + if let Some(r) = get_logs_from_disk(text.log_offset, &logs, &text.log_file_index).await { return r; } Ok(Body::from(logs)) @@ -2077,7 +2124,9 @@ enum PreviewKind { Identity, Http, Noop, + Bundle, } + #[derive(Deserialize)] struct Preview { content: Option, @@ -2420,6 +2469,7 @@ pub async fn run_workflow_as_code( let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind { JobKind::Preview => ( JobPayload::Code(RawCode { + hash: None, content: job.raw_code.unwrap_or_default(), path: job.script_path, language: job.language.unwrap_or_else(|| ScriptLang::Deno), @@ -2521,7 +2571,7 @@ async fn run_wait_result( w_id: String, node_id_for_empty_return: Option, ) -> error::Result { - let mut result; + let mut result = None; let timeout = SERVER_CONFIG.read().await.timeout_wait_result.clone(); let timeout_ms = if timeout <= 0 { 2000 @@ -2545,7 +2595,9 @@ async fn run_wait_result( ) .await .ok(); - } else { + } + + if result.is_none() { let row = sqlx::query( "SELECT result, language, flow_status FROM completed_job WHERE id = $1 AND workspace_id = $2", ) @@ -2563,8 +2615,6 @@ async fn run_wait_result( FormattedResult::RawValue(rv) => rv, FormattedResult::Vec(v) => Some(to_raw_value(&v)), }; - } else { - result = None; } } @@ -3083,6 +3133,7 @@ async fn run_preview_script( Some(PreviewKind::Identity) => JobPayload::Identity, Some(PreviewKind::Noop) => JobPayload::Noop, _ => JobPayload::Code(RawCode { + hash: None, content: preview.content.unwrap_or_default(), path: preview.path, language: preview.language.unwrap_or(ScriptLang::Deno), @@ -3117,6 +3168,136 @@ async fn run_preview_script( Ok((StatusCode::CREATED, uuid.to_string())) } +#[cfg(all(feature = "enterprise", feature = "parquet"))] +async fn run_bundle_preview_script( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Extension(rsmq): Extension>, + Path(w_id): Path, + Query(run_query): Query, + mut multipart: axum::extract::Multipart, +) -> error::Result<(StatusCode, String)> { + check_license_key_valid().await?; + + check_scopes(&authed, || format!("runscript"))?; + if authed.is_operator { + return Err(error::Error::NotAuthorized( + "Operators cannot run preview jobs for security reasons".to_string(), + )); + } + + let mut job_id = None; + let mut tx = None; + let mut uploaded = false; + while let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let data = field.bytes().await.unwrap(); + if name == "preview" { + let preview: Preview = serde_json::from_slice(&data).map_err(to_anyhow)?; + + let scheduled_for = run_query.get_scheduled_for(&db).await?; + let tag = run_query.tag.clone().or(preview.tag.clone()); + check_tag_available_for_workspace(&w_id, &tag).await?; + let ltx = + PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq.clone()); + + let args = preview.args.unwrap_or_default(); + + // hmap.insert("") + let (uuid, ntx) = push( + &db, + ltx, + &w_id, + match preview.kind { + Some(PreviewKind::Identity) => JobPayload::Identity, + Some(PreviewKind::Noop) => JobPayload::Noop, + _ => JobPayload::Code(RawCode { + hash: Some(PREVIEW_IS_CODEBASE_HASH), + content: preview.content.unwrap_or_default(), + path: preview.path, + language: preview.language.unwrap_or(ScriptLang::Deno), + lock: preview.lock, + concurrent_limit: None, // TODO(gbouv): once I find out how to store limits in the content of a script, should be easy to plug limits here + concurrency_time_window_s: None, // TODO(gbouv): same as above + cache_ttl: None, + dedicated_worker: preview.dedicated_worker, + }), + }, + args, + authed.display_username(), + &authed.email, + username_to_permissioned_as(&authed.username), + scheduled_for, + None, + None, + None, + run_query.job_id, + false, + false, + None, + true, + tag, + run_query.timeout, + None, + None, + ) + .await?; + job_id = Some(uuid); + tx = Some(ntx); + } + if name == "file" { + let id = job_id + .as_ref() + .ok_or_else(|| { + Error::BadRequest( + "script need to be passed first in the multipart upload".to_string(), + ) + })? + .to_string(); + + uploaded = true; + + if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + let path = windmill_common::s3_helpers::bundle(&w_id, &id); + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } + } + // println!("Length of `{}` is {} bytes", name, data.len()); + } + if !uploaded { + return Err(Error::BadRequest("No file uploaded".to_string())); + } + if job_id.is_none() { + return Err(Error::BadRequest( + "No script found in the uploaded file".to_string(), + )); + } + + tx.unwrap().commit().await?; + + Ok((StatusCode::CREATED, job_id.unwrap().to_string())) +} + +#[cfg(not(all(feature = "enterprise", feature = "parquet")))] +async fn run_bundle_preview_script() -> error::Result<(StatusCode, String)> { + return Err(Error::BadRequest( + "bundle preview is an ee feature".to_string(), + )); +} + #[derive(Deserialize)] pub struct RunDependenciesRequest { pub raw_scripts: Vec, @@ -3511,45 +3692,20 @@ pub struct JobUpdate { pub flow_status: Option, } -// #[cfg(all(feature = "enterprise", feature = "parquet"))] -// async fn get_logs_from_store( -// log_offset: i32, -// logs: &str, -// log_file_index: Option>, -// ) -> Option> { -// if log_offset > 0 { -// if let Some(file_index) = log_file_index { -// tracing::debug!("Getting logs from store: {file_index:?}"); -// if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { -// tracing::debug!("object store client present, streaming from there"); - -// let logs = logs.to_string(); -// let stream = async_stream::stream! { -// for file_p in file_index { -// let file_p_2 = file_p.clone(); -// let file = os.get(&object_store::path::Path::from(file_p)).await; -// if let Ok(file) = file { -// if let Ok(bytes) = file.bytes().await { -// yield Ok(bytes::Bytes::from(bytes)) as object_store::Result; -// } -// } else { -// tracing::debug!("error getting file from store: {file_p_2}: {}", file.err().unwrap()); -// } -// } - -// yield Ok(bytes::Bytes::from(logs)) -// }; -// return Some(Ok(Body::from_stream(stream))); -// } else { -// tracing::debug!("object store client not present, cannot stream logs from store"); -// } -// } -// } -// return None; -// } - -#[cfg(all(feature = "enterprise", feature = "parquet"))] async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::Result { + let local_file = format!("{TMP_DIR}/logs/{file_p}"); + if tokio::fs::metadata(&local_file).await.is_ok() { + let mut file = tokio::fs::File::open(local_file).await.map_err(to_anyhow)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).await.map_err(to_anyhow)?; + let res = Response::builder() + .header(http::header::CONTENT_TYPE, "text/plain") + .body(Body::from(bytes::Bytes::from(buffer))) + .unwrap(); + return Ok(res); + } + + #[cfg(all(feature = "enterprise", feature = "parquet"))] if let Some(os) = OBJECT_STORE_CACHE_SETTINGS.read().await.clone() { let file = os .get(&object_store::path::Path::from(format!("logs/{file_p}"))) @@ -3579,12 +3735,9 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R "Object store client not present, cannot stream logs from store".to_string(), )); } -} -#[cfg(not(all(feature = "enterprise", feature = "parquet")))] -async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::Result { return Err(error::Error::NotFound(format!( - "Get log file is an EE feature: {}", + "File not found on server logs volume /tmp/windmill/logs and no distributed logs s3 storage for {}", file_p ))); } diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 29ad935447..9015740904 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -25,6 +25,7 @@ use db::DB; use git_version::git_version; use reqwest::Client; use std::collections::HashMap; +use std::time::Duration; use std::{net::SocketAddr, sync::Arc}; use tokio::sync::RwLock; use tower::ServiceBuilder; @@ -104,6 +105,8 @@ lazy_static::lazy_static! { pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() .user_agent("windmill/beta") + .connect_timeout(Duration::from_secs(10)) + .timeout(Duration::from_secs(30)) .danger_accept_invalid_certs(std::env::var("ACCEPT_INVALID_CERTS").is_ok()) .build().unwrap(); @@ -141,14 +144,12 @@ pub async fn run_server( )); let argon2 = Arc::new(Argon2::default()); + let disable_response_logs = std::env::var("DISABLE_RESPONSE_LOGS") + .ok() + .map(|x| x == "true") + .unwrap_or(false); + let middleware_stack = ServiceBuilder::new() - .layer( - TraceLayer::new_for_http() - .on_response(MyOnResponse {}) - .make_span_with(MyMakeSpan {}) - .on_request(()) - .on_failure(MyOnFailure {}), - ) .layer(Extension(db.clone())) .layer(Extension(rsmq)) .layer(Extension(user_db)) @@ -285,6 +286,18 @@ pub async fn run_server( .fallback(static_assets::static_handler) .layer(middleware_stack); + let app = if disable_response_logs { + app + } else { + app.layer( + TraceLayer::new_for_http() + .on_response(MyOnResponse {}) + .make_span_with(MyMakeSpan {}) + .on_request(()) + .on_failure(MyOnFailure {}), + ) + }; + let instance_name = rd_string(5); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); @@ -320,6 +333,7 @@ async fn is_up_to_date() -> Result { let error_reading_version = || anyhow::anyhow!("Error reading latest released version"); let version = HTTP_CLIENT .get("https://api.github.com/repos/windmill-labs/windmill/releases/latest") + .timeout(Duration::from_secs(10)) .send() .await .context("Impossible to reach api.github")? diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index b19481e3cf..0073d0a5f3 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -13,6 +13,9 @@ use crate::{ webhook_util::{WebhookMessage, WebhookShared}, HTTP_CLIENT, }; +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use axum::extract::Multipart; + use axum::{ extract::{Extension, Path, Query}, response::IntoResponse, @@ -31,6 +34,10 @@ use std::{ }; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use windmill_common::error::to_anyhow; + use windmill_common::{ db::UserDB, error::{Error, JsonResult, Result}, @@ -116,6 +123,7 @@ pub fn workspaced_service() -> Router { .route("/list", get(list_scripts)) .route("/list_search", get(list_search_scripts)) .route("/create", post(create_script)) + .route("/create_snapshot", post(create_snapshot_script)) .route("/archive/p/*path", post(archive_script_by_path)) .route("/get/draft/*path", get(get_script_by_path_w_draft)) .route("/get/p/*path", get(get_script_by_path)) @@ -196,6 +204,7 @@ async fn list_scripts( "draft_only", "ws_error_handler_muted", "no_main_func", + "codebase IS NOT NULL as use_codebase" ]) .left() .join("favorite") @@ -312,6 +321,82 @@ fn hash_script(ns: &NewScript) -> i64 { dh.finish() as i64 } +#[cfg(not(all(feature = "enterprise", feature = "parquet")))] +async fn create_snapshot_script() -> Result<(StatusCode, String)> { + Err(Error::BadRequest("Upgrade to EE to use bundle".to_string())) +} + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +async fn create_snapshot_script( + authed: ApiAuthed, + Extension(user_db): Extension, + Extension(rsmq): Extension>, + Extension(webhook): Extension, + Extension(db): Extension, + Path(w_id): Path, + mut multipart: Multipart, +) -> Result<(StatusCode, String)> { + let mut script_hash = None; + let mut tx = None; + let mut uploaded = false; + while let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let data = field.bytes().await.unwrap(); + if name == "script" { + let ns = Some(serde_json::from_slice(&data).map_err(to_anyhow)?); + let (new_hash, ntx) = create_script_internal( + ns.unwrap(), + w_id.clone(), + authed.clone(), + db.clone(), + rsmq.clone(), + user_db.clone(), + webhook.clone(), + ) + .await?; + script_hash = Some(new_hash.to_string()); + tx = Some(ntx); + } + if name == "file" { + let hash = script_hash.as_ref().ok_or_else(|| { + Error::BadRequest( + "script need to be passed first in the multipart upload".to_string(), + ) + })?; + + uploaded = true; + if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + let path = windmill_common::s3_helpers::bundle(&w_id, &hash); + if let Err(e) = os + .put(&object_store::path::Path::from(path.clone()), data) + .await + { + tracing::info!("Failed to put snapshot to s3 at {path}: {:?}", e); + return Err(Error::ExecutionErr(format!("Failed to put {path} to s3"))); + } + } else { + return Err(Error::BadConfig("Object store is required for snapshot script and is not configured for servers".to_string())); + } + } + // println!("Length of `{}` is {} bytes", name, data.len()); + } + if !uploaded { + return Err(Error::BadRequest("No file uploaded".to_string())); + } + if script_hash.is_none() { + return Err(Error::BadRequest( + "No script found in the uploaded file".to_string(), + )); + } + + tx.unwrap().commit().await?; + return Ok((StatusCode::CREATED, format!("{}", script_hash.unwrap()))); +} + async fn create_script( authed: ApiAuthed, Extension(user_db): Extension, @@ -321,6 +406,24 @@ async fn create_script( Path(w_id): Path, Json(ns): Json, ) -> Result<(StatusCode, String)> { + let (hash, tx) = create_script_internal(ns, w_id, authed, db, rsmq, user_db, webhook).await?; + tx.commit().await?; + Ok((StatusCode::CREATED, format!("{}", hash))) +} + +async fn create_script_internal<'c>( + ns: NewScript, + w_id: String, + authed: ApiAuthed, + db: sqlx::Pool, + rsmq: Option, + user_db: UserDB, + webhook: WebhookShared, +) -> Result<( + ScriptHash, + QueueTransaction<'c, rsmq_async::MultiplexedRsmq>, +)> { + let codebase = ns.codebase.as_ref(); #[cfg(not(feature = "enterprise"))] if ns.ws_error_handler_muted.is_some_and(|val| val) { return Err(Error::BadRequest( @@ -328,12 +431,10 @@ async fn create_script( .to_string(), )); } - let script_path = ns.path.clone(); let hash = ScriptHash(hash_script(&ns)); let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await; let mut tx: QueueTransaction<'_, _> = (rsmq.clone(), user_db.begin(&authed).await?).into(); - if sqlx::query_scalar!( "SELECT 1 FROM script WHERE hash = $1 AND workspace_id = $2", hash.0, @@ -349,7 +450,6 @@ async fn create_script( .to_owned(), )); }; - let clashing_script = sqlx::query_as::<_, Script>( "SELECT * FROM script WHERE path = $1 AND archived = false AND workspace_id = $2", ) @@ -357,7 +457,6 @@ async fn create_script( .bind(&w_id) .fetch_optional(&mut tx) .await?; - struct ParentInfo { p_hashes: Vec, perms: serde_json::Value, @@ -450,13 +549,11 @@ async fn create_script( r } }?; - let p_hashes = parent_hashes_and_perms.as_ref().map(|v| &v.p_hashes[..]); let extra_perms = parent_hashes_and_perms .as_ref() .map(|v| v.perms.clone()) .unwrap_or(json!({})); - let lock = if !(ns.language == ScriptLang::Python3 || ns.language == ScriptLang::Go || ns.language == ScriptLang::Bun @@ -467,23 +564,20 @@ async fn create_script( ns.lock .and_then(|e| if e.is_empty() { None } else { Some(e) }) }; - let needs_lock_gen = lock.is_none(); - let envs = ns.envs.as_ref().map(|x| x.as_slice()); let envs = if ns.envs.is_none() || ns.envs.as_ref().unwrap().is_empty() { None } else { envs }; - //::text::json is to ensure we use serde_json with preserve order sqlx::query!( "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \ content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \ draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \ dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \ - delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func) \ - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)", + delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase) \ + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30)", &w_id, &hash.0, ns.path, @@ -512,11 +606,11 @@ async fn create_script( ns.timeout, ns.concurrency_key, ns.visible_to_runner_only, - ns.no_main_func + ns.no_main_func, + codebase ) .execute(&mut tx) .await?; - let p_path_opt = parent_hashes_and_perms.as_ref().map(|x| x.p_path.clone()); if let Some(ref p_path) = p_path_opt { sqlx::query!( @@ -566,7 +660,6 @@ async fn create_script( .execute(&mut tx) .await?; } - if p_hashes.is_some() && !p_hashes.unwrap().is_empty() { audit_log( &mut tx, @@ -612,7 +705,6 @@ async fn create_script( }, ); } - let permissioned_as = username_to_permissioned_as(&authed.username); if needs_lock_gen { let tag = if ns.dedicated_worker.is_some_and(|x| x) { @@ -661,7 +753,7 @@ async fn create_script( None, ) .await?; - new_tx.commit().await?; + Ok((hash, new_tx)) } else { handle_deployment_metadata( &authed.email, @@ -678,10 +770,8 @@ async fn create_script( false, ) .await?; - tx.commit().await?; + Ok((hash, tx)) } - - Ok((StatusCode::CREATED, format!("{}", hash))) } pub async fn get_hub_script_by_path( diff --git a/backend/windmill-api/src/webhook_util.rs b/backend/windmill-api/src/webhook_util.rs index 590a706cc0..27a3b489d8 100644 --- a/backend/windmill-api/src/webhook_util.rs +++ b/backend/windmill-api/src/webhook_util.rs @@ -71,6 +71,7 @@ impl WebhookShared { let (tx, mut rx) = mpsc::unbounded_channel::(); let _process = tokio::spawn(async move { let client = reqwest::Client::builder() + .connect_timeout(Duration::from_secs(5)) // TODO: investigate pool timeouts and such if TCP load is high .timeout(Duration::from_secs(5)) .build() diff --git a/backend/windmill-api/src/workers.rs b/backend/windmill-api/src/workers.rs index 6a39e8585c..8bb56e78fd 100644 --- a/backend/windmill-api/src/workers.rs +++ b/backend/windmill-api/src/workers.rs @@ -14,14 +14,16 @@ use axum::{ use serde::{Deserialize, Serialize}; use sqlx::FromRow; +use uuid::Uuid; use windmill_common::{ db::UserDB, error::JsonResult, utils::{paginate, Pagination}, worker::{ALL_TAGS, DEFAULT_TAGS, DEFAULT_TAGS_PER_WORKSPACE}, + DB, }; -use crate::db::ApiAuthed; +use crate::{db::ApiAuthed, utils::require_super_admin}; pub fn global_service() -> Router { Router::new() @@ -33,6 +35,7 @@ pub fn global_service() -> Router { get(get_default_tags_per_workspace), ) .route("/get_default_tags", get(get_default_tags)) + .route("/queue_metrics", get(get_queue_metrics)) } #[derive(FromRow, Serialize, Deserialize)] @@ -43,9 +46,12 @@ struct WorkerPing { started_at: chrono::DateTime, ip: String, jobs_executed: i32, + current_job_id: Option, + current_job_workspace_id: Option, custom_tags: Option>, worker_group: String, wm_version: String, + occupancy_rate: Option, } #[derive(Serialize, Deserialize)] @@ -62,21 +68,25 @@ pub struct ListWorkerQuery { async fn list_worker_pings( authed: ApiAuthed, + Extension(db): Extension, Extension(user_db): Extension, Query(query): Query, ) -> JsonResult> { + let is_super_admin = require_super_admin(&db, &authed.email).await.is_ok(); let mut tx = user_db.begin(&authed).await?; let (per_page, offset) = paginate(Pagination { page: query.page, per_page: query.per_page }); let rows = sqlx::query_as!( WorkerPing, - "SELECT worker, worker_instance, EXTRACT(EPOCH FROM (now() - ping_at))::integer as last_ping, started_at, ip, jobs_executed, custom_tags, worker_group, wm_version FROM worker_ping - WHERE ($1::integer IS NULL AND ping_at > now() - interval '5 minute') OR (ping_at > now() - ($1 || ' seconds')::interval) - ORDER BY ping_at desc LIMIT $2 OFFSET $3", + "SELECT worker, worker_instance, EXTRACT(EPOCH FROM (now() - ping_at))::integer as last_ping, started_at, ip, jobs_executed, CASE WHEN $4 IS TRUE THEN current_job_id ELSE NULL END as current_job_id, CASE WHEN $4 IS TRUE THEN current_job_workspace_id ELSE NULL END as current_job_workspace_id, custom_tags, worker_group, wm_version, occupancy_rate + FROM worker_ping + WHERE ($1::integer IS NULL AND ping_at > now() - interval '5 minute') OR (ping_at > now() - ($1 || ' seconds')::interval) + ORDER BY ping_at desc LIMIT $2 OFFSET $3", query.ping_since, per_page as i64, - offset as i64 + offset as i64, + is_super_admin ) .fetch_all(&mut *tx) .await?; @@ -118,3 +128,35 @@ async fn get_default_tags_per_workspace() -> JsonResult { async fn get_default_tags() -> JsonResult> { Ok(Json(DEFAULT_TAGS.clone())) } + +#[derive(Serialize)] +struct QueueMetric { + id: String, + values: Vec, +} + +async fn get_queue_metrics( + authed: ApiAuthed, + Extension(db): Extension, +) -> JsonResult> { + require_super_admin(&db, &authed.email).await?; + + let queue_metrics = sqlx::query_as!( + QueueMetric, + "WITH queue_metrics as ( + SELECT id, value, created_at + FROM metrics + WHERE id LIKE 'queue_%' + AND created_at > now() - interval '14 day' + ORDER BY created_at ASC + ) + SELECT id, array_agg(json_build_object('value', value, 'created_at', created_at)) as \"values!\" + FROM queue_metrics + GROUP BY id + ORDER BY id ASC" + ) + .fetch_all(&db) + .await?; + + Ok(Json(queue_metrics)) +} diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index b85302d236..b242e7ef84 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -2218,6 +2218,8 @@ struct ScriptMetadata { pub visible_to_runner_only: Option, #[serde(skip_serializing_if = "Option::is_none")] pub no_main_func: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub codebase: Option, } pub fn is_none_or_false(val: &Option) -> bool { @@ -2495,6 +2497,7 @@ async fn tarball_workspace( restart_unless_cancelled: script.restart_unless_cancelled, visible_to_runner_only: script.visible_to_runner_only, no_main_func: script.no_main_func, + codebase: script.codebase, }; let metadata_str = serde_json::to_string_pretty(&metadata).unwrap(); archive diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index 9ac9ea67a7..e1c0af06fa 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -10,7 +10,7 @@ enterprise = [] prometheus = ["dep:prometheus"] flamegraph = ["dep:tracing-flame"] loki = ["dep:tracing-loki"] -parquet = ["dep:object_store", "dep:aws-config", "dep:aws-sdk-sts"] +parquet = ["dep:object_store", "dep:aws-config", "dep:aws-sdk-sts", "dep:bytes"] [lib] name = "windmill_common" @@ -47,4 +47,5 @@ prometheus = { workspace = true, optional = true } aws-config = { workspace = true, optional = true } aws-sdk-sts = { workspace = true, optional = true } indexmap.workspace = true -mail-send.workspace = true \ No newline at end of file +bytes = { workspace = true, optional = true } +mail-send.workspace = true diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index 32e9740d78..b26f074abb 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -58,6 +58,8 @@ pub enum Error { JsonErr(serde_json::Value), #[error("{0}")] OpenAIError(String), + #[error("{0}")] + AlreadyCompleted(String), } impl Error { diff --git a/backend/windmill-common/src/external_ip.rs b/backend/windmill-common/src/external_ip.rs index 087c908c76..0831bb54bc 100644 --- a/backend/windmill-common/src/external_ip.rs +++ b/backend/windmill-common/src/external_ip.rs @@ -13,16 +13,19 @@ use std::time::Duration; -use reqwest::Result; - -pub async fn get_ip() -> Result { - reqwest::ClientBuilder::new() - .timeout(Duration::from_secs(3)) +pub async fn get_ip() -> anyhow::Result { + tokio::select! { + biased; + _ = tokio::time::sleep(Duration::from_secs(10)) => { + return Err(anyhow::anyhow!("Expected to get ip under 10s")) + }, + ip = reqwest::ClientBuilder::new() + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(5)) .build()? .get("https://hub.windmill.dev/getip") - .send() - .await? - .error_for_status()? - .text() - .await + .send() => Ok(ip? + .error_for_status()? + .text().await?), + } } diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index f93609385f..f90ea4e3c7 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -345,6 +345,7 @@ pub enum JobPayload { pub struct RawCode { pub content: String, pub path: Option, + pub hash: Option, pub language: ScriptLang, pub lock: Option, pub concurrent_limit: Option, diff --git a/backend/windmill-common/src/s3_helpers.rs b/backend/windmill-common/src/s3_helpers.rs index 4be07d33d9..fbf33af0e0 100644 --- a/backend/windmill-common/src/s3_helpers.rs +++ b/backend/windmill-common/src/s3_helpers.rs @@ -177,6 +177,48 @@ pub fn build_object_store_client( } } +#[cfg(feature = "parquet")] +pub async fn attempt_fetch_bytes( + client: Arc, + path: &str, +) -> error::Result { + use object_store::path::Path; + + let object = client.get(&Path::from(path)).await; + if let Err(e) = object { + tracing::info!( + "Failed to pull bytes from object store at path {path}. Error: {:?}", + e + ); + return Err(error::Error::ExecutionErr(format!( + "Failed to pull bytes from object store: {path}" + ))); + } + + let bytes = object.unwrap().bytes().await; + if bytes.is_err() { + tracing::info!( + "Failed to read bytes from object store: {path}. Error: {:?}", + bytes.err() + ); + return Err(error::Error::ExecutionErr(format!( + "Failed to read bytes from object store: {path}" + ))); + } + let bytes = bytes.unwrap(); + + tracing::info!("{path} len: {}", bytes.len()); + + if bytes.len() == 0 { + tracing::info!("object {path} not found in bucket, bytes empty",); + return Err(error::Error::ExecutionErr(format!( + "object {path} does not exist in bucket" + ))); + } + + return Ok(bytes); +} + #[cfg(feature = "parquet")] use aws_config::{default_provider::credentials::DefaultCredentialsChain, Region}; #[cfg(feature = "parquet")] @@ -408,3 +450,7 @@ impl CredentialProvider for AwsCredentialAdapter { })) } } + +pub fn bundle(w_id: &str, hash: &str) -> String { + format!("script_bundle/{}/{}", w_id, hash) +} diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index 3543c08f78..b7ab659b5b 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -128,6 +128,8 @@ impl Display for ScriptKind { } } +pub const PREVIEW_IS_CODEBASE_HASH: i64 = -42; + #[derive(Serialize, sqlx::FromRow)] pub struct Script { pub workspace_id: String, @@ -177,6 +179,8 @@ pub struct Script { pub visible_to_runner_only: Option, #[serde(skip_serializing_if = "Option::is_none")] pub no_main_func: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub codebase: Option, } #[derive(Serialize, sqlx::FromRow)] @@ -198,6 +202,12 @@ pub struct ListableScript { pub ws_error_handler_muted: Option, #[serde(skip_serializing_if = "Option::is_none")] pub no_main_func: Option, + #[serde(skip_serializing_if = "is_false")] + pub use_codebase: bool, +} + +fn is_false(x: &bool) -> bool { + return !x; } #[derive(Serialize)] @@ -256,6 +266,7 @@ pub struct NewScript { pub concurrency_key: Option, pub visible_to_runner_only: Option, pub no_main_func: Option, + pub codebase: Option, } fn lock_deserialize<'de, D>(deserializer: D) -> Result, D::Error> diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index fc7157308e..007a1031a3 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -6,7 +6,9 @@ * LICENSE-AGPL for a copy of the license. */ -use crate::ee::{trigger_critical_error_channels, LICENSE_KEY_ID}; +#[cfg(feature = "enterprise")] +use crate::ee::trigger_critical_error_channels; +use crate::ee::LICENSE_KEY_ID; use crate::error::{to_anyhow, Error, Result}; use crate::global_settings::UNIQUE_ID_SETTING; use crate::server::Smtp; @@ -233,5 +235,6 @@ pub async fn send_email( pub async fn report_critical_error(error_message: String) -> () { tracing::error!("CRITICAL ERROR: {error_message}"); + #[cfg(feature = "enterprise")] trigger_critical_error_channels(error_message).await; } diff --git a/backend/windmill-common/src/variables.rs b/backend/windmill-common/src/variables.rs index c922d94b07..cb761b4643 100644 --- a/backend/windmill-common/src/variables.rs +++ b/backend/windmill-common/src/variables.rs @@ -276,12 +276,6 @@ pub async fn get_reserved_variables( description: "State resource path unique to a script and its trigger".to_string(), is_custom: false, }, - ContextualVariable { - name: "WM_STATE_PATH_NEW".to_string(), - value: state_path, - description: "State resource path unique to a script and its trigger (legacy)".to_string(), - is_custom: false, - }, ContextualVariable { name: "WM_FLOW_STEP_ID".to_string(), value: step_id.unwrap_or_else(|| "".to_string()), diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index a767a8d672..1b3a2c46ab 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -70,6 +70,8 @@ lazy_static::lazy_static! { } +pub const TMP_DIR: &str = "/tmp/windmill"; + pub async fn reload_custom_tags_setting(db: &DB) -> error::Result<()> { let q = sqlx::query!( "SELECT value FROM global_settings WHERE name = $1", @@ -140,15 +142,54 @@ pub async fn update_ping(worker_instance: &str, worker_name: &str, ip: &str, db: .map(|x| format!("{}:{}", x.workspace_id, x.path)), ) }; + let mut vcpus = std::process::Command::new("cat") + .args(["/sys/fs/cgroup/cpu.max"]) + .output() + .ok() + .map(|o| { + String::from_utf8_lossy(&o.stdout) + .to_string() + .split(" ") + .map(|s| s.to_string()) + .collect::>() + .get(0) + .map(|s| s.to_string()) + }) + .flatten(); + + if vcpus.is_none() { + vcpus = std::process::Command::new("cat") + .args(["/sys/fs/cgroup/cpu/cpu.cfs_quota_us"]) + .output() + .ok() + .map(|o| String::from_utf8_lossy(&o.stdout).to_string()) + } + + let mut memory = std::process::Command::new("cat") + .args(["/sys/fs/cgroup/memory.max"]) + .output() + .ok() + .map(|o| String::from_utf8_lossy(&o.stdout).to_string()); + + if memory.is_none() { + memory = std::process::Command::new("cat") + .args(["/sys/fs/cgroup/memory/memory.limit_in_bytes"]) + .output() + .ok() + .map(|o| String::from_utf8_lossy(&o.stdout).to_string()) + } + sqlx::query!( - "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5", + "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5", worker_instance, worker_name, ip, tags.as_slice(), *WORKER_GROUP, dw, - crate::utils::GIT_VERSION + crate::utils::GIT_VERSION, + vcpus.map(|x| x.parse::().ok()).flatten(), + memory.map(|x| x.parse::().ok()).flatten() ) .execute(db) .await diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 0b3bd7a791..7f819fdbe4 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -106,11 +106,10 @@ lazy_static::lazy_static! { lazy_static::lazy_static! { pub static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() .user_agent("windmill/beta") + .timeout(std::time::Duration::from_secs(20)) + .connect_timeout(std::time::Duration::from_secs(10)) .build().unwrap(); - pub static ref HTTP_CLIENT_WORKER: Client = reqwest::ClientBuilder::new() - .user_agent("windmill/beta") - .build().unwrap(); } @@ -133,24 +132,16 @@ pub struct CanceledBy { pub reason: Option, } -#[async_recursion] -pub async fn cancel_job<'c: 'async_recursion>( +pub async fn cancel_single_job<'c>( username: &str, reason: Option, - id: Uuid, + job_running: &QueuedJob, w_id: &str, mut tx: Transaction<'c, Postgres>, db: &Pool, rsmq: Option, force_cancel: bool, ) -> error::Result<(Transaction<'c, Postgres>, Option)> { - let job_running = get_queued_job_tx(id, &w_id, &mut tx).await?; - - if job_running.is_none() { - return Ok((tx, None)); - } - let job_running = job_running.unwrap(); - if ((job_running.running || job_running.root_job.is_some()) || (job_running.is_flow())) && !force_cancel { @@ -158,7 +149,7 @@ pub async fn cancel_job<'c: 'async_recursion>( "UPDATE queue SET canceled = true, canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 RETURNING id", username, reason, - id, + job_running.id, w_id ) .fetch_optional(&mut *tx) @@ -172,7 +163,7 @@ pub async fn cancel_job<'c: 'async_recursion>( .unwrap_or_else(|| "unexplicited reasons".to_string()); let e = serde_json::json!({"message": format!("Job canceled: {reason} by {username}"), "name": "Canceled", "reason": reason, "canceler": username}); append_logs( - id, + job_running.id, w_id.to_string(), format!("canceled by {username}: (force cancel: {force_cancel})"), db, @@ -180,7 +171,7 @@ pub async fn cancel_job<'c: 'async_recursion>( .await; let add_job = add_completed_job_error( &db, - &job_running, + job_running, job_running.mem_peak.unwrap_or(0), Some(CanceledBy { username: Some(username.to_string()), reason: Some(reason) }), e, @@ -189,16 +180,73 @@ pub async fn cancel_job<'c: 'async_recursion>( false, ) .await; + if let Err(e) = add_job { tracing::error!("Failed to add canceled job: {}", e); } } if let Some(mut rsmq) = rsmq.clone() { - rsmq.change_message_visibility(&job_running.tag, &id.to_string(), 0) + rsmq.change_message_visibility(&job_running.tag, &job_running.id.to_string(), 0) .await .map_err(|e| anyhow::anyhow!(e))?; } + Ok((tx, Some(job_running.id))) +} + +pub async fn cancel_job<'c>( + username: &str, + reason: Option, + id: Uuid, + w_id: &str, + mut tx: Transaction<'c, Postgres>, + db: &Pool, + rsmq: Option, + force_cancel: bool, +) -> error::Result<(Transaction<'c, Postgres>, Option)> { + let job = get_queued_job_tx(id, &w_id, &mut tx).await?; + + if job.is_none() { + return Ok((tx, None)); + } + + let job = job.unwrap(); + + let (ntx, _) = cancel_single_job( + username, + reason.clone(), + &job, + w_id, + tx, + db, + rsmq.clone(), + force_cancel, + ) + .await?; + tx = ntx; + + // soft cancel parent if force cancel and job is a flow step + if force_cancel && job.is_flow_step { + if let Some(parent_job_id) = job.parent_job { + let job = get_queued_job_tx(parent_job_id, &w_id, &mut tx).await?; + if let Some(job) = job { + let (ntx, _) = cancel_single_job( + username, + reason.clone(), + &job, + w_id, + tx, + db, + rsmq.clone(), + false, + ) + .await?; + tx = ntx; + } + } + } + + // cancel children let mut jobs = vec![id]; let mut jobs_to_cancel = vec![]; while !jobs.is_empty() { @@ -213,19 +261,23 @@ pub async fn cancel_job<'c: 'async_recursion>( jobs.extend(new_jobs.clone()); jobs_to_cancel.extend(new_jobs); } - for job in jobs_to_cancel { - let (ntx, _) = cancel_job( - username, - reason.clone(), - job, - w_id, - tx, - db, - rsmq.clone(), - force_cancel, - ) - .await?; - tx = ntx; + for job_id in jobs_to_cancel { + let job = get_queued_job_tx(job_id, &w_id, &mut tx).await?; + + if let Some(job) = job { + let (ntx, _) = cancel_single_job( + username, + reason.clone(), + &job, + w_id, + tx, + db, + rsmq.clone(), + force_cancel, + ) + .await?; + tx = ntx; + } } Ok((tx, Some(id))) } @@ -633,8 +685,19 @@ pub async fn add_completed_job< if let Some(schedule) = schedule { skip_downstream_error_handlers = schedule.ws_error_handler_muted; - if !queued_job.is_flow() { - // script only + // script or flow that failed on start and might not have been rescheduled + let schedule_next_tick = !queued_job.is_flow() + || { + let flow_status = queued_job.parse_flow_status(); + flow_status.is_some_and(|fs| { + fs.step == 0 + && fs.modules.get(0).is_some_and(|m| { + matches!(m, FlowStatusModule::WaitingForPriorSteps { .. }) || matches!(m, FlowStatusModule::Failure { job, ..} if job == &Uuid::nil()) + }) + }) + }; + + if schedule_next_tick { if let Err(err) = handle_maybe_scheduled_job( rsmq.clone(), db, @@ -2882,6 +2945,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection JobPayload::Code(RawCode { content, path, + hash, language, lock, concurrent_limit, @@ -2889,7 +2953,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection cache_ttl, dedicated_worker, }) => ( - None, + hash, path, Some((content, lock)), JobKind::Preview, diff --git a/backend/windmill-worker/loader.bun.ts b/backend/windmill-worker/loader.bun.js similarity index 97% rename from backend/windmill-worker/loader.bun.ts rename to backend/windmill-worker/loader.bun.js index 66694a948e..c3dfeeaf54 100644 --- a/backend/windmill-worker/loader.bun.ts +++ b/backend/windmill-worker/loader.bun.js @@ -27,7 +27,7 @@ const p = { const cdir = resolve("./"); const cdirNoPrivate = cdir.replace(/^\/private/, ""); // for macos const filter = new RegExp( - `^(?!\\.\/main\\.ts)(?!${cdir}\/main\\.ts)(?!(?:/private)?${cdirNoPrivate}\/wrapper\\.ts).*\\.ts$` + `^(?!\\.\/main\\.ts)(?!${cdir}\/main\\.ts)(?!(?:/private)?${cdirNoPrivate}\/wrapper\\.mjs).*\\.ts$` ); build.onResolve({ filter }, (args) => { const file_path = diff --git a/backend/windmill-worker/loader_builder.bun.ts b/backend/windmill-worker/loader_builder.bun.js similarity index 91% rename from backend/windmill-worker/loader_builder.bun.ts rename to backend/windmill-worker/loader_builder.bun.js index 111a1661ca..1ef09f1a04 100644 --- a/backend/windmill-worker/loader_builder.bun.ts +++ b/backend/windmill-worker/loader_builder.bun.js @@ -21,7 +21,7 @@ if (!bo.success) { content.replaceAll("__require", "require") ); - const dependencies: Record = {}; + const dependencies = {}; for (const i of imports) { let [_, name, version] = i.path.match(captureVersion) ?? []; if (name == undefined) { @@ -46,7 +46,7 @@ if (!bo.success) { } } } - const resolvedDeps: Record = {}; + const resolvedDeps = {}; for (const i in dependencies) { const versions = dependencies[i]; resolvedDeps[i] = @@ -61,7 +61,7 @@ if (!bo.success) { JSON.stringify({ dependencies: resolvedDeps }, null, 2) ); - function reduceIntersect(versions: string[], name: string): string { + function reduceIntersect(versions, name) { console.log( `multiple versions detected for ${name}: ${versions.join(", ")}` ); diff --git a/backend/windmill-worker/nsjail/run.bun.config.proto b/backend/windmill-worker/nsjail/run.bun.config.proto index d5de58cc51..20b0893b4e 100644 --- a/backend/windmill-worker/nsjail/run.bun.config.proto +++ b/backend/windmill-worker/nsjail/run.bun.config.proto @@ -66,7 +66,7 @@ mount { src: "{JOB_DIR}/package.json" dst: "/tmp/{LANG}/package.json" is_bind: true - mandatory: true + mandatory: false } mount { @@ -77,13 +77,6 @@ mount { } -mount { - src: "{JOB_DIR}/wrapper.ts" - dst: "/tmp/{LANG}/wrapper.ts" - is_bind: true - mandatory: false -} - mount { src: "{JOB_DIR}/wrapper.mjs" dst: "/tmp/{LANG}/wrapper.mjs" @@ -114,6 +107,13 @@ mount { mandatory: false } +mount { + src: "{JOB_DIR}/main.js" + dst: "/tmp/{LANG}/main.js" + is_bind: true + mandatory: false +} + mount { src: "{JOB_DIR}/args.json" diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index 1390ed3c61..5cbc2c75f6 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -35,11 +35,15 @@ use windmill_common::{ error::{self, to_anyhow, Result}, jobs::QueuedJob, }; + +#[cfg(all(feature = "enterprise", feature = "parquet"))] +use windmill_common::s3_helpers::attempt_fetch_bytes; + use windmill_parser::Typ; -const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.ts"); +const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.js"); -const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.ts"); +const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js"); const NSJAIL_CONFIG_RUN_BUN_CONTENT: &str = include_str!("../nsjail/run.bun.config.proto"); @@ -76,7 +80,7 @@ pub async fn gen_lockfile( } else { let _ = write_file( &job_dir, - "build.ts", + "build.js", &format!( r#" {} @@ -100,7 +104,7 @@ pub async fn gen_lockfile( .current_dir(job_dir) .env_clear() .envs(common_bun_proc_envs.clone()) - .args(vec!["run", "build.ts"]) + .args(vec!["run", "build.js"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); let child_process = start_child_process(child_cmd, &*BUN_PATH).await?; @@ -370,7 +374,7 @@ try {{ }} const bo = await Bun.build({{ - entrypoints: ["{job_dir}/wrapper.ts"], + entrypoints: ["{job_dir}/wrapper.mjs"], outdir: "./", target: "node", plugins: [p], @@ -389,7 +393,7 @@ if (!bo.success) {{ } else { write_file( &job_dir, - "loader.bun.ts", + "loader.bun.js", &format!( r#" import {{ plugin }} from "bun"; @@ -450,9 +454,46 @@ pub async fn generate_wrapper_mjs( Ok(()) } +#[cfg(all(feature = "enterprise", feature = "parquet"))] +pub async fn pull_codebase(w_id: &str, id: &str, job_dir: &str) -> Result<()> { + let path = windmill_common::s3_helpers::bundle(&w_id, &id); + let bun_cache_path = format!("{}/{}", BUN_CACHE_DIR, path); + let dst = format!("{job_dir}/main.js"); + let dirs_splitted = bun_cache_path.split("/").collect_vec(); + tokio::fs::create_dir_all(dirs_splitted[..dirs_splitted.len() - 1].join("/")).await?; + if tokio::fs::metadata(&bun_cache_path).await.is_ok() { + tracing::info!("loading {bun_cache_path} from cache"); + tokio::fs::symlink(&bun_cache_path, dst).await?; + } else if let Some(os) = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS + .read() + .await + .clone() + { + let bytes = attempt_fetch_bytes(os, &path).await?; + if *windmill_common::worker::CLOUD_HOSTED { + tokio::fs::write(dst, &bytes).await?; + } else { + tokio::fs::write(&bun_cache_path, &bytes).await?; + tokio::fs::symlink(bun_cache_path, dst).await?; + } + + // extract_tar(bytes, job_dir).await?; + } + + return Ok(()); +} + +#[cfg(not(all(feature = "enterprise", feature = "parquet")))] +pub async fn pull_codebase(_w_id: &str, _id: &str, _job_dir: &str) -> Result<()> { + return Err(error::Error::ExecutionErr( + "codebase is an EE feature".to_string(), + )); +} + #[tracing::instrument(level = "trace", skip_all)] pub async fn handle_bun_job( requirements_o: Option, + codebase: Option, mem_peak: &mut i32, canceled_by: &mut Option, job: &QueuedJob, @@ -465,13 +506,20 @@ pub async fn handle_bun_job( envs: HashMap, shared_mount: &str, ) -> error::Result> { - let _ = write_file(job_dir, "main.ts", inner_content).await?; + if !codebase.is_some() { + let _ = write_file(job_dir, "main.ts", inner_content).await?; + } else { + let _ = write_file(job_dir, "package.json", r#"{ "type": "module" }"#).await?; + } let common_bun_proc_envs: HashMap = get_common_bun_proc_envs(&base_internal_url).await; - let annotation = get_annotation(inner_content); + let mut annotation = get_annotation(inner_content); + if codebase.is_some() { + annotation.nodejs_mode = true + } let main_override = get_main_override(job.args.as_ref()); #[cfg(not(feature = "enterprise"))] @@ -481,7 +529,9 @@ pub async fn handle_bun_job( )); } - if let Some(reqs) = requirements_o { + if let Some(codebase) = codebase.as_ref() { + pull_codebase(&job.workspace_id, codebase, job_dir).await?; + } else if let Some(reqs) = requirements_o { let splitted = reqs.split(BUN_LOCKB_SPLIT).collect::>(); if splitted.len() != 2 { return Err(error::Error::ExecutionErr( @@ -547,10 +597,11 @@ pub async fn handle_bun_job( // } } - let main_code = remove_pinned_imports(inner_content)?; - let _ = write_file(job_dir, "main.ts", &main_code).await?; + let _ = write_file(job_dir, "main.ts", &remove_pinned_imports(inner_content)?).await?; - let init_logs = if annotation.nodejs_mode { + let init_logs = if codebase.is_some() { + "\n\n--- NODE SNAPSHOT EXECUTION ---\n".to_string() + } else if annotation.nodejs_mode { "\n\n--- NODE CODE EXECUTION ---\n".to_string() } else { "\n\n--- BUN CODE EXECUTION ---\n".to_string() @@ -581,11 +632,17 @@ pub async fn handle_bun_job( // we cannot use Bun.read and Bun.write because it results in an EBADF error on cloud let main_name = main_override.unwrap_or("main".to_string()); + let main_import = if codebase.is_some() { + "./main.js" + } else { + "./main.ts" + }; + let wrapper_content: String = format!( r#" -import {{ {main_name} }} from "./main.ts"; +import * as Main from "{main_import}"; -const fs = require('fs/promises'); +import * as fs from "fs/promises"; const args = await fs.readFile('args.json', {{ encoding: 'utf8' }}).then(JSON.parse) .then(({{ {spread} }}) => [ {spread} ]) @@ -596,7 +653,7 @@ BigInt.prototype.toJSON = function () {{ {dates} async function run() {{ - let res: any = await {main_name}(...args); + let res = await Main.{main_name}(...args); const res_json = JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value); await fs.writeFile("result.json", res_json); process.exit(0); @@ -614,7 +671,7 @@ try {{ }} "#, ); - write_file(job_dir, "wrapper.ts", &wrapper_content).await?; + write_file(job_dir, "wrapper.mjs", &wrapper_content).await?; Ok(()) as error::Result<()> }; @@ -633,15 +690,19 @@ try {{ }; let write_loader_f = async { - build_loader( - job_dir, - base_internal_url, - &client.get_token().await, - &job.workspace_id, - &job.script_path(), - annotation.nodejs_mode, - ) - .await + if !codebase.is_some() { + build_loader( + job_dir, + base_internal_url, + &client.get_token().await, + &job.workspace_id, + &job.script_path(), + annotation.nodejs_mode, + ) + .await + } else { + Ok(()) + } }; let (reserved_variables, _, _) = tokio::try_join!( @@ -650,7 +711,7 @@ try {{ write_loader_f )?; - if annotation.nodejs_mode { + if annotation.nodejs_mode && !codebase.is_some() { generate_wrapper_mjs( job_dir, &job.workspace_id, @@ -705,6 +766,15 @@ try {{ &NODE_PATH, "/tmp/nodejs/wrapper.mjs", ] + } else if codebase.is_some() { + vec![ + "--config", + "run.config.proto", + "--", + &BUN_PATH, + "run", + "/tmp/bun/wrapper.mjs", + ] } else { vec![ "--config", @@ -715,8 +785,8 @@ try {{ "-i", "--prefer-offline", "-r", - "/tmp/bun/loader.bun.ts", - "/tmp/bun/wrapper.ts", + "/tmp/bun/loader.bun.js", + "/tmp/bun/wrapper.mjs", ] }; nsjail_cmd @@ -746,23 +816,28 @@ try {{ .stderr(Stdio::piped()); bun_cmd } else { - let script_path = format!("{job_dir}/wrapper.ts"); + let script_path = format!("{job_dir}/wrapper.mjs"); let mut bun_cmd = Command::new(&*BUN_PATH); + let args = if codebase.is_some() { + vec!["run", &script_path] + } else { + vec![ + "run", + "-i", + "--prefer-offline", + "-r", + "./loader.bun.js", + &script_path, + ] + }; bun_cmd .current_dir(job_dir) .env_clear() .envs(envs) .envs(reserved_variables) .envs(common_bun_proc_envs) - .args(vec![ - "run", - "-i", - "--prefer-offline", - "-r", - "./loader.bun.ts", - &script_path, - ]) + .args(args) .stdout(Stdio::piped()) .stderr(Stdio::piped()); bun_cmd @@ -824,6 +899,7 @@ use std::sync::Arc; #[cfg(feature = "enterprise")] pub async fn start_worker( requirements_o: Option, + codebase: Option, db: &sqlx::Pool, inner_content: &str, base_internal_url: &str, @@ -840,7 +916,13 @@ pub async fn start_worker( let mut logs = "".to_string(); let mut mem_peak: i32 = 0; let mut canceled_by: Option = None; - let _ = write_file(job_dir, "main.ts", inner_content).await?; + tracing::info!("Starting worker {w_id};{script_path} (codebase: {codebase:?}"); + if !codebase.is_some() { + let _ = write_file(job_dir, "main.ts", inner_content).await?; + } else { + let _ = write_file(job_dir, "package.json", r#"{ "type": "module" }"#).await?; + } + let common_bun_proc_envs: HashMap = get_common_bun_proc_envs(&base_internal_url).await; @@ -867,8 +949,10 @@ pub async fn start_worker( ) .await; let context_envs = build_envs_map(context.to_vec()).await; - let annotation = get_annotation(inner_content); - if let Some(reqs) = requirements_o { + + if let Some(codebase) = codebase.as_ref() { + pull_codebase(w_id, codebase, job_dir).await?; + } else if let Some(reqs) = requirements_o { let splitted = reqs.split(BUN_LOCKB_SPLIT).collect::>(); if splitted.len() != 2 { return Err(error::Error::ExecutionErr( @@ -959,10 +1043,15 @@ pub async fn start_worker( "" }; + let main_import = if codebase.is_some() { + "./main.js" + } else { + "./main.ts" + }; let wrapper_content: String = format!( r#" -import {{ main }} from "./main.ts"; -import {{ createInterface }} from "node:readline" +import * as Main from "{main_import}"; +import * as Readline from "node:readline" BigInt.prototype.toJSON = function () {{ return this.toString(); @@ -972,7 +1061,7 @@ BigInt.prototype.toJSON = function () {{ console.log('start'); -for await (const line of createInterface({{ input: process.stdin }})) {{ +for await (const line of Readline.createInterface({{ input: process.stdin }})) {{ {print_lines} if (line === "end") {{ @@ -980,7 +1069,7 @@ for await (const line of createInterface({{ input: process.stdin }})) {{ }} try {{ let {{ {spread} }} = JSON.parse(line) - let res: any = await main(...[ {spread} ]); + let res = await Main.main(...[ {spread} ]); console.log("wm_res[success]:" + JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value)); }} catch (e) {{ console.log("wm_res[error]:" + JSON.stringify({{ message: e.message, name: e.name, stack: e.stack, line: line }})); @@ -988,20 +1077,22 @@ for await (const line of createInterface({{ input: process.stdin }})) {{ }} "#, ); - write_file(job_dir, "wrapper.ts", &wrapper_content).await?; + write_file(job_dir, "wrapper.mjs", &wrapper_content).await?; } - build_loader( - job_dir, - base_internal_url, - token, - w_id, - script_path, - annotation.nodejs_mode, - ) - .await?; + if !codebase.is_some() { + build_loader( + job_dir, + base_internal_url, + token, + w_id, + script_path, + annotation.nodejs_mode, + ) + .await?; + } - if annotation.nodejs_mode { + if annotation.nodejs_mode && !codebase.is_some() { generate_wrapper_mjs( job_dir, w_id, @@ -1050,8 +1141,8 @@ for await (const line of createInterface({{ input: process.stdin }})) {{ "-i", "--prefer-offline", "-r", - "./loader.bun.ts", - &format!("{job_dir}/wrapper.ts"), + "./loader.bun.js", + &format!("{job_dir}/wrapper.mjs"), ], killpill_rx, job_completed_tx, diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index 836765a8fd..fb2e9d4cac 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -26,7 +26,7 @@ use windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS; use windmill_common::s3_helpers::{ get_etag_or_empty, LargeFileStorage, ObjectStoreResource, S3Object, }; -use windmill_common::worker::{CLOUD_HOSTED, WORKER_CONFIG}; +use windmill_common::worker::{CLOUD_HOSTED, TMP_DIR, WORKER_CONFIG}; use windmill_common::{ error::{self, Error}, jobs::QueuedJob, @@ -39,6 +39,7 @@ use windmill_queue::{append_logs, CanceledBy}; #[cfg(any(target_os = "linux", target_os = "macos"))] use std::os::unix::process::ExitStatusExt; +use std::process::ExitStatus; use std::sync::atomic::AtomicU32; use std::sync::Arc; use std::{ @@ -69,7 +70,7 @@ use futures::{ use crate::{ AuthedClient, AuthedClientBackgroundTask, JOB_DEFAULT_TIMEOUT, MAX_RESULT_SIZE, - MAX_TIMEOUT_DURATION, MAX_WAIT_FOR_SIGINT, MAX_WAIT_FOR_SIGTERM, ROOT_CACHE_DIR, TMP_DIR, + MAX_TIMEOUT_DURATION, MAX_WAIT_FOR_SIGINT, MAX_WAIT_FOR_SIGTERM, ROOT_CACHE_DIR, }; pub async fn build_args_map<'a>( @@ -482,7 +483,7 @@ pub async fn run_future_with_polling_update_job_poller( result_f: Fut, worker_name: &str, w_id: &str, -) -> anyhow::Result +) -> error::Result where Fut: Future>, { @@ -514,12 +515,22 @@ where tracing::error!("Query timeout: {}", e); Error::ExecutionErr(format!("Query timeout after (>{}s)", timeout_ms/1000)) })?, - _ = update_job, if job_id != Uuid::nil() => Err(Error::ExecutionErr("Job cancelled".to_string())).map_err(to_anyhow)?, + ex = update_job, if job_id != Uuid::nil() => { + match ex { + UpdateJobPollingExit::Done => Err(Error::ExecutionErr("Job cancelled".to_string())).map_err(to_anyhow)?, + UpdateJobPollingExit::AlreadyCompleted => Err(Error::AlreadyCompleted("Job already completed".to_string())).map_err(to_anyhow)?, + } + } }?; drop(tx); Ok(rows) } +pub enum UpdateJobPollingExit { + Done, + AlreadyCompleted, +} + pub async fn update_job_poller( job_id: Uuid, db: &DB, @@ -529,13 +540,14 @@ pub async fn update_job_poller( worker_name: &str, w_id: &str, mut rx: broadcast::Receiver<()>, -) where +) -> UpdateJobPollingExit +where F: Fn() -> Fut, Fut: Future, { let update_job_interval = Duration::from_millis(500); if job_id == Uuid::nil() { - return; + return UpdateJobPollingExit::Done; } let db = db.clone(); @@ -556,7 +568,9 @@ pub async fn update_job_poller( i+=1; if i % 10 == 0 { sqlx::query!( - "UPDATE worker_ping SET ping_at = now() WHERE worker = $1", + "UPDATE worker_ping SET ping_at = now(), current_job_id = $1, current_job_workspace_id = $2 WHERE worker = $3", + &job_id, + &w_id, &worker_name ) .execute(&db) @@ -593,28 +607,36 @@ pub async fn update_job_poller( } } - let (canceled, canceled_by, canceled_reason) = sqlx::query_as::<_, (bool, Option, Option)>("UPDATE queue SET mem_peak = $1, last_ping = now() WHERE id = $2 RETURNING canceled, canceled_by, canceled_reason") + let (canceled, canceled_by, canceled_reason, already_completed) = sqlx::query_as::<_, (bool, Option, Option, bool)>("UPDATE queue SET mem_peak = $1, last_ping = now() WHERE id = $2 RETURNING canceled, canceled_by, canceled_reason, false") .bind(*mem_peak) .bind(job_id) .fetch_optional(&db) .await .unwrap_or_else(|e| { tracing::error!(%e, "error updating job {job_id}: {e}"); - Some((false, None, None)) + Some((false, None, None, false)) }) - .unwrap_or((false, None, None)); + .unwrap_or_else(|| { + // if the job is not in queue, it can only be in the completed_job so it is already complete + (false, None, None, true) + }); + if already_completed { + return UpdateJobPollingExit::AlreadyCompleted + } if canceled { canceled_by_ref.replace(CanceledBy { username: canceled_by.clone(), reason: canceled_reason.clone(), }); - break; + break } } }, ); } tracing::info!("job {job_id} finished"); + + UpdateJobPollingExit::Done } pub enum CompactLogs { @@ -630,7 +652,7 @@ async fn compact_logs( nlogs: String, total_size: Arc, compact_kind: CompactLogs, - worker_name: &str, + _worker_name: &str, ) -> error::Result<(String, String)> { let size = sqlx::query_scalar!( "SELECT char_length(logs) FROM job_logs WHERE job_id = $1 AND workspace_id = $2", @@ -695,9 +717,9 @@ async fn compact_logs( ); let mut new_current_logs = match compact_kind { - CompactLogs::NoS3 => format!("[windmill] worker {worker_name}: Logs length has exceeded a threshold\n[windmill] Previous logs have been saved to disk at {path}, add object storage in the instance settings to save it on distributed storage and allow direct download from Windmill\n"), + CompactLogs::NoS3 => format!("[windmill] No object storage set in instance settings. Previous logs have been saved to disk at {path}\n"), CompactLogs::S3 => format!("[windmill] Previous logs have been saved to object storage at {path}\n"), - CompactLogs::NotEE => format!("[windmill] worker {worker_name}: Logs length has exceeded a threshold\n[windmill] Previous logs have been saved to disk at {path}\n[windmill] Upgrade to EE and add object storage to save it persistentely on distributed storage and allow direct download from Windmill\n"), + CompactLogs::NotEE => format!("[windmill] Previous logs have been saved to disk at {path}\n"), }; new_current_logs.push_str(¤t_logs); @@ -897,6 +919,7 @@ pub async fn handle_child( TooManyLogs, Timeout, Cancelled, + AlreadyCompleted, } let (timeout_duration, timeout_warn_msg) = @@ -914,7 +937,10 @@ pub async fn handle_child( result = child.wait() => return result.map(Ok), Ok(()) = too_many_logs.changed() => KillReason::TooManyLogs, _ = sleep(timeout_duration) => KillReason::Timeout, - _ = update_job, if job_id != Uuid::nil() => KillReason::Cancelled, + ex = update_job, if job_id != Uuid::nil() => match ex { + UpdateJobPollingExit::Done => KillReason::Cancelled, + UpdateJobPollingExit::AlreadyCompleted => KillReason::AlreadyCompleted, + }, }; tx.send(()).expect("rx should never be dropped"); drop(tx); @@ -1120,26 +1146,7 @@ pub async fn handle_child( _ if *too_many_logs.borrow() => Err(Error::ExecutionErr(format!( "logs or result reached limit. (current max size: {MAX_RESULT_SIZE} characters)" ))), - Ok(Ok(status)) => { - if status.success() { - Ok(()) - } else if let Some(code) = status.code() { - Err(error::Error::ExitStatus(code)) - } else { - #[cfg(any(target_os = "linux", target_os = "macos"))] - return Err(error::Error::ExecutionErr(format!( - "process terminated by signal: {:#?}, stopped_signal: {:#?}, core_dumped: {}", - status.signal(), - status.stopped_signal(), - status.core_dumped() - ))); - - #[cfg(not(any(target_os = "linux", target_os = "macos")))] - return Err(error::Error::ExecutionErr(String::from( - "process terminated by signal", - ))); - } - } + Ok(Ok(status)) => process_status(status), Ok(Err(kill_reason)) => Err(Error::ExecutionErr(format!( "job process killed because {kill_reason:#?}" ))), @@ -1147,6 +1154,26 @@ pub async fn handle_child( } } +pub fn process_status(status: ExitStatus) -> error::Result<()> { + if status.success() { + Ok(()) + } else if let Some(code) = status.code() { + Err(error::Error::ExitStatus(code)) + } else { + #[cfg(any(target_os = "linux", target_os = "macos"))] + return Err(error::Error::ExecutionErr(format!( + "process terminated by signal: {:#?}, stopped_signal: {:#?}, core_dumped: {}", + status.signal(), + status.stopped_signal(), + status.core_dumped() + ))); + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + return Err(error::Error::ExecutionErr(String::from( + "process terminated by signal", + ))); + } +} pub async fn start_child_process(mut cmd: Command, executable: &str) -> Result { return cmd .spawn() diff --git a/backend/windmill-worker/src/dedicated_worker.rs b/backend/windmill-worker/src/dedicated_worker.rs index 426eb2204b..284008232c 100644 --- a/backend/windmill-worker/src/dedicated_worker.rs +++ b/backend/windmill-worker/src/dedicated_worker.rs @@ -16,7 +16,8 @@ use std::{collections::VecDeque, process::Stdio, sync::Arc}; use anyhow::Context; use crate::{ - common::start_child_process, JobCompleted, JobCompletedSender, MAX_BUFFERED_DEDICATED_JOBS, + common::{process_status, start_child_process}, + JobCompleted, JobCompletedSender, MAX_BUFFERED_DEDICATED_JOBS, }; use futures::{future, Future}; @@ -108,7 +109,11 @@ pub async fn handle_dedicated_process( .wait() .await .expect("child process encountered an error"); - tracing::info!("child status was: {}", status); + if let Err(e) = process_status(status) { + tracing::error!("child exit status was not success: {e}"); + } else { + tracing::info!("child exist status was success"); + } }); let mut jobs = VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS); diff --git a/backend/windmill-worker/src/global_cache.rs b/backend/windmill-worker/src/global_cache.rs index e9553943e3..a1887d60f8 100644 --- a/backend/windmill-worker/src/global_cache.rs +++ b/backend/windmill-worker/src/global_cache.rs @@ -71,50 +71,10 @@ pub async fn build_tar_and_push( Ok(()) } -#[cfg(all(feature = "enterprise", feature = "parquet"))] -pub async fn attempt_fetch_bytes( - client: Arc, - path: &str, -) -> error::Result { - use object_store::path::Path; - - let object = client.get(&Path::from(path.clone())).await; - if let Err(e) = object { - tracing::info!( - "Failed to pull bytes from object store at path {path}. Error: {:?}", - e - ); - return Err(error::Error::ExecutionErr(format!( - "Failed to pull bytes from object store: {path}" - ))); - } - - let bytes = object.unwrap().bytes().await; - if bytes.is_err() { - tracing::info!( - "Failed to read bytes from object store: {path}. Error: {:?}", - bytes.err() - ); - return Err(error::Error::ExecutionErr(format!( - "Failed to read bytes from object store: {path}" - ))); - } - let bytes = bytes.unwrap(); - - tracing::info!("{path} len: {}", bytes.len()); - - if bytes.len() == 0 { - tracing::info!("object {path} not found in bucket, bytes empty",); - return Err(error::Error::ExecutionErr(format!( - "object {path} does not exist in bucket" - ))); - } - - return Ok(bytes); -} - #[cfg(all(feature = "enterprise", feature = "parquet"))] pub async fn pull_from_tar(client: Arc, folder: String) -> error::Result<()> { + use windmill_common::s3_helpers::attempt_fetch_bytes; + let folder_name = folder.split("/").last().unwrap(); tracing::info!("Attempting to pull piptar {folder_name} from bucket"); @@ -125,7 +85,7 @@ pub async fn pull_from_tar(client: Arc, folder: String) -> erro // tracing::info!("B: {target} {folder}"); - extract_pip_tar(bytes, &folder).await.map_err(|e| { + extract_tar(bytes, &folder).await.map_err(|e| { tracing::error!("Failed to extract piptar {folder_name}. Error: {:?}", e); e })?; @@ -139,7 +99,7 @@ pub async fn pull_from_tar(client: Arc, folder: String) -> erro } #[cfg(all(feature = "enterprise", feature = "parquet"))] -pub async fn extract_pip_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { +pub async fn extract_tar(tar: bytes::Bytes, folder: &str) -> error::Result<()> { use bytes::Buf; use tokio::fs::{self}; @@ -149,14 +109,14 @@ pub async fn extract_pip_tar(tar: bytes::Bytes, folder: &str) -> error::Result<( let mut ar = tar::Archive::new(tar.reader()); if let Err(e) = ar.unpack(folder) { - tracing::info!("Failed to untar piptar. Error: {:?}", e); + tracing::info!("Failed to untar to {folder}. Error: {:?}", e); fs::remove_dir_all(&folder).await?; return Err(error::Error::ExecutionErr(format!( "Failed to untar piptar {folder}" ))); } tracing::info!( - "Finished extracting pip tar {folder}. Took {}ms", + "Finished extracting tar to {folder}. Took {}ms", start.elapsed().as_millis(), ); Ok(()) diff --git a/backend/windmill-worker/src/go_executor.rs b/backend/windmill-worker/src/go_executor.rs index 53562107af..129389ec2a 100644 --- a/backend/windmill-worker/src/go_executor.rs +++ b/backend/windmill-worker/src/go_executor.rs @@ -120,7 +120,7 @@ async fn load_cache(bin_path: &str, _hash: &str) -> (bool, String) { .await .clone() { - use crate::global_cache::attempt_fetch_bytes; + use windmill_common::s3_helpers::attempt_fetch_bytes; if let Ok(mut x) = attempt_fetch_bytes(os, &hash_to_os_path(_hash)).await { if let Err(e) = write_binary_file(bin_path, &mut x).await { diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index f3c547d319..91d9365ead 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -579,6 +579,7 @@ pub struct LogString { } pub async fn eval_fetch_timeout( + env_code: String, ts_expr: String, js_expr: String, args: Option<&Json>>>, @@ -681,7 +682,7 @@ pub async fn eval_fetch_timeout( let future = async { tokio::select! { - r = eval_fetch(&mut js_runtime, &js_expr) => Ok(r), + r = eval_fetch(&mut js_runtime, &js_expr, Some(env_code)) => Ok(r), _ = memory_limit_rx.recv() => Err(Error::ExecutionErr("Memory limit reached, killing isolate".to_string())) } }; @@ -722,11 +723,26 @@ pub async fn eval_fetch_timeout( Ok(r) } -async fn eval_fetch(js_runtime: &mut JsRuntime, expr: &str) -> anyhow::Result> { +const WINDMILL_CLIENT: &str = include_str!("./windmill-client.js"); + +async fn eval_fetch( + js_runtime: &mut JsRuntime, + expr: &str, + env_code: Option, +) -> anyhow::Result> { + if let Some(env_code) = env_code.as_ref() { + let _ = js_runtime + .load_side_module( + &deno_core::resolve_url("file:///windmill.ts")?, + Some(format!("{env_code}\n{}", WINDMILL_CLIENT.to_string()).into()), + ) + .await?; + } + let _ = js_runtime .load_side_module( &deno_core::resolve_url("file:///eval.ts")?, - Some(expr.to_string().into()), + Some(format!("{}\n{expr}", env_code.unwrap_or_default()).into()), ) .await?; diff --git a/backend/windmill-worker/src/runtime.js b/backend/windmill-worker/src/runtime.js index 07de02c1cb..4c119b2f1a 100644 --- a/backend/windmill-worker/src/runtime.js +++ b/backend/windmill-worker/src/runtime.js @@ -36,6 +36,8 @@ globalThis.FileReader = fileReader.FileReader; globalThis.console = new console.Console((msg, level) => globalThis.Deno.core.ops.op_log(msg) ); +globalThis.AbortController = abortSignal.AbortController; +globalThis.AbortSignal = abortSignal.AbortSignal; // Object.assign(globalThis, { // console: nonEnumerable( // new console.Console((msg, level) => core.print(msg, level > 1)) @@ -122,8 +124,8 @@ globalThis.console = new console.Console((msg, level) => // FormData: nonEnumerable(formData.FormData), // // abort signal -// AbortController: nonEnumerable(abortSignal.AbortController), -// AbortSignal: nonEnumerable(abortSignal.AbortSignal), +// AbortController: nonEnumerable(abortSignal.AbortController), +// AbortSignal: nonEnumerable(abortSignal.AbortSignal), // // // web sockets // // WebSocket: nonEnumerable(webSocket.WebSocket), diff --git a/backend/windmill-worker/src/windmill-client.js b/backend/windmill-worker/src/windmill-client.js new file mode 100644 index 0000000000..ea13a6e43c --- /dev/null +++ b/backend/windmill-worker/src/windmill-client.js @@ -0,0 +1,10380 @@ +// src/core/ApiError.ts +var ApiError = class extends Error { + constructor(request2, response, message) { + super(message); + this.name = "ApiError"; + this.url = response.url; + this.status = response.status; + this.statusText = response.statusText; + this.body = response.body; + this.request = request2; + } +}; + +// src/core/CancelablePromise.ts +var CancelError = class extends Error { + constructor(message) { + super(message); + this.name = "CancelError"; + } + get isCancelled() { + return true; + } +}; +var CancelablePromise = class { + constructor(executor) { + this._isResolved = false; + this._isRejected = false; + this._isCancelled = false; + this.cancelHandlers = []; + this.promise = new Promise((resolve2, reject) => { + this._resolve = resolve2; + this._reject = reject; + const onResolve = (value) => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isResolved = true; + if (this._resolve) this._resolve(value); + }; + const onReject = (reason) => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isRejected = true; + if (this._reject) this._reject(reason); + }; + const onCancel = (cancelHandler) => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this.cancelHandlers.push(cancelHandler); + }; + Object.defineProperty(onCancel, "isResolved", { + get: () => this._isResolved, + }); + Object.defineProperty(onCancel, "isRejected", { + get: () => this._isRejected, + }); + Object.defineProperty(onCancel, "isCancelled", { + get: () => this._isCancelled, + }); + return executor(onResolve, onReject, onCancel); + }); + } + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + then(onFulfilled, onRejected) { + return this.promise.then(onFulfilled, onRejected); + } + catch(onRejected) { + return this.promise.catch(onRejected); + } + finally(onFinally) { + return this.promise.finally(onFinally); + } + cancel() { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isCancelled = true; + if (this.cancelHandlers.length) { + try { + for (const cancelHandler of this.cancelHandlers) { + cancelHandler(); + } + } catch (error) { + console.warn("Cancellation threw an error", error); + return; + } + } + this.cancelHandlers.length = 0; + if (this._reject) this._reject(new CancelError("Request aborted")); + } + get isCancelled() { + return this._isCancelled; + } +}; + +// src/core/OpenAPI.ts +var Interceptors = class { + constructor() { + this._fns = []; + } + eject(fn) { + const index = this._fns.indexOf(fn); + if (index !== -1) { + this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; + } + } + use(fn) { + this._fns = [...this._fns, fn]; + } +}; +var OpenAPI = { + BASE: "/api", + CREDENTIALS: "include", + ENCODE_PATH: void 0, + HEADERS: void 0, + PASSWORD: void 0, + TOKEN: void 0, + USERNAME: void 0, + VERSION: "1.326.0", + WITH_CREDENTIALS: false, + interceptors: { + request: new Interceptors(), + response: new Interceptors(), + }, +}; + +// src/schemas.gen.ts +var $Script = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + hash: { + type: "string", + }, + path: { + type: "string", + }, + parent_hashes: { + type: "array", + description: `The first element is the direct parent of the script, the second is the parent of the first, etc +`, + items: { + type: "string", + }, + }, + summary: { + type: "string", + }, + description: { + type: "string", + }, + content: { + type: "string", + }, + created_by: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + archived: { + type: "boolean", + }, + schema: { + type: "object", + }, + deleted: { + type: "boolean", + }, + is_template: { + type: "boolean", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + lock: { + type: "string", + }, + lock_error_logs: { + type: "string", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + kind: { + type: "string", + enum: ["script", "failure", "trigger", "command", "approval"], + }, + starred: { + type: "boolean", + }, + tag: { + type: "string", + }, + has_draft: { + type: "boolean", + }, + draft_only: { + type: "boolean", + }, + envs: { + type: "array", + items: { + type: "string", + }, + }, + concurrent_limit: { + type: "integer", + }, + concurrency_time_window_s: { + type: "integer", + }, + cache_ttl: { + type: "number", + }, + dedicated_worker: { + type: "boolean", + }, + ws_error_handler_muted: { + type: "boolean", + }, + priority: { + type: "integer", + }, + restart_unless_cancelled: { + type: "boolean", + }, + timeout: { + type: "integer", + }, + delete_after_use: { + type: "boolean", + }, + visible_to_runner_only: { + type: "boolean", + }, + no_main_func: { + type: "boolean", + }, + codebase: { + type: "string", + }, + }, + required: [ + "hash", + "path", + "summary", + "description", + "content", + "created_by", + "created_at", + "archived", + "deleted", + "is_template", + "extra_perms", + "language", + "kind", + "starred", + "no_main_func", + ], +}; +var $NewScript = { + type: "object", + properties: { + path: { + type: "string", + }, + parent_hash: { + type: "string", + }, + summary: { + type: "string", + }, + description: { + type: "string", + }, + content: { + type: "string", + }, + schema: { + type: "object", + }, + is_template: { + type: "boolean", + }, + lock: { + type: "string", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + kind: { + type: "string", + enum: ["script", "failure", "trigger", "command", "approval"], + }, + tag: { + type: "string", + }, + draft_only: { + type: "boolean", + }, + envs: { + type: "array", + items: { + type: "string", + }, + }, + concurrent_limit: { + type: "integer", + }, + concurrency_time_window_s: { + type: "integer", + }, + cache_ttl: { + type: "number", + }, + dedicated_worker: { + type: "boolean", + }, + ws_error_handler_muted: { + type: "boolean", + }, + priority: { + type: "integer", + }, + restart_unless_cancelled: { + type: "boolean", + }, + timeout: { + type: "integer", + }, + delete_after_use: { + type: "boolean", + }, + deployment_message: { + type: "string", + }, + concurrency_key: { + type: "string", + }, + visible_to_runner_only: { + type: "boolean", + }, + no_main_func: { + type: "boolean", + }, + codebase: { + type: "string", + }, + }, + required: ["path", "summary", "description", "content", "language"], +}; +var $NewScriptWithDraft = { + allOf: [ + { + $ref: "#/components/schemas/NewScript", + }, + { + type: "object", + properties: { + draft: { + $ref: "#/components/schemas/NewScript", + }, + hash: { + type: "string", + }, + }, + required: ["hash"], + }, + ], +}; +var $ScriptHistory = { + type: "object", + properties: { + script_hash: { + type: "string", + }, + deployment_msg: { + type: "string", + }, + }, + required: ["script_hash"], +}; +var $ScriptArgs = { + type: "object", + additionalProperties: {}, +}; +var $Input = { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + args: { + type: "object", + }, + created_by: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + is_public: { + type: "boolean", + }, + success: { + type: "boolean", + }, + }, + required: ["id", "name", "args", "created_by", "created_at", "is_public"], +}; +var $CreateInput = { + type: "object", + properties: { + name: { + type: "string", + }, + args: { + type: "object", + }, + }, + required: ["name", "args", "created_by"], +}; +var $UpdateInput = { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + is_public: { + type: "boolean", + }, + }, + required: ["id", "name", "is_public"], +}; +var $RunnableType = { + type: "string", + enum: ["ScriptHash", "ScriptPath", "FlowPath"], +}; +var $QueuedJob = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + id: { + type: "string", + format: "uuid", + }, + parent_job: { + type: "string", + format: "uuid", + }, + created_by: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + started_at: { + type: "string", + format: "date-time", + }, + scheduled_for: { + type: "string", + format: "date-time", + }, + running: { + type: "boolean", + }, + script_path: { + type: "string", + }, + script_hash: { + type: "string", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + logs: { + type: "string", + }, + raw_code: { + type: "string", + }, + canceled: { + type: "boolean", + }, + canceled_by: { + type: "string", + }, + canceled_reason: { + type: "string", + }, + last_ping: { + type: "string", + format: "date-time", + }, + job_kind: { + type: "string", + enum: [ + "script", + "preview", + "dependencies", + "flowdependencies", + "appdependencies", + "flow", + "flowpreview", + "script_hub", + "identity", + "deploymentcallback", + "singlescriptflow", + ], + }, + schedule_path: { + type: "string", + }, + permissioned_as: { + type: "string", + description: `The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +`, + }, + flow_status: { + $ref: "#/components/schemas/FlowStatus", + }, + raw_flow: { + $ref: "#/components/schemas/FlowValue", + }, + is_flow_step: { + type: "boolean", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + email: { + type: "string", + }, + visible_to_owner: { + type: "boolean", + }, + mem_peak: { + type: "integer", + }, + tag: { + type: "string", + }, + priority: { + type: "integer", + }, + }, + required: [ + "id", + "running", + "canceled", + "job_kind", + "permissioned_as", + "is_flow_step", + "email", + "visible_to_owner", + "tag", + ], +}; +var $CompletedJob = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + id: { + type: "string", + format: "uuid", + }, + parent_job: { + type: "string", + format: "uuid", + }, + created_by: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + started_at: { + type: "string", + format: "date-time", + }, + duration_ms: { + type: "integer", + }, + success: { + type: "boolean", + }, + script_path: { + type: "string", + }, + script_hash: { + type: "string", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + result: {}, + logs: { + type: "string", + }, + deleted: { + type: "boolean", + }, + raw_code: { + type: "string", + }, + canceled: { + type: "boolean", + }, + canceled_by: { + type: "string", + }, + canceled_reason: { + type: "string", + }, + job_kind: { + type: "string", + enum: [ + "script", + "preview", + "dependencies", + "flow", + "flowdependencies", + "appdependencies", + "flowpreview", + "script_hub", + "identity", + "deploymentcallback", + "singlescriptflow", + ], + }, + schedule_path: { + type: "string", + }, + permissioned_as: { + type: "string", + description: `The user (u/userfoo) or group (g/groupfoo) whom +the execution of this script will be permissioned_as and by extension its DT_TOKEN. +`, + }, + flow_status: { + $ref: "#/components/schemas/FlowStatus", + }, + raw_flow: { + $ref: "#/components/schemas/FlowValue", + }, + is_flow_step: { + type: "boolean", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + is_skipped: { + type: "boolean", + }, + email: { + type: "string", + }, + visible_to_owner: { + type: "boolean", + }, + mem_peak: { + type: "integer", + }, + tag: { + type: "string", + }, + priority: { + type: "integer", + }, + labels: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: [ + "id", + "created_by", + "duration_ms", + "created_at", + "started_at", + "success", + "canceled", + "job_kind", + "permissioned_as", + "is_flow_step", + "is_skipped", + "email", + "visible_to_owner", + "tag", + ], +}; +var $Job = { + oneOf: [ + { + allOf: [ + { + $ref: "#/components/schemas/CompletedJob", + }, + { + type: "object", + properties: { + type: { + type: "string", + enum: ["CompletedJob"], + }, + }, + }, + ], + }, + { + allOf: [ + { + $ref: "#/components/schemas/QueuedJob", + }, + { + type: "object", + properties: { + type: { + type: "string", + enum: ["QueuedJob"], + }, + }, + }, + ], + }, + ], + discriminator: { + propertyName: "type", + }, +}; +var $User = { + type: "object", + properties: { + email: { + type: "string", + }, + username: { + type: "string", + }, + is_admin: { + type: "boolean", + }, + is_super_admin: { + type: "boolean", + }, + created_at: { + type: "string", + format: "date-time", + }, + operator: { + type: "boolean", + }, + disabled: { + type: "boolean", + }, + groups: { + type: "array", + items: { + type: "string", + }, + }, + folders: { + type: "array", + items: { + type: "string", + }, + }, + folders_owners: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: [ + "email", + "username", + "is_admin", + "is_super_admin", + "created_at", + "operator", + "disabled", + "folders", + "folders_owners", + ], +}; +var $UserUsage = { + type: "object", + properties: { + email: { + type: "string", + }, + executions: { + type: "number", + }, + }, +}; +var $Login = { + type: "object", + properties: { + email: { + type: "string", + }, + password: { + type: "string", + }, + }, + required: ["email", "password"], +}; +var $EditWorkspaceUser = { + type: "object", + properties: { + is_admin: { + type: "boolean", + }, + operator: { + type: "boolean", + }, + disabled: { + type: "boolean", + }, + }, +}; +var $TruncatedToken = { + type: "object", + properties: { + label: { + type: "string", + }, + expiration: { + type: "string", + format: "date-time", + }, + token_prefix: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + last_used_at: { + type: "string", + format: "date-time", + }, + scopes: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: ["token_prefix", "created_at", "last_used_at"], +}; +var $NewToken = { + type: "object", + properties: { + label: { + type: "string", + }, + expiration: { + type: "string", + format: "date-time", + }, + scopes: { + type: "array", + items: { + type: "string", + }, + }, + }, +}; +var $NewTokenImpersonate = { + type: "object", + properties: { + label: { + type: "string", + }, + expiration: { + type: "string", + format: "date-time", + }, + impersonate_email: { + type: "string", + }, + }, + required: ["impersonate_email"], +}; +var $ListableVariable = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + value: { + type: "string", + }, + is_secret: { + type: "boolean", + }, + description: { + type: "string", + }, + account: { + type: "integer", + }, + is_oauth: { + type: "boolean", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + is_expired: { + type: "boolean", + }, + refresh_error: { + type: "string", + }, + is_linked: { + type: "boolean", + }, + is_refreshed: { + type: "boolean", + }, + }, + required: ["workspace_id", "path", "is_secret", "extra_perms"], +}; +var $ContextualVariable = { + type: "object", + properties: { + name: { + type: "string", + }, + value: { + type: "string", + }, + description: { + type: "string", + }, + is_custom: { + type: "boolean", + }, + }, + required: ["name", "value", "description", "is_custom"], +}; +var $CreateVariable = { + type: "object", + properties: { + path: { + type: "string", + }, + value: { + type: "string", + }, + is_secret: { + type: "boolean", + }, + description: { + type: "string", + }, + account: { + type: "integer", + }, + is_oauth: { + type: "boolean", + }, + }, + required: ["path", "value", "is_secret", "description"], +}; +var $EditVariable = { + type: "object", + properties: { + path: { + type: "string", + }, + value: { + type: "string", + }, + is_secret: { + type: "boolean", + }, + description: { + type: "string", + }, + }, +}; +var $AuditLog = { + type: "object", + properties: { + id: { + type: "integer", + }, + timestamp: { + type: "string", + format: "date-time", + }, + username: { + type: "string", + }, + operation: { + type: "string", + enum: [ + "jobs.run", + "jobs.run.script", + "jobs.run.preview", + "jobs.run.flow", + "jobs.run.flow_preview", + "jobs.run.script_hub", + "jobs.run.dependencies", + "jobs.run.identity", + "jobs.run.noop", + "jobs.flow_dependencies", + "jobs", + "jobs.cancel", + "jobs.force_cancel", + "jobs.disapproval", + "jobs.delete", + "account.delete", + "openai.request", + "resources.create", + "resources.update", + "resources.delete", + "resource_types.create", + "resource_types.update", + "resource_types.delete", + "schedule.create", + "schedule.setenabled", + "schedule.edit", + "schedule.delete", + "scripts.create", + "scripts.update", + "scripts.archive", + "scripts.delete", + "users.create", + "users.delete", + "users.update", + "users.login", + "users.logout", + "users.accept_invite", + "users.decline_invite", + "users.token.create", + "users.token.delete", + "users.add_to_workspace", + "users.add_global", + "users.setpassword", + "users.impersonate", + "users.leave_workspace", + "oauth.login", + "oauth.signup", + "variables.create", + "variables.delete", + "variables.update", + "flows.create", + "flows.update", + "flows.delete", + "flows.archive", + "apps.create", + "apps.update", + "apps.delete", + "folder.create", + "folder.update", + "folder.delete", + "folder.add_owner", + "folder.remove_owner", + "group.create", + "group.delete", + "group.edit", + "group.adduser", + "group.removeuser", + "igroup.create", + "igroup.delete", + "igroup.adduser", + "igroup.removeuser", + "variables.decrypt_secret", + "workspaces.edit_command_script", + "workspaces.edit_deploy_to", + "workspaces.edit_auto_invite_domain", + "workspaces.edit_webhook", + "workspaces.edit_copilot_config", + "workspaces.edit_error_handler", + "workspaces.create", + "workspaces.update", + "workspaces.archive", + "workspaces.unarchive", + "workspaces.delete", + ], + }, + action_kind: { + type: "string", + enum: ["Created", "Updated", "Delete", "Execute"], + }, + resource: { + type: "string", + }, + parameters: { + type: "object", + }, + }, + required: ["id", "timestamp", "username", "operation", "action_kind"], +}; +var $MainArgSignature = { + type: "object", + properties: { + type: { + type: "string", + enum: ["Valid", "Invalid"], + }, + error: { + type: "string", + }, + star_args: { + type: "boolean", + }, + star_kwargs: { + type: "boolean", + }, + args: { + type: "array", + items: { + type: "object", + properties: { + name: { + type: "string", + }, + typ: { + oneOf: [ + { + type: "string", + enum: [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ], + }, + { + type: "object", + properties: { + resource: { + type: "string", + nullable: true, + }, + }, + required: ["resource"], + }, + { + type: "object", + properties: { + str: { + type: "array", + items: { + type: "string", + }, + nullable: true, + }, + }, + required: ["str"], + }, + { + type: "object", + properties: { + object: { + type: "array", + items: { + type: "object", + properties: { + key: { + type: "string", + }, + typ: { + oneOf: [ + { + type: "string", + enum: [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ], + }, + { + type: "object", + properties: { + str: {}, + }, + required: ["str"], + }, + ], + }, + }, + required: ["key", "typ"], + }, + }, + }, + required: ["object"], + }, + { + type: "object", + properties: { + list: { + oneOf: [ + { + type: "string", + enum: [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ], + }, + { + type: "object", + properties: { + str: {}, + }, + required: ["str"], + }, + ], + nullable: true, + }, + }, + required: ["list"], + }, + ], + }, + has_default: { + type: "boolean", + }, + default: {}, + }, + required: ["name", "typ"], + }, + }, + }, + required: ["star_args", "start_kwargs", "args", "type", "error"], +}; +var $Preview = { + type: "object", + properties: { + content: { + type: "string", + }, + path: { + type: "string", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + tag: { + type: "string", + }, + kind: { + type: "string", + enum: ["code", "identity", "http"], + }, + dedicated_worker: { + type: "boolean", + }, + lock: { + type: "string", + }, + }, + required: ["args"], +}; +var $WorkflowTask = { + type: "object", + properties: { + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + }, + required: ["args"], +}; +var $WorkflowStatusRecord = { + type: "object", + additionalProperties: { + $ref: "#/components/schemas/WorkflowStatus", + }, +}; +var $WorkflowStatus = { + type: "object", + properties: { + scheduled_for: { + type: "string", + format: "date-time", + }, + started_at: { + type: "string", + format: "date-time", + }, + duration_ms: { + type: "number", + }, + name: { + type: "string", + }, + }, +}; +var $CreateResource = { + type: "object", + properties: { + path: { + type: "string", + }, + value: {}, + description: { + type: "string", + }, + resource_type: { + type: "string", + }, + }, + required: ["path", "value", "resource_type"], +}; +var $EditResource = { + type: "object", + properties: { + path: { + type: "string", + }, + description: { + type: "string", + }, + value: {}, + }, +}; +var $Resource = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + description: { + type: "string", + }, + resource_type: { + type: "string", + }, + value: {}, + is_oauth: { + type: "boolean", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + }, + required: ["path", "resource_type", "is_oauth"], +}; +var $ListableResource = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + description: { + type: "string", + }, + resource_type: { + type: "string", + }, + value: {}, + is_oauth: { + type: "boolean", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + is_expired: { + type: "boolean", + }, + refresh_error: { + type: "string", + }, + is_linked: { + type: "boolean", + }, + is_refreshed: { + type: "boolean", + }, + account: { + type: "number", + }, + }, + required: ["path", "resource_type", "is_oauth", "is_linked", "is_refreshed"], +}; +var $ResourceType = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + name: { + type: "string", + }, + schema: {}, + description: { + type: "string", + }, + }, + required: ["name"], +}; +var $EditResourceType = { + type: "object", + properties: { + schema: {}, + description: { + type: "string", + }, + }, +}; +var $Schedule = { + type: "object", + properties: { + path: { + type: "string", + }, + edited_by: { + type: "string", + }, + edited_at: { + type: "string", + format: "date-time", + }, + schedule: { + type: "string", + }, + timezone: { + type: "string", + }, + enabled: { + type: "boolean", + }, + script_path: { + type: "string", + }, + is_flow: { + type: "boolean", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + email: { + type: "string", + }, + error: { + type: "string", + }, + on_failure: { + type: "string", + }, + on_failure_times: { + type: "number", + }, + on_failure_exact: { + type: "boolean", + }, + on_failure_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + on_recovery: { + type: "string", + }, + on_recovery_times: { + type: "number", + }, + on_recovery_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + ws_error_handler_muted: { + type: "boolean", + }, + retry: { + $ref: "#/components/schemas/Retry", + }, + summary: { + type: "string", + }, + no_flow_overlap: { + type: "boolean", + }, + tag: { + type: "string", + }, + }, + required: [ + "path", + "edited_by", + "edited_at", + "schedule", + "script_path", + "timezone", + "extra_perms", + "is_flow", + "enabled", + "email", + ], +}; +var $ScheduleWJobs = { + allOf: [ + { + $ref: "#/components/schemas/Schedule", + }, + { + type: "object", + properties: { + jobs: { + type: "array", + items: { + type: "object", + properties: { + id: { + type: "string", + }, + success: { + type: "boolean", + }, + duration_ms: { + type: "number", + }, + }, + required: ["id", "success", "duration_ms"], + }, + }, + }, + }, + ], +}; +var $NewSchedule = { + type: "object", + properties: { + path: { + type: "string", + }, + schedule: { + type: "string", + }, + timezone: { + type: "string", + }, + script_path: { + type: "string", + }, + is_flow: { + type: "boolean", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + enabled: { + type: "boolean", + }, + on_failure: { + type: "string", + }, + on_failure_times: { + type: "number", + }, + on_failure_exact: { + type: "boolean", + }, + on_failure_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + on_recovery: { + type: "string", + }, + on_recovery_times: { + type: "number", + }, + on_recovery_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + ws_error_handler_muted: { + type: "boolean", + }, + retry: { + $ref: "#/components/schemas/Retry", + }, + no_flow_overlap: { + type: "boolean", + }, + summary: { + type: "string", + }, + tag: { + type: "string", + }, + }, + required: ["path", "schedule", "timezone", "script_path", "is_flow", "args"], +}; +var $EditSchedule = { + type: "object", + properties: { + schedule: { + type: "string", + }, + timezone: { + type: "string", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + on_failure: { + type: "string", + }, + on_failure_times: { + type: "number", + }, + on_failure_exact: { + type: "boolean", + }, + on_failure_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + on_recovery: { + type: "string", + }, + on_recovery_times: { + type: "number", + }, + on_recovery_extra_args: { + $ref: "#/components/schemas/ScriptArgs", + }, + ws_error_handler_muted: { + type: "boolean", + }, + retry: { + $ref: "#/components/schemas/Retry", + }, + no_flow_overlap: { + type: "boolean", + }, + summary: { + type: "string", + }, + tag: { + type: "string", + }, + }, + required: ["schedule", "timezone", "script_path", "is_flow", "args"], +}; +var $Group = { + type: "object", + properties: { + name: { + type: "string", + }, + summary: { + type: "string", + }, + members: { + type: "array", + items: { + type: "string", + }, + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + }, + required: ["name"], +}; +var $InstanceGroup = { + type: "object", + properties: { + name: { + type: "string", + }, + summary: { + type: "string", + }, + emails: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: ["name"], +}; +var $Folder = { + type: "object", + properties: { + name: { + type: "string", + }, + owners: { + type: "array", + items: { + type: "string", + }, + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + }, + required: ["name", "owners", "extra_perms"], +}; +var $WorkerPing = { + type: "object", + properties: { + worker: { + type: "string", + }, + worker_instance: { + type: "string", + }, + last_ping: { + type: "number", + }, + started_at: { + type: "string", + format: "date-time", + }, + ip: { + type: "string", + }, + jobs_executed: { + type: "integer", + }, + custom_tags: { + type: "array", + items: { + type: "string", + }, + }, + worker_group: { + type: "string", + }, + wm_version: { + type: "string", + }, + current_job_id: { + type: "string", + }, + current_job_workspace_id: { + type: "string", + }, + occupancy_rate: { + type: "number", + }, + }, + required: [ + "worker", + "worker_instance", + "ping_at", + "started_at", + "ip", + "jobs_executed", + "worker_group", + "wm_version", + ], +}; +var $UserWorkspaceList = { + type: "object", + properties: { + email: { + type: "string", + }, + workspaces: { + type: "array", + items: { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + username: { + type: "string", + }, + }, + required: ["id", "name", "username"], + }, + }, + }, + required: ["email", "workspaces"], +}; +var $CreateWorkspace = { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + username: { + type: "string", + }, + }, + required: ["id", "name"], +}; +var $Workspace = { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + owner: { + type: "string", + }, + domain: { + type: "string", + }, + }, + required: ["id", "name", "owner"], +}; +var $WorkspaceInvite = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + email: { + type: "string", + }, + is_admin: { + type: "boolean", + }, + operator: { + type: "boolean", + }, + }, + required: ["workspace_id", "email", "is_admin", "operator"], +}; +var $GlobalUserInfo = { + type: "object", + properties: { + email: { + type: "string", + }, + login_type: { + type: "string", + enum: ["password", "github"], + }, + super_admin: { + type: "boolean", + }, + verified: { + type: "boolean", + }, + name: { + type: "string", + }, + company: { + type: "string", + }, + username: { + type: "string", + }, + }, + required: ["email", "login_type", "super_admin", "verified"], +}; +var $Flow = { + allOf: [ + { + $ref: "#/components/schemas/OpenFlow", + }, + { + $ref: "#/components/schemas/FlowMetadata", + }, + ], +}; +var $ExtraPerms = { + type: "object", + additionalProperties: { + type: "boolean", + }, +}; +var $FlowMetadata = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + edited_by: { + type: "string", + }, + edited_at: { + type: "string", + format: "date-time", + }, + archived: { + type: "boolean", + }, + extra_perms: { + $ref: "#/components/schemas/ExtraPerms", + }, + starred: { + type: "boolean", + }, + draft_only: { + type: "boolean", + }, + tag: { + type: "string", + }, + ws_error_handler_muted: { + type: "boolean", + }, + priority: { + type: "integer", + }, + dedicated_worker: { + type: "boolean", + }, + timeout: { + type: "number", + }, + visible_to_runner_only: { + type: "boolean", + }, + }, + required: ["path", "edited_by", "edited_at", "archived", "extra_perms"], +}; +var $OpenFlowWPath = { + allOf: [ + { + $ref: "#/components/schemas/OpenFlow", + }, + { + type: "object", + properties: { + path: { + type: "string", + }, + tag: { + type: "string", + }, + ws_error_handler_muted: { + type: "boolean", + }, + priority: { + type: "integer", + }, + dedicated_worker: { + type: "boolean", + }, + timeout: { + type: "number", + }, + visible_to_runner_only: { + type: "boolean", + }, + }, + required: ["path"], + }, + ], +}; +var $FlowPreview = { + type: "object", + properties: { + value: { + $ref: "#/components/schemas/FlowValue", + }, + path: { + type: "string", + }, + args: { + $ref: "#/components/schemas/ScriptArgs", + }, + tag: { + type: "string", + }, + restarted_from: { + $ref: "#/components/schemas/RestartedFrom", + }, + }, + required: ["value", "content", "args"], +}; +var $RestartedFrom = { + type: "object", + properties: { + flow_job_id: { + type: "string", + format: "uuid", + }, + step_id: { + type: "string", + }, + branch_or_iteration_n: { + type: "integer", + }, + }, +}; +var $Policy = { + type: "object", + properties: { + triggerables: { + type: "object", + additionalProperties: { + type: "object", + }, + }, + triggerables_v2: { + type: "object", + additionalProperties: { + type: "object", + }, + }, + execution_mode: { + type: "string", + enum: ["viewer", "publisher", "anonymous"], + }, + on_behalf_of: { + type: "string", + }, + on_behalf_of_email: { + type: "string", + }, + }, +}; +var $ListableApp = { + type: "object", + properties: { + id: { + type: "integer", + }, + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + summary: { + type: "string", + }, + version: { + type: "integer", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + starred: { + type: "boolean", + }, + edited_at: { + type: "string", + format: "date-time", + }, + execution_mode: { + type: "string", + enum: ["viewer", "publisher", "anonymous"], + }, + }, + required: [ + "id", + "workspace_id", + "path", + "summary", + "version", + "extra_perms", + "edited_at", + "execution_mode", + ], +}; +var $ListableRawApp = { + type: "object", + properties: { + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + summary: { + type: "string", + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + starred: { + type: "boolean", + }, + version: { + type: "number", + }, + edited_at: { + type: "string", + format: "date-time", + }, + }, + required: [ + "workspace_id", + "path", + "summary", + "extra_perms", + "version", + "edited_at", + ], +}; +var $AppWithLastVersion = { + type: "object", + properties: { + id: { + type: "integer", + }, + workspace_id: { + type: "string", + }, + path: { + type: "string", + }, + summary: { + type: "string", + }, + versions: { + type: "array", + items: { + type: "integer", + }, + }, + created_by: { + type: "string", + }, + created_at: { + type: "string", + format: "date-time", + }, + value: { + type: "object", + }, + policy: { + $ref: "#/components/schemas/Policy", + }, + execution_mode: { + type: "string", + enum: ["viewer", "publisher", "anonymous"], + }, + extra_perms: { + type: "object", + additionalProperties: { + type: "boolean", + }, + }, + }, + required: [ + "id", + "workspace_id", + "path", + "summary", + "versions", + "created_by", + "created_at", + "value", + "policy", + "execution_mode", + "extra_perms", + ], +}; +var $AppWithLastVersionWDraft = { + allOf: [ + { + $ref: "#/components/schemas/AppWithLastVersion", + }, + { + type: "object", + properties: { + draft_only: { + type: "boolean", + }, + draft: {}, + }, + }, + ], +}; +var $AppHistory = { + type: "object", + properties: { + version: { + type: "integer", + }, + deployment_msg: { + type: "string", + }, + }, + required: ["version"], +}; +var $SlackToken = { + type: "object", + properties: { + access_token: { + type: "string", + }, + team_id: { + type: "string", + }, + team_name: { + type: "string", + }, + bot: { + type: "object", + properties: { + bot_access_token: { + type: "string", + }, + }, + }, + }, + required: ["access_token", "team_id", "team_name", "bot"], +}; +var $TokenResponse = { + type: "object", + properties: { + access_token: { + type: "string", + }, + expires_in: { + type: "integer", + }, + refresh_token: { + type: "string", + }, + scope: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: ["access_token"], +}; +var $HubScriptKind = { + name: "kind", + schema: { + type: "string", + enum: ["script", "failure", "trigger", "approval"], + }, +}; +var $PolarsClientKwargs = { + type: "object", + properties: { + region_name: { + type: "string", + }, + }, + required: ["region_name"], +}; +var $LargeFileStorage = { + type: "object", + properties: { + type: { + type: "string", + enum: [ + "S3Storage", + "AzureBlobStorage", + "AzureWorkloadIdentity", + "S3AwsOidc", + ], + }, + s3_resource_path: { + type: "string", + }, + azure_blob_resource_path: { + type: "string", + }, + public_resource: { + type: "boolean", + }, + }, +}; +var $WindmillLargeFile = { + type: "object", + properties: { + s3: { + type: "string", + }, + }, + required: ["s3"], +}; +var $WindmillFileMetadata = { + type: "object", + properties: { + mime_type: { + type: "string", + }, + size_in_bytes: { + type: "integer", + }, + last_modified: { + type: "string", + format: "date-time", + }, + expires: { + type: "string", + format: "date-time", + }, + version_id: { + type: "string", + }, + }, +}; +var $WindmillFilePreview = { + type: "object", + properties: { + msg: { + type: "string", + }, + content: { + type: "string", + }, + content_type: { + type: "string", + enum: ["RawText", "Csv", "Parquet", "Unknown"], + }, + }, + required: ["content_type"], +}; +var $S3Resource = { + type: "object", + properties: { + bucket: { + type: "string", + }, + region: { + type: "string", + }, + endPoint: { + type: "string", + }, + useSSL: { + type: "boolean", + }, + accessKey: { + type: "string", + }, + secretKey: { + type: "string", + }, + pathStyle: { + type: "boolean", + }, + }, + required: ["bucket", "region", "endPoint", "useSSL", "pathStyle"], +}; +var $WorkspaceGitSyncSettings = { + type: "object", + properties: { + include_path: { + type: "array", + items: { + type: "string", + }, + }, + include_type: { + type: "array", + items: { + type: "string", + enum: [ + "script", + "flow", + "app", + "folder", + "resource", + "variable", + "secret", + "resourcetype", + "schedule", + "user", + "group", + ], + }, + }, + repositories: { + type: "array", + items: { + $ref: "#/components/schemas/GitRepositorySettings", + }, + }, + }, +}; +var $WorkspaceDefaultScripts = { + type: "object", + properties: { + order: { + type: "array", + items: { + type: "string", + }, + }, + hidden: { + type: "array", + items: { + type: "string", + }, + }, + default_script_content: { + additionalProperties: { + type: "string", + }, + }, + }, +}; +var $GitRepositorySettings = { + type: "object", + properties: { + script_path: { + type: "string", + }, + git_repo_resource_path: { + type: "string", + }, + use_individual_branch: { + type: "boolean", + }, + group_by_folder: { + type: "boolean", + }, + exclude_types_override: { + type: "array", + items: { + type: "string", + enum: [ + "script", + "flow", + "app", + "folder", + "resource", + "variable", + "secret", + "resourcetype", + "schedule", + "user", + "group", + ], + }, + }, + }, + required: ["script_path", "git_repo_resource_path"], +}; +var $UploadFilePart = { + type: "object", + properties: { + part_number: { + type: "integer", + }, + tag: { + type: "string", + }, + }, + required: ["part_number", "tag"], +}; +var $MetricMetadata = { + type: "object", + properties: { + id: { + type: "string", + }, + name: { + type: "string", + }, + }, + required: ["id"], +}; +var $ScalarMetric = { + type: "object", + properties: { + metric_id: { + type: "string", + }, + value: { + type: "number", + }, + }, + required: ["id", "value"], +}; +var $TimeseriesMetric = { + type: "object", + properties: { + metric_id: { + type: "string", + }, + values: { + type: "array", + items: { + $ref: "#/components/schemas/MetricDataPoint", + }, + }, + }, + required: ["id", "values"], +}; +var $MetricDataPoint = { + type: "object", + properties: { + timestamp: { + type: "string", + format: "date-time", + }, + value: { + type: "number", + }, + }, + required: ["timestamp", "value"], +}; +var $RawScriptForDependencies = { + type: "object", + properties: { + raw_code: { + type: "string", + }, + path: { + type: "string", + }, + language: { + type: "string", + enum: [ + "python3", + "deno", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + "bun", + ], + }, + }, + required: ["raw_code", "path", "language"], +}; +var $ConcurrencyGroup = { + type: "object", + properties: { + concurrency_id: { + type: "string", + }, + job_uuids: { + type: "array", + items: { + type: "string", + }, + }, + }, + required: ["concurrency_id", "job_uuids"], +}; +var $OpenFlow = { + type: "object", + properties: { + summary: { + type: "string", + }, + description: { + type: "string", + }, + value: { + $ref: "#/components/schemas/FlowValue", + }, + schema: { + type: "object", + }, + }, + required: ["summary", "value"], +}; +var $FlowValue = { + type: "object", + properties: { + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + }, + failure_module: { + $ref: "#/components/schemas/FlowModule", + }, + same_worker: { + type: "boolean", + }, + concurrent_limit: { + type: "number", + }, + concurrency_key: { + type: "string", + }, + concurrency_time_window_s: { + type: "number", + }, + skip_expr: { + type: "string", + }, + cache_ttl: { + type: "number", + }, + priority: { + type: "number", + }, + early_return: { + type: "string", + }, + }, + required: ["modules"], +}; +var $Retry = { + type: "object", + properties: { + constant: { + type: "object", + properties: { + attempts: { + type: "integer", + }, + seconds: { + type: "integer", + }, + }, + }, + exponential: { + type: "object", + properties: { + attempts: { + type: "integer", + }, + multiplier: { + type: "integer", + }, + seconds: { + type: "integer", + }, + random_factor: { + type: "integer", + minimum: 0, + maximum: 100, + }, + }, + }, + }, +}; +var $FlowModule = { + type: "object", + properties: { + id: { + type: "string", + }, + value: { + $ref: "#/components/schemas/FlowModuleValue", + }, + stop_after_if: { + type: "object", + properties: { + skip_if_stopped: { + type: "boolean", + }, + expr: { + type: "string", + }, + }, + required: ["expr"], + }, + sleep: { + $ref: "#/components/schemas/InputTransform", + }, + cache_ttl: { + type: "number", + }, + timeout: { + type: "number", + }, + delete_after_use: { + type: "boolean", + }, + summary: { + type: "string", + }, + mock: { + type: "object", + properties: { + enabled: { + type: "boolean", + }, + return_value: {}, + }, + }, + suspend: { + type: "object", + properties: { + required_events: { + type: "integer", + }, + timeout: { + type: "integer", + }, + resume_form: { + type: "object", + properties: { + schema: { + type: "object", + }, + }, + }, + user_auth_required: { + type: "boolean", + }, + user_groups_required: { + $ref: "#/components/schemas/InputTransform", + }, + self_approval_disabled: { + type: "boolean", + }, + hide_cancel: { + type: "boolean", + }, + }, + }, + priority: { + type: "number", + }, + continue_on_error: { + type: "boolean", + }, + retry: { + $ref: "#/components/schemas/Retry", + }, + }, + required: ["value", "id"], +}; +var $InputTransform = { + oneOf: [ + { + $ref: "#/components/schemas/StaticTransform", + }, + { + $ref: "#/components/schemas/JavascriptTransform", + }, + ], + discriminator: { + propertyName: "type", + mapping: { + static: "#/components/schemas/StaticTransform", + javascript: "#/components/schemas/JavascriptTransform", + }, + }, +}; +var $StaticTransform = { + type: "object", + properties: { + value: {}, + type: { + type: "string", + enum: ["javascript"], + }, + }, + required: ["expr", "type"], +}; +var $JavascriptTransform = { + type: "object", + properties: { + expr: { + type: "string", + }, + type: { + type: "string", + enum: ["javascript"], + }, + }, + required: ["expr", "type"], +}; +var $FlowModuleValue = { + oneOf: [ + { + $ref: "#/components/schemas/RawScript", + }, + { + $ref: "#/components/schemas/PathScript", + }, + { + $ref: "#/components/schemas/PathFlow", + }, + { + $ref: "#/components/schemas/ForloopFlow", + }, + { + $ref: "#/components/schemas/WhileloopFlow", + }, + { + $ref: "#/components/schemas/BranchOne", + }, + { + $ref: "#/components/schemas/BranchAll", + }, + { + $ref: "#/components/schemas/Identity", + }, + ], + discriminator: { + propertyName: "type", + mapping: { + rawscript: "#/components/schemas/RawScript", + script: "#/components/schemas/PathScript", + flow: "#/components/schemas/PathFlow", + forloopflow: "#/components/schemas/ForloopFlow", + whileloopflow: "#/components/schemas/WhileloopFlow", + branchone: "#/components/schemas/BranchOne", + branchall: "#/components/schemas/BranchAll", + identity: "#/components/schemas/Identity", + }, + }, +}; +var $RawScript = { + type: "object", + properties: { + input_transforms: { + type: "object", + additionalProperties: { + $ref: "#/components/schemas/InputTransform", + }, + }, + content: { + type: "string", + }, + language: { + type: "string", + enum: [ + "deno", + "bun", + "python3", + "go", + "bash", + "powershell", + "postgresql", + "mysql", + "bigquery", + "snowflake", + "mssql", + "graphql", + "nativets", + ], + }, + path: { + type: "string", + }, + lock: { + type: "string", + }, + type: { + type: "string", + enum: ["rawscript"], + }, + tag: { + type: "string", + }, + concurrent_limit: { + type: "number", + }, + concurrency_time_window_s: { + type: "number", + }, + }, + required: ["type", "content", "language", "input_transforms"], +}; +var $PathScript = { + type: "object", + properties: { + input_transforms: { + type: "object", + additionalProperties: { + $ref: "#/components/schemas/InputTransform", + }, + }, + path: { + type: "string", + }, + hash: { + type: "string", + }, + type: { + type: "string", + enum: ["script"], + }, + }, + required: ["type", "path", "input_transforms"], +}; +var $PathFlow = { + type: "object", + properties: { + input_transforms: { + type: "object", + additionalProperties: { + $ref: "#/components/schemas/InputTransform", + }, + }, + path: { + type: "string", + }, + type: { + type: "string", + enum: ["flow"], + }, + }, + required: ["type", "path", "input_transforms"], +}; +var $ForloopFlow = { + type: "object", + properties: { + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + }, + iterator: { + $ref: "#/components/schemas/InputTransform", + }, + skip_failures: { + type: "boolean", + }, + type: { + type: "string", + enum: ["forloopflow"], + }, + parallel: { + type: "boolean", + }, + parallelism: { + type: "integer", + }, + }, + required: ["modules", "iterator", "skip_failures", "type"], +}; +var $WhileloopFlow = { + type: "object", + properties: { + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + }, + skip_failures: { + type: "boolean", + }, + type: { + type: "string", + enum: ["forloopflow"], + }, + parallel: { + type: "boolean", + }, + parallelism: { + type: "integer", + }, + }, + required: ["modules", "skip_failures", "type"], +}; +var $BranchOne = { + type: "object", + properties: { + branches: { + type: "array", + items: { + type: "object", + properties: { + summary: { + type: "string", + }, + expr: { + type: "string", + }, + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + }, + }, + required: ["modules", "expr"], + }, + }, + default: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + required: ["modules"], + }, + type: { + type: "string", + enum: ["branchone"], + }, + }, + required: ["branches", "default", "type"], +}; +var $BranchAll = { + type: "object", + properties: { + branches: { + type: "array", + items: { + type: "object", + properties: { + summary: { + type: "string", + }, + skip_failure: { + type: "boolean", + }, + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowModule", + }, + }, + }, + required: ["modules", "expr"], + }, + }, + type: { + type: "string", + enum: ["branchall"], + }, + parallel: { + type: "boolean", + }, + }, + required: ["branches", "type"], +}; +var $Identity = { + type: "object", + properties: { + type: { + type: "string", + enum: ["identity"], + }, + flow: { + type: "boolean", + }, + }, + required: ["type"], +}; +var $FlowStatus = { + type: "object", + properties: { + step: { + type: "integer", + }, + modules: { + type: "array", + items: { + $ref: "#/components/schemas/FlowStatusModule", + }, + }, + user_states: { + additionalProperties: true, + }, + failure_module: { + allOf: [ + { + $ref: "#/components/schemas/FlowStatusModule", + }, + { + type: "object", + properties: { + parent_module: { + type: "string", + }, + }, + }, + ], + }, + retry: { + type: "object", + properties: { + fail_count: { + type: "integer", + }, + failed_jobs: { + type: "array", + items: { + type: "string", + format: "uuid", + }, + }, + }, + }, + }, + required: ["step", "modules", "failure_module"], +}; +var $FlowStatusModule = { + type: "object", + properties: { + type: { + type: "string", + enum: [ + "WaitingForPriorSteps", + "WaitingForEvents", + "WaitingForExecutor", + "InProgress", + "Success", + "Failure", + ], + }, + id: { + type: "string", + }, + job: { + type: "string", + format: "uuid", + }, + count: { + type: "integer", + }, + iterator: { + type: "object", + properties: { + index: { + type: "integer", + }, + itered: { + type: "array", + items: {}, + }, + args: {}, + }, + }, + flow_jobs: { + type: "array", + items: { + type: "string", + }, + }, + branch_chosen: { + type: "object", + properties: { + type: { + type: "string", + enum: ["branch", "default"], + }, + branch: { + type: "integer", + }, + }, + required: ["type"], + }, + branchall: { + type: "object", + properties: { + branch: { + type: "integer", + }, + len: { + type: "integer", + }, + }, + required: ["branch", "len"], + }, + approvers: { + type: "array", + items: { + type: "object", + properties: { + resume_id: { + type: "integer", + }, + approver: { + type: "string", + }, + }, + required: ["resume_id", "approver"], + }, + }, + }, + required: ["type"], +}; + +// src/core/request.ts +var isString = (value) => { + return typeof value === "string"; +}; +var isStringWithValue = (value) => { + return isString(value) && value !== ""; +}; +var isBlob = (value) => { + return value instanceof Blob; +}; +var isFormData = (value) => { + return value instanceof FormData; +}; +var base64 = (str) => { + try { + return btoa(str); + } catch (err) { + return Buffer.from(str).toString("base64"); + } +}; +var getQueryString = (params) => { + const qs = []; + const append = (key, value) => { + qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); + }; + const encodePair = (key, value) => { + if (value === void 0 || value === null) { + return; + } + if (value instanceof Date) { + append(key, value.toISOString()); + } else if (Array.isArray(value)) { + value.forEach((v) => encodePair(key, v)); + } else if (typeof value === "object") { + Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); + } else { + append(key, value); + } + }; + Object.entries(params).forEach(([key, value]) => encodePair(key, value)); + return qs.length ? `?${qs.join("&")}` : ""; +}; +var getUrl = (config, options) => { + const encoder = config.ENCODE_PATH || encodeURI; + const path = options.url + .replace("{api-version}", config.VERSION) + .replace(/{(.*?)}/g, (substring, group) => { + if (options.path?.hasOwnProperty(group)) { + return encoder(String(options.path[group])); + } + return substring; + }); + const url = config.BASE + path; + return options.query ? url + getQueryString(options.query) : url; +}; +var getFormData = (options) => { + if (options.formData) { + const formData = new FormData(); + const process2 = (key, value) => { + if (isString(value) || isBlob(value)) { + formData.append(key, value); + } else { + formData.append(key, JSON.stringify(value)); + } + }; + Object.entries(options.formData) + .filter(([, value]) => value !== void 0 && value !== null) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach((v) => process2(key, v)); + } else { + process2(key, value); + } + }); + return formData; + } + return void 0; +}; +var resolve = async (options, resolver) => { + if (typeof resolver === "function") { + return resolver(options); + } + return resolver; +}; +var getHeaders = async (config, options) => { + const [token, username, password, additionalHeaders] = await Promise.all([ + resolve(options, config.TOKEN), + resolve(options, config.USERNAME), + resolve(options, config.PASSWORD), + resolve(options, config.HEADERS), + ]); + const headers = Object.entries({ + Accept: "application/json", + ...additionalHeaders, + ...options.headers, + }) + .filter(([, value]) => value !== void 0 && value !== null) + .reduce( + (headers2, [key, value]) => ({ + ...headers2, + [key]: String(value), + }), + {} + ); + if (isStringWithValue(token)) { + headers["Authorization"] = `Bearer ${token}`; + } + if (isStringWithValue(username) && isStringWithValue(password)) { + const credentials = base64(`${username}:${password}`); + headers["Authorization"] = `Basic ${credentials}`; + } + if (options.body !== void 0) { + if (options.mediaType) { + headers["Content-Type"] = options.mediaType; + } else if (isBlob(options.body)) { + headers["Content-Type"] = options.body.type || "application/octet-stream"; + } else if (isString(options.body)) { + headers["Content-Type"] = "text/plain"; + } else if (!isFormData(options.body)) { + headers["Content-Type"] = "application/json"; + } + } + return new Headers(headers); +}; +var getRequestBody = (options) => { + if (options.body !== void 0) { + if ( + options.mediaType?.includes("application/json") || + options.mediaType?.includes("+json") + ) { + return JSON.stringify(options.body); + } else if ( + isString(options.body) || + isBlob(options.body) || + isFormData(options.body) + ) { + return options.body; + } else { + return JSON.stringify(options.body); + } + } + return void 0; +}; +var sendRequest = async ( + config, + options, + url, + body, + formData, + headers, + onCancel +) => { + const controller = new AbortController(); + let request2 = { + headers, + body: body ?? formData, + method: options.method, + signal: controller.signal, + }; + if (config.WITH_CREDENTIALS) { + request2.credentials = config.CREDENTIALS; + } + for (const fn of config.interceptors.request._fns) { + request2 = await fn(request2); + } + onCancel(() => controller.abort()); + return await fetch(url, request2); +}; +var getResponseHeader = (response, responseHeader) => { + if (responseHeader) { + const content = response.headers.get(responseHeader); + if (isString(content)) { + return content; + } + } + return void 0; +}; +var getResponseBody = async (response) => { + if (response.status !== 204) { + try { + const contentType = response.headers.get("Content-Type"); + if (contentType) { + const binaryTypes = [ + "application/octet-stream", + "application/pdf", + "application/zip", + "audio/", + "image/", + "video/", + ]; + if ( + contentType.includes("application/json") || + contentType.includes("+json") + ) { + return await response.json(); + } else if (binaryTypes.some((type) => contentType.includes(type))) { + return await response.blob(); + } else if (contentType.includes("multipart/form-data")) { + return await response.formData(); + } else if (contentType.includes("text/")) { + return await response.text(); + } + } + } catch (error) { + console.error(error); + } + } + return void 0; +}; +var catchErrorCodes = (options, result) => { + const errors = { + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Payload Too Large", + 414: "URI Too Long", + 415: "Unsupported Media Type", + 416: "Range Not Satisfiable", + 417: "Expectation Failed", + 418: "Im a teapot", + 421: "Misdirected Request", + 422: "Unprocessable Content", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", + ...options.errors, + }; + const error = errors[result.status]; + if (error) { + throw new ApiError(options, result, error); + } + if (!result.ok) { + const errorStatus = result.status ?? "unknown"; + const errorStatusText = result.statusText ?? "unknown"; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return void 0; + } + })(); + throw new ApiError( + options, + result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); + } +}; +var request = (config, options) => { + return new CancelablePromise(async (resolve2, reject, onCancel) => { + try { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + if (!onCancel.isCancelled) { + let response = await sendRequest( + config, + options, + url, + body, + formData, + headers, + onCancel + ); + for (const fn of config.interceptors.response._fns) { + response = await fn(response); + } + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader( + response, + options.responseHeader + ); + const result = { + url, + ok: response.ok, + status: response.status, + statusText: response.statusText, + body: responseHeader ?? responseBody, + }; + catchErrorCodes(options, result); + resolve2(result.body); + } + } catch (error) { + reject(error); + } + }); +}; + +// src/services.gen.ts +var SettingsService = class { + /** + * get backend version + * @returns string git version of backend + * @throws ApiError + */ + static backendVersion() { + return request(OpenAPI, { + method: "GET", + url: "/version", + }); + } + /** + * is backend up to date + * @returns string is backend up to date + * @throws ApiError + */ + static backendUptodate() { + return request(OpenAPI, { + method: "GET", + url: "/uptodate", + }); + } + /** + * get license id + * @returns string get license id (empty if not ee) + * @throws ApiError + */ + static getLicenseId() { + return request(OpenAPI, { + method: "GET", + url: "/ee_license", + }); + } + /** + * get openapi yaml spec + * @returns string openapi yaml file content + * @throws ApiError + */ + static getOpenApiYaml() { + return request(OpenAPI, { + method: "GET", + url: "/openapi.yaml", + }); + } +}; +var AuditService = class { + /** + * get audit log (requires admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns AuditLog an audit log + * @throws ApiError + */ + static getAuditLog(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/audit/get/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * list audit logs (requires admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.before filter on created before (exclusive) timestamp + * @param data.after filter on created after (exclusive) timestamp + * @param data.username filter on exact username of user + * @param data.operation filter on exact or prefix name of operation + * @param data.resource filter on exact or prefix name of resource + * @param data.actionKind filter on type of operation + * @returns AuditLog a list of audit logs + * @throws ApiError + */ + static listAuditLogs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/audit/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + before: data.before, + after: data.after, + username: data.username, + operation: data.operation, + resource: data.resource, + action_kind: data.actionKind, + }, + }); + } +}; +var UserService = class { + /** + * login with password + * @param data The data for the request. + * @param data.requestBody credentials + * @returns string Successfully authenticated. The session ID is returned in a cookie named `token` and as plaintext response. Preferred method of authorization is through the bearer token. The cookie is only for browser convenience. + * + * @throws ApiError + */ + static login(data) { + return request(OpenAPI, { + method: "POST", + url: "/auth/login", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * logout + * @returns string clear cookies and clear token (if applicable) + * @throws ApiError + */ + static logout() { + return request(OpenAPI, { + method: "POST", + url: "/auth/logout", + }); + } + /** + * get user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @returns User user created + * @throws ApiError + */ + static getUser(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + }); + } + /** + * update user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @param data.requestBody new user + * @returns string edited user + * @throws ApiError + */ + static updateUser(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/users/update/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * is owner of path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean is owner + * @throws ApiError + */ + static isOwnerOfPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/is_owner/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * set password + * @param data The data for the request. + * @param data.requestBody set password + * @returns string password set + * @throws ApiError + */ + static setPassword(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/setpassword", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * create user + * @param data The data for the request. + * @param data.requestBody user info + * @returns string user created + * @throws ApiError + */ + static createUserGlobally(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/create", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * global update user (require super admin) + * @param data The data for the request. + * @param data.email + * @param data.requestBody new user info + * @returns string user updated + * @throws ApiError + */ + static globalUserUpdate(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/update/{email}", + path: { + email: data.email, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * global username info (require super admin) + * @param data The data for the request. + * @param data.email + * @returns unknown user renamed + * @throws ApiError + */ + static globalUsernameInfo(data) { + return request(OpenAPI, { + method: "GET", + url: "/users/username_info/{email}", + path: { + email: data.email, + }, + }); + } + /** + * global rename user (require super admin) + * @param data The data for the request. + * @param data.email + * @param data.requestBody new username + * @returns string user renamed + * @throws ApiError + */ + static globalUserRename(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/rename/{email}", + path: { + email: data.email, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * global delete user (require super admin) + * @param data The data for the request. + * @param data.email + * @returns string user deleted + * @throws ApiError + */ + static globalUserDelete(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/users/delete/{email}", + path: { + email: data.email, + }, + }); + } + /** + * delete user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @returns string delete user + * @throws ApiError + */ + static deleteUser(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/users/delete/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + }); + } + /** + * get current user email (if logged in) + * @returns string user email + * @throws ApiError + */ + static getCurrentEmail() { + return request(OpenAPI, { + method: "GET", + url: "/users/email", + }); + } + /** + * refresh the current token + * @returns string free usage + * @throws ApiError + */ + static refreshUserToken() { + return request(OpenAPI, { + method: "GET", + url: "/users/refresh_token", + }); + } + /** + * get tutorial progress + * @returns unknown tutorial progress + * @throws ApiError + */ + static getTutorialProgress() { + return request(OpenAPI, { + method: "GET", + url: "/users/tutorial_progress", + }); + } + /** + * update tutorial progress + * @param data The data for the request. + * @param data.requestBody progress update + * @returns string tutorial progress + * @throws ApiError + */ + static updateTutorialProgress(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/tutorial_progress", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * leave instance + * @returns string status + * @throws ApiError + */ + static leaveInstance() { + return request(OpenAPI, { + method: "POST", + url: "/users/leave_instance", + }); + } + /** + * get current usage outside of premium workspaces + * @returns number free usage + * @throws ApiError + */ + static getUsage() { + return request(OpenAPI, { + method: "GET", + url: "/users/usage", + }); + } + /** + * get all runnables in every workspace + * @returns unknown free all runnables + * @throws ApiError + */ + static getRunnable() { + return request(OpenAPI, { + method: "GET", + url: "/users/all_runnables", + }); + } + /** + * get current global whoami (if logged in) + * @returns GlobalUserInfo user email + * @throws ApiError + */ + static globalWhoami() { + return request(OpenAPI, { + method: "GET", + url: "/users/whoami", + }); + } + /** + * list all workspace invites + * @returns WorkspaceInvite list all workspace invites + * @throws ApiError + */ + static listWorkspaceInvites() { + return request(OpenAPI, { + method: "GET", + url: "/users/list_invites", + }); + } + /** + * whoami + * @param data The data for the request. + * @param data.workspace + * @returns User user + * @throws ApiError + */ + static whoami(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/whoami", + path: { + workspace: data.workspace, + }, + }); + } + /** + * accept invite to workspace + * @param data The data for the request. + * @param data.requestBody accept invite + * @returns string status + * @throws ApiError + */ + static acceptInvite(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/accept_invite", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * decline invite to workspace + * @param data The data for the request. + * @param data.requestBody decline invite + * @returns string status + * @throws ApiError + */ + static declineInvite(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/decline_invite", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * whois + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @returns User user + * @throws ApiError + */ + static whois(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/whois/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + }); + } + /** + * exists email + * @param data The data for the request. + * @param data.email + * @returns boolean user + * @throws ApiError + */ + static existsEmail(data) { + return request(OpenAPI, { + method: "GET", + url: "/users/exists/{email}", + path: { + email: data.email, + }, + }); + } + /** + * list all users as super admin (require to be super amdin) + * @param data The data for the request. + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns GlobalUserInfo user + * @throws ApiError + */ + static listUsersAsSuperAdmin(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/users/list_as_super_admin", + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * list users + * @param data The data for the request. + * @param data.workspace + * @returns User user + * @throws ApiError + */ + static listUsers(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/list", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list users usage + * @param data The data for the request. + * @param data.workspace + * @returns UserUsage user + * @throws ApiError + */ + static listUsersUsage(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/list_usage", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list usernames + * @param data The data for the request. + * @param data.workspace + * @returns string user + * @throws ApiError + */ + static listUsernames(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/list_usernames", + path: { + workspace: data.workspace, + }, + }); + } + /** + * create token + * @param data The data for the request. + * @param data.requestBody new token + * @returns string token created + * @throws ApiError + */ + static createToken(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/tokens/create", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * create token to impersonate a user (require superadmin) + * @param data The data for the request. + * @param data.requestBody new token + * @returns string token created + * @throws ApiError + */ + static createTokenImpersonate(data) { + return request(OpenAPI, { + method: "POST", + url: "/users/tokens/impersonate", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete token + * @param data The data for the request. + * @param data.tokenPrefix + * @returns string delete token + * @throws ApiError + */ + static deleteToken(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/users/tokens/delete/{token_prefix}", + path: { + token_prefix: data.tokenPrefix, + }, + }); + } + /** + * list token + * @param data The data for the request. + * @param data.excludeEphemeral + * @returns TruncatedToken truncated token + * @throws ApiError + */ + static listTokens(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/users/tokens/list", + query: { + exclude_ephemeral: data.excludeEphemeral, + }, + }); + } + /** + * login with oauth authorization flow + * @param data The data for the request. + * @param data.clientName + * @param data.requestBody Partially filled script + * @returns string Successfully authenticated. The session ID is returned in a cookie named `token` and as plaintext response. Preferred method of authorization is through the bearer token. The cookie is only for browser convenience. + * + * @throws ApiError + */ + static loginWithOauth(data) { + return request(OpenAPI, { + method: "POST", + url: "/oauth/login_callback/{client_name}", + path: { + client_name: data.clientName, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var AdminService = class { + /** + * get user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @returns User user created + * @throws ApiError + */ + static getUser(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/users/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + }); + } + /** + * update user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @param data.requestBody new user + * @returns string edited user + * @throws ApiError + */ + static updateUser(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/users/update/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete user (require admin privilege) + * @param data The data for the request. + * @param data.workspace + * @param data.username + * @returns string delete user + * @throws ApiError + */ + static deleteUser(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/users/delete/{username}", + path: { + workspace: data.workspace, + username: data.username, + }, + }); + } +}; +var WorkspaceService = class { + /** + * list all workspaces visible to me + * @returns Workspace all workspaces + * @throws ApiError + */ + static listWorkspaces() { + return request(OpenAPI, { + method: "GET", + url: "/workspaces/list", + }); + } + /** + * is domain allowed for auto invi + * @returns boolean domain allowed or not + * @throws ApiError + */ + static isDomainAllowed() { + return request(OpenAPI, { + method: "GET", + url: "/workspaces/allowed_domain_auto_invite", + }); + } + /** + * list all workspaces visible to me with user info + * @returns UserWorkspaceList workspace with associated username + * @throws ApiError + */ + static listUserWorkspaces() { + return request(OpenAPI, { + method: "GET", + url: "/workspaces/users", + }); + } + /** + * list all workspaces as super admin (require to be super admin) + * @param data The data for the request. + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Workspace workspaces + * @throws ApiError + */ + static listWorkspacesAsSuperAdmin(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/workspaces/list_as_superadmin", + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * create workspace + * @param data The data for the request. + * @param data.requestBody new token + * @returns string token created + * @throws ApiError + */ + static createWorkspace(data) { + return request(OpenAPI, { + method: "POST", + url: "/workspaces/create", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * exists workspace + * @param data The data for the request. + * @param data.requestBody id of workspace + * @returns boolean status + * @throws ApiError + */ + static existsWorkspace(data) { + return request(OpenAPI, { + method: "POST", + url: "/workspaces/exists", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * exists username + * @param data The data for the request. + * @param data.requestBody + * @returns boolean status + * @throws ApiError + */ + static existsUsername(data) { + return request(OpenAPI, { + method: "POST", + url: "/workspaces/exists_username", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * invite user to workspace + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ + static inviteUser(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/invite_user", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * add user to workspace + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ + static addUser(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/add_user", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete user invite + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ + static deleteInvite(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/delete_invite", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * archive workspace + * @param data The data for the request. + * @param data.workspace + * @returns string status + * @throws ApiError + */ + static archiveWorkspace(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/archive", + path: { + workspace: data.workspace, + }, + }); + } + /** + * unarchive workspace + * @param data The data for the request. + * @param data.workspace + * @returns string status + * @throws ApiError + */ + static unarchiveWorkspace(data) { + return request(OpenAPI, { + method: "POST", + url: "/workspaces/unarchive/{workspace}", + path: { + workspace: data.workspace, + }, + }); + } + /** + * delete workspace (require super admin) + * @param data The data for the request. + * @param data.workspace + * @returns string status + * @throws ApiError + */ + static deleteWorkspace(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/workspaces/delete/{workspace}", + path: { + workspace: data.workspace, + }, + }); + } + /** + * leave workspace + * @param data The data for the request. + * @param data.workspace + * @returns string status + * @throws ApiError + */ + static leaveWorkspace(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/leave", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get workspace name + * @param data The data for the request. + * @param data.workspace + * @returns string status + * @throws ApiError + */ + static getWorkspaceName(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/get_workspace_name", + path: { + workspace: data.workspace, + }, + }); + } + /** + * change workspace name + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns string status + * @throws ApiError + */ + static changeWorkspaceName(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/change_workspace_name", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * change workspace id + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns string status + * @throws ApiError + */ + static changeWorkspaceId(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/change_workspace_id", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * list pending invites for a workspace + * @param data The data for the request. + * @param data.workspace + * @returns WorkspaceInvite user + * @throws ApiError + */ + static listPendingInvites(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/list_pending_invites", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get settings + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getSettings(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/get_settings", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get deploy to + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getDeployTo(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/get_deploy_to", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get if workspace is premium + * @param data The data for the request. + * @param data.workspace + * @returns boolean status + * @throws ApiError + */ + static getIsPremium(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/is_premium", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get premium info + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getPremiumInfo(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/premium_info", + path: { + workspace: data.workspace, + }, + }); + } + /** + * set automatic billing + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody automatic billing + * @returns string status + * @throws ApiError + */ + static setAutomaticBilling(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/set_automatic_billing", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit slack command + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ + static editSlackCommand(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_slack_command", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run a job that sends a message to Slack + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody path to hub script to run and its corresponding args + * @returns unknown status + * @throws ApiError + */ + static runSlackMessageTestJob(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/run_slack_message_test_job", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit deploy to + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns string status + * @throws ApiError + */ + static editDeployTo(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_deploy_to", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit auto invite + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceInvite + * @returns string status + * @throws ApiError + */ + static editAutoInvite(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_auto_invite", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit webhook + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceWebhook + * @returns string status + * @throws ApiError + */ + static editWebhook(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_webhook", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit copilot config + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceCopilotConfig + * @returns string status + * @throws ApiError + */ + static editCopilotConfig(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_copilot_config", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get copilot info + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getCopilotInfo(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/get_copilot_info", + path: { + workspace: data.workspace, + }, + }); + } + /** + * edit error handler + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody WorkspaceErrorHandler + * @returns string status + * @throws ApiError + */ + static editErrorHandler(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_error_handler", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit large file storage settings + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody LargeFileStorage info + * @returns unknown status + * @throws ApiError + */ + static editLargeFileStorageConfig(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_large_file_storage_config", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit workspace git sync settings + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Workspace Git sync settings + * @returns unknown status + * @throws ApiError + */ + static editWorkspaceGitSyncConfig(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_git_sync_config", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit default app for workspace + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Workspace default app + * @returns string status + * @throws ApiError + */ + static editWorkspaceDefaultApp(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/edit_default_app", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * edit default scripts for workspace + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Workspace default app + * @returns string status + * @throws ApiError + */ + static editDefaultScripts(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/default_scripts", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get default scripts for workspace + * @param data The data for the request. + * @param data.workspace + * @returns WorkspaceDefaultScripts status + * @throws ApiError + */ + static getDefaultScripts(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/default_scripts", + path: { + workspace: data.workspace, + }, + }); + } + /** + * set environment variable + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Workspace default app + * @returns string status + * @throws ApiError + */ + static setEnvironmentVariable(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/set_environment_variable", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * retrieves the encryption key for this workspace + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getWorkspaceEncryptionKey(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/encryption_key", + path: { + workspace: data.workspace, + }, + }); + } + /** + * update the encryption key for this workspace + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody New encryption key + * @returns string status + * @throws ApiError + */ + static setWorkspaceEncryptionKey(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/workspaces/encryption_key", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get default app for workspace + * @param data The data for the request. + * @param data.workspace + * @returns unknown status + * @throws ApiError + */ + static getWorkspaceDefaultApp(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/default_app", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get large file storage config + * @param data The data for the request. + * @param data.workspace + * @returns LargeFileStorage status + * @throws ApiError + */ + static getLargeFileStorageConfig(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/get_large_file_storage_config", + path: { + workspace: data.workspace, + }, + }); + } + /** + * get usage + * @param data The data for the request. + * @param data.workspace + * @returns number usage + * @throws ApiError + */ + static getWorkspaceUsage(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/workspaces/usage", + path: { + workspace: data.workspace, + }, + }); + } +}; +var SettingService = class { + /** + * get global settings + * @param data The data for the request. + * @param data.key + * @returns unknown status + * @throws ApiError + */ + static getGlobal(data) { + return request(OpenAPI, { + method: "GET", + url: "/settings/global/{key}", + path: { + key: data.key, + }, + }); + } + /** + * post global settings + * @param data The data for the request. + * @param data.key + * @param data.requestBody value set + * @returns string status + * @throws ApiError + */ + static setGlobal(data) { + return request(OpenAPI, { + method: "POST", + url: "/settings/global/{key}", + path: { + key: data.key, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get local settings + * @returns unknown status + * @throws ApiError + */ + static getLocal() { + return request(OpenAPI, { + method: "GET", + url: "/settings/local", + }); + } + /** + * test smtp + * @param data The data for the request. + * @param data.requestBody test smtp payload + * @returns string status + * @throws ApiError + */ + static testSmtp(data) { + return request(OpenAPI, { + method: "POST", + url: "/settings/test_smtp", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * test license key + * @param data The data for the request. + * @param data.requestBody test license key + * @returns string status + * @throws ApiError + */ + static testLicenseKey(data) { + return request(OpenAPI, { + method: "POST", + url: "/settings/test_license_key", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * test object storage config + * @param data The data for the request. + * @param data.requestBody test object storage config + * @returns string status + * @throws ApiError + */ + static testObjectStorageConfig(data) { + return request(OpenAPI, { + method: "POST", + url: "/settings/test_object_storage_config", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * send stats + * @returns string status + * @throws ApiError + */ + static sendStats() { + return request(OpenAPI, { + method: "POST", + url: "/settings/send_stats", + }); + } + /** + * test metadata + * @param data The data for the request. + * @param data.requestBody test metadata + * @returns string status + * @throws ApiError + */ + static testMetadata(data) { + return request(OpenAPI, { + method: "POST", + url: "/saml/test_metadata", + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var OidcService = class { + /** + * get OIDC token (ee only) + * @param data The data for the request. + * @param data.workspace + * @param data.audience + * @returns string new oidc token + * @throws ApiError + */ + static getOidcToken(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oidc/token/{audience}", + path: { + workspace: data.workspace, + audience: data.audience, + }, + }); + } +}; +var VariableService = class { + /** + * create variable + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new variable + * @param data.alreadyEncrypted + * @returns string variable created + * @throws ApiError + */ + static createVariable(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/variables/create", + path: { + workspace: data.workspace, + }, + query: { + already_encrypted: data.alreadyEncrypted, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * encrypt value + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new variable + * @returns string encrypted value + * @throws ApiError + */ + static encryptValue(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/variables/encrypt", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete variable + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string variable deleted + * @throws ApiError + */ + static deleteVariable(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/variables/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update variable + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated variable + * @param data.alreadyEncrypted + * @returns string variable updated + * @throws ApiError + */ + static updateVariable(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/variables/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + already_encrypted: data.alreadyEncrypted, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get variable + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.decryptSecret ask to decrypt secret if this variable is secret + * (if not secret no effect, default: true) + * + * @param data.includeEncrypted ask to include the encrypted value if secret and decrypt secret is not true (default: false) + * + * @returns ListableVariable variable + * @throws ApiError + */ + static getVariable(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/variables/get/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + decrypt_secret: data.decryptSecret, + include_encrypted: data.includeEncrypted, + }, + }); + } + /** + * get variable value + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string variable + * @throws ApiError + */ + static getVariableValue(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/variables/get_value/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * does variable exists at path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean variable + * @throws ApiError + */ + static existsVariable(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/variables/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * list variables + * @param data The data for the request. + * @param data.workspace + * @returns ListableVariable variable list + * @throws ApiError + */ + static listVariable(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/variables/list", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list contextual variables + * @param data The data for the request. + * @param data.workspace + * @returns ContextualVariable contextual variable list + * @throws ApiError + */ + static listContextualVariables(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/variables/list_contextual", + path: { + workspace: data.workspace, + }, + }); + } +}; +var OauthService = class { + /** + * connect slack callback + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody code endpoint + * @returns string slack token + * @throws ApiError + */ + static connectSlackCallback(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oauth/connect_slack_callback", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * connect callback + * @param data The data for the request. + * @param data.clientName + * @param data.requestBody code endpoint + * @returns TokenResponse oauth token + * @throws ApiError + */ + static connectCallback(data) { + return request(OpenAPI, { + method: "POST", + url: "/oauth/connect_callback/{client_name}", + path: { + client_name: data.clientName, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * create OAuth account + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody code endpoint + * @returns string account set + * @throws ApiError + */ + static createAccount(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oauth/create_account", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * refresh token + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.requestBody variable path + * @returns string token refreshed + * @throws ApiError + */ + static refreshToken(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oauth/refresh_token/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * disconnect account + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns string disconnected client + * @throws ApiError + */ + static disconnectAccount(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oauth/disconnect/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * disconnect slack + * @param data The data for the request. + * @param data.workspace + * @returns string disconnected slack + * @throws ApiError + */ + static disconnectSlack(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/oauth/disconnect_slack", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list oauth logins + * @returns unknown list of oauth and saml login clients + * @throws ApiError + */ + static listOauthLogins() { + return request(OpenAPI, { + method: "GET", + url: "/oauth/list_logins", + }); + } + /** + * list oauth connects + * @returns unknown list of oauth connects clients + * @throws ApiError + */ + static listOauthConnects() { + return request(OpenAPI, { + method: "GET", + url: "/oauth/list_connects", + }); + } +}; +var ResourceService = class { + /** + * create resource + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new resource + * @param data.updateIfExists + * @returns string resource created + * @throws ApiError + */ + static createResource(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/resources/create", + path: { + workspace: data.workspace, + }, + query: { + update_if_exists: data.updateIfExists, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete resource + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string resource deleted + * @throws ApiError + */ + static deleteResource(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/resources/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update resource + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated resource + * @returns string resource updated + * @throws ApiError + */ + static updateResource(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/resources/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update resource value + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated resource + * @returns string resource value updated + * @throws ApiError + */ + static updateResourceValue(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/resources/update_value/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get resource + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns Resource resource + * @throws ApiError + */ + static getResource(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/get/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get resource interpolated (variables and resources are fully unrolled) + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.jobId job id + * @returns unknown resource value + * @throws ApiError + */ + static getResourceValueInterpolated(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/get_value_interpolated/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + job_id: data.jobId, + }, + }); + } + /** + * get resource value + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown resource value + * @throws ApiError + */ + static getResourceValue(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/get_value/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * does resource exists + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean does resource exists + * @throws ApiError + */ + static existsResource(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * list resources + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.resourceType resource_types to list from, separated by ',', + * @param data.resourceTypeExclude resource_types to not list from, separated by ',', + * @returns ListableResource resource list + * @throws ApiError + */ + static listResource(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + resource_type: data.resourceType, + resource_type_exclude: data.resourceTypeExclude, + }, + }); + } + /** + * list resources for search + * @param data The data for the request. + * @param data.workspace + * @returns unknown resource list + * @throws ApiError + */ + static listSearchResource(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/list_search", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list resource names + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns unknown resource list names + * @throws ApiError + */ + static listResourceNames(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/list_names/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * create resource_type + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new resource_type + * @returns string resource_type created + * @throws ApiError + */ + static createResourceType(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/resources/type/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete resource_type + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string resource_type deleted + * @throws ApiError + */ + static deleteResourceType(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/resources/type/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update resource_type + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated resource_type + * @returns string resource_type updated + * @throws ApiError + */ + static updateResourceType(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/resources/type/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get resource_type + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns ResourceType resource_type deleted + * @throws ApiError + */ + static getResourceType(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/type/get/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * does resource_type exists + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean does resource_type exist + * @throws ApiError + */ + static existsResourceType(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/type/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * list resource_types + * @param data The data for the request. + * @param data.workspace + * @returns ResourceType resource_type list + * @throws ApiError + */ + static listResourceType(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/type/list", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list resource_types names + * @param data The data for the request. + * @param data.workspace + * @returns string resource_type list + * @throws ApiError + */ + static listResourceTypeNames(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/resources/type/listnames", + path: { + workspace: data.workspace, + }, + }); + } + /** + * query resource types by similarity + * @param data The data for the request. + * @param data.workspace + * @param data.text query text + * @param data.limit query limit + * @returns unknown resource type details + * @throws ApiError + */ + static queryResourceTypes(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/embeddings/query_resource_types", + path: { + workspace: data.workspace, + }, + query: { + text: data.text, + limit: data.limit, + }, + }); + } +}; +var IntegrationService = class { + /** + * list hub integrations + * @param data The data for the request. + * @param data.kind query integrations kind + * @returns unknown integrations details + * @throws ApiError + */ + static listHubIntegrations(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/integrations/hub/list", + query: { + kind: data.kind, + }, + }); + } +}; +var FlowService = class { + /** + * list all hub flows + * @returns unknown hub flows list + * @throws ApiError + */ + static listHubFlows() { + return request(OpenAPI, { + method: "GET", + url: "/flows/hub/list", + }); + } + /** + * get hub flow by id + * @param data The data for the request. + * @param data.id + * @returns unknown flow + * @throws ApiError + */ + static getHubFlowById(data) { + return request(OpenAPI, { + method: "GET", + url: "/flows/hub/get/{id}", + path: { + id: data.id, + }, + }); + } + /** + * list all flow paths + * @param data The data for the request. + * @param data.workspace + * @returns string list of flow paths + * @throws ApiError + */ + static listFlowPaths(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/list_paths", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list flows for search + * @param data The data for the request. + * @param data.workspace + * @returns unknown flow list + * @throws ApiError + */ + static listSearchFlow(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/list_search", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list all flows + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.pathStart mask to filter matching starting path + * @param data.pathExact mask to filter exact matching path + * @param data.showArchived (default false) + * show also the archived files. + * when multiple archived hash share the same path, only the ones with the latest create_at + * are displayed. + * + * @param data.starredOnly (default false) + * show only the starred items + * + * @returns unknown All flow + * @throws ApiError + */ + static listFlows(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + order_desc: data.orderDesc, + created_by: data.createdBy, + path_start: data.pathStart, + path_exact: data.pathExact, + show_archived: data.showArchived, + starred_only: data.starredOnly, + }, + }); + } + /** + * get flow by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns Flow flow details + * @throws ApiError + */ + static getFlowByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/get/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * Toggle ON and OFF the workspace error handler for a given flow + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody Workspace error handler enabled + * @returns string error handler toggled + * @throws ApiError + */ + static toggleWorkspaceErrorHandlerForFlow(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/flows/toggle_workspace_error_handler/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get flow by path with draft + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown flow details with draft + * @throws ApiError + */ + static getFlowByPathWithDraft(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/get/draft/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * exists flow by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean flow details + * @throws ApiError + */ + static existsFlowByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * create flow + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Partially filled flow + * @returns string flow created + * @throws ApiError + */ + static createFlow(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/flows/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update flow + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody Partially filled flow + * @returns string flow updated + * @throws ApiError + */ + static updateFlow(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/flows/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * archive flow by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody archiveFlow + * @returns string flow archived + * @throws ApiError + */ + static archiveFlowByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/flows/archive/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete flow by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string flow delete + * @throws ApiError + */ + static deleteFlowByPath(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/flows/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * list inputs for previous completed flow jobs + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Input input history for completed jobs with this flow path + * @throws ApiError + */ + static getFlowInputHistoryByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/flows/input_history/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } +}; +var AppService = class { + /** + * list all hub apps + * @returns unknown hub apps list + * @throws ApiError + */ + static listHubApps() { + return request(OpenAPI, { + method: "GET", + url: "/apps/hub/list", + }); + } + /** + * get hub app by id + * @param data The data for the request. + * @param data.id + * @returns unknown app + * @throws ApiError + */ + static getHubAppById(data) { + return request(OpenAPI, { + method: "GET", + url: "/apps/hub/get/{id}", + path: { + id: data.id, + }, + }); + } + /** + * list apps for search + * @param data The data for the request. + * @param data.workspace + * @returns unknown app list + * @throws ApiError + */ + static listSearchApp(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/list_search", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list all apps + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.pathStart mask to filter matching starting path + * @param data.pathExact mask to filter exact matching path + * @param data.starredOnly (default false) + * show only the starred items + * + * @returns ListableApp All apps + * @throws ApiError + */ + static listApps(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + order_desc: data.orderDesc, + created_by: data.createdBy, + path_start: data.pathStart, + path_exact: data.pathExact, + starred_only: data.starredOnly, + }, + }); + } + /** + * create app + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new app + * @returns string app created + * @throws ApiError + */ + static createApp(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/apps/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * does an app exisst at path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean app exists + * @throws ApiError + */ + static existsApp(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get app by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns AppWithLastVersion app details + * @throws ApiError + */ + static getAppByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/get/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get app by path with draft + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns AppWithLastVersionWDraft app details with draft + * @throws ApiError + */ + static getAppByPathWithDraft(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/get/draft/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get app history by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns AppHistory app history + * @throws ApiError + */ + static getAppHistoryByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/history/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update app history + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.version + * @param data.requestBody App deployment message + * @returns string success + * @throws ApiError + */ + static updateAppHistory(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/apps/history_update/a/{id}/v/{version}", + path: { + workspace: data.workspace, + id: data.id, + version: data.version, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get public app by secret + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns AppWithLastVersion app details + * @throws ApiError + */ + static getPublicAppBySecret(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps_u/public_app/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get public resource + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown resource value + * @throws ApiError + */ + static getPublicResource(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps_u/public_resource/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get public secret of app + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string app secret + * @throws ApiError + */ + static getPublicSecretOfApp(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/secret_of/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get app by version + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns AppWithLastVersion app details + * @throws ApiError + */ + static getAppByVersion(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/get/v/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * delete app + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string app deleted + * @throws ApiError + */ + static deleteApp(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/apps/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update app + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody update app + * @returns string app updated + * @throws ApiError + */ + static updateApp(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/apps/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * executeComponent + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody update app + * @returns string job uuid + * @throws ApiError + */ + static executeComponent(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/apps_u/execute_component/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var ScriptService = class { + /** + * get hub script content by path + * @param data The data for the request. + * @param data.path + * @returns string script details + * @throws ApiError + */ + static getHubScriptContentByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/scripts/hub/get/{path}", + path: { + path: data.path, + }, + }); + } + /** + * get full hub script by path + * @param data The data for the request. + * @param data.path + * @returns unknown script details + * @throws ApiError + */ + static getHubScriptByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/scripts/hub/get_full/{path}", + path: { + path: data.path, + }, + }); + } + /** + * get top hub scripts + * @param data The data for the request. + * @param data.limit query limit + * @param data.app query scripts app + * @param data.kind query scripts kind + * @returns unknown hub scripts list + * @throws ApiError + */ + static getTopHubScripts(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/scripts/hub/top", + query: { + limit: data.limit, + app: data.app, + kind: data.kind, + }, + }); + } + /** + * query hub scripts by similarity + * @param data The data for the request. + * @param data.text query text + * @param data.kind query scripts kind + * @param data.limit query limit + * @param data.app query scripts app + * @returns unknown script details + * @throws ApiError + */ + static queryHubScripts(data) { + return request(OpenAPI, { + method: "GET", + url: "/embeddings/query_hub_scripts", + query: { + text: data.text, + kind: data.kind, + limit: data.limit, + app: data.app, + }, + }); + } + /** + * list scripts for search + * @param data The data for the request. + * @param data.workspace + * @returns unknown script list + * @throws ApiError + */ + static listSearchScript(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/list_search", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list all scripts + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.pathStart mask to filter matching starting path + * @param data.pathExact mask to filter exact matching path + * @param data.firstParentHash mask to filter scripts whom first direct parent has exact hash + * @param data.lastParentHash mask to filter scripts whom last parent in the chain has exact hash. + * Beware that each script stores only a limited number of parents. Hence + * the last parent hash for a script is not necessarily its top-most parent. + * To find the top-most parent you will have to jump from last to last hash + * until finding the parent + * + * @param data.parentHash is the hash present in the array of stored parent hashes for this script. + * The same warning applies than for last_parent_hash. A script only store a + * limited number of direct parent + * + * @param data.showArchived (default false) + * show also the archived files. + * when multiple archived hash share the same path, only the ones with the latest create_at + * are + * ed. + * + * @param data.hideWithoutMain (default false) + * hide the scripts without an exported main function + * + * @param data.isTemplate (default regardless) + * if true show only the templates + * if false show only the non templates + * if not defined, show all regardless of if the script is a template + * + * @param data.kinds (default regardless) + * script kinds to filter, split by comma + * + * @param data.starredOnly (default false) + * show only the starred items + * + * @returns Script All scripts + * @throws ApiError + */ + static listScripts(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + order_desc: data.orderDesc, + created_by: data.createdBy, + path_start: data.pathStart, + path_exact: data.pathExact, + first_parent_hash: data.firstParentHash, + last_parent_hash: data.lastParentHash, + parent_hash: data.parentHash, + show_archived: data.showArchived, + hide_without_main: data.hideWithoutMain, + is_template: data.isTemplate, + kinds: data.kinds, + starred_only: data.starredOnly, + }, + }); + } + /** + * list all scripts paths + * @param data The data for the request. + * @param data.workspace + * @returns string list of script paths + * @throws ApiError + */ + static listScriptPaths(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/list_paths", + path: { + workspace: data.workspace, + }, + }); + } + /** + * create script + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Partially filled script + * @returns string script created + * @throws ApiError + */ + static createScript(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Toggle ON and OFF the workspace error handler for a given script + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody Workspace error handler enabled + * @returns string error handler toggled + * @throws ApiError + */ + static toggleWorkspaceErrorHandlerForScript(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * archive script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string script archived + * @throws ApiError + */ + static archiveScriptByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/archive/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * archive script by hash + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @returns Script script details + * @throws ApiError + */ + static archiveScriptByHash(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/archive/h/{hash}", + path: { + workspace: data.workspace, + hash: data.hash, + }, + }); + } + /** + * delete script by hash (erase content but keep hash, require admin) + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @returns Script script details + * @throws ApiError + */ + static deleteScriptByHash(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/delete/h/{hash}", + path: { + workspace: data.workspace, + hash: data.hash, + }, + }); + } + /** + * delete all scripts at a given path (require admin) + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string script path + * @throws ApiError + */ + static deleteScriptByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/delete/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns Script script details + * @throws ApiError + */ + static getScriptByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/get/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get script by path with draft + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns NewScriptWithDraft script details + * @throws ApiError + */ + static getScriptByPathWithDraft(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/get/draft/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get history of a script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns ScriptHistory script history + * @throws ApiError + */ + static getScriptHistoryByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/history/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * update history of a script + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @param data.path + * @param data.requestBody Script deployment message + * @returns string success + * @throws ApiError + */ + static updateScriptHistory(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/scripts/history_update/h/{hash}/p/{path}", + path: { + workspace: data.workspace, + hash: data.hash, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * raw script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string script content + * @throws ApiError + */ + static rawScriptByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/raw/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * raw script by path with a token (mostly used by lsp to be used with import maps to resolve scripts) + * @param data The data for the request. + * @param data.workspace + * @param data.token + * @param data.path + * @returns string script content + * @throws ApiError + */ + static rawScriptByPathTokened(data) { + return request(OpenAPI, { + method: "GET", + url: "/scripts_u/tokened_raw/{workspace}/{token}/{path}", + path: { + workspace: data.workspace, + token: data.token, + path: data.path, + }, + }); + } + /** + * exists script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean does it exists + * @throws ApiError + */ + static existsScriptByPath(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/exists/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get script by hash + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @returns Script script details + * @throws ApiError + */ + static getScriptByHash(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/get/h/{hash}", + path: { + workspace: data.workspace, + hash: data.hash, + }, + }); + } + /** + * raw script by hash + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string script content + * @throws ApiError + */ + static rawScriptByHash(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/raw/h/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get script deployment status + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @returns unknown script details + * @throws ApiError + */ + static getScriptDeploymentStatus(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/scripts/deployment_status/h/{hash}", + path: { + workspace: data.workspace, + hash: data.hash, + }, + }); + } +}; +var DraftService = class { + /** + * create draft + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns string draft created + * @throws ApiError + */ + static createDraft(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/drafts/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete draft + * @param data The data for the request. + * @param data.workspace + * @param data.kind + * @param data.path + * @returns string draft deleted + * @throws ApiError + */ + static deleteDraft(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/drafts/delete/{kind}/{path}", + path: { + workspace: data.workspace, + kind: data.kind, + path: data.path, + }, + }); + } +}; +var WorkerService = class { + /** + * get all instance custom tags (tags are used to dispatch jobs to different worker groups) + * @returns string list of custom tags + * @throws ApiError + */ + static getCustomTags() { + return request(OpenAPI, { + method: "GET", + url: "/workers/custom_tags", + }); + } + /** + * get all instance default tags + * @returns string list of default tags + * @throws ApiError + */ + static geDefaultTags() { + return request(OpenAPI, { + method: "GET", + url: "/workers/get_default_tags", + }); + } + /** + * is default tags per workspace + * @returns boolean is the default tags per workspace + * @throws ApiError + */ + static isDefaultTagsPerWorkspace() { + return request(OpenAPI, { + method: "GET", + url: "/workers/is_default_tags_per_workspace", + }); + } + /** + * list workers + * @param data The data for the request. + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.pingSince number of seconds the worker must have had a last ping more recent of (default to 300) + * @returns WorkerPing a list of workers + * @throws ApiError + */ + static listWorkers(data = {}) { + return request(OpenAPI, { + method: "GET", + url: "/workers/list", + query: { + page: data.page, + per_page: data.perPage, + ping_since: data.pingSince, + }, + }); + } + /** + * exists worker with tag + * @param data The data for the request. + * @param data.tag + * @returns boolean whether a worker with the tag exists + * @throws ApiError + */ + static existsWorkerWithTag(data) { + return request(OpenAPI, { + method: "GET", + url: "/workers/exists_worker_with_tag", + query: { + tag: data.tag, + }, + }); + } + /** + * get queue metrics + * @returns unknown metrics + * @throws ApiError + */ + static getQueueMetrics() { + return request(OpenAPI, { + method: "GET", + url: "/workers/queue_metrics", + }); + } +}; +var JobService = class { + /** + * run script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody script args + * @param data.scheduledFor when to schedule this job (leave empty for immediate run) + * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.invisibleToOwner make the run invisible to the the script owner (default false) + * @returns string job created + * @throws ApiError + */ + static runScriptByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + scheduled_for: data.scheduledFor, + scheduled_in_secs: data.scheduledInSecs, + parent_job: data.parentJob, + tag: data.tag, + cache_ttl: data.cacheTtl, + job_id: data.jobId, + invisible_to_owner: data.invisibleToOwner, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run script by path in openai format + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody script args + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit + * + * @returns unknown job result + * @throws ApiError + */ + static openaiSyncScriptByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/openai_sync/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + parent_job: data.parentJob, + job_id: data.jobId, + include_header: data.includeHeader, + queue_limit: data.queueLimit, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run script by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody script args + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit + * + * @returns unknown job result + * @throws ApiError + */ + static runWaitResultScriptByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run_wait_result/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + parent_job: data.parentJob, + tag: data.tag, + cache_ttl: data.cacheTtl, + job_id: data.jobId, + include_header: data.includeHeader, + queue_limit: data.queueLimit, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run script by path with get + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit + * + * @param data.payload The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent + * `encodeURIComponent(btoa(JSON.stringify({a: 2})))` + * + * @returns unknown job result + * @throws ApiError + */ + static runWaitResultScriptByPathGet(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/run_wait_result/p/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + parent_job: data.parentJob, + tag: data.tag, + cache_ttl: data.cacheTtl, + job_id: data.jobId, + include_header: data.includeHeader, + queue_limit: data.queueLimit, + payload: data.payload, + }, + }); + } + /** + * run flow by path and wait until completion in openai format + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody script args + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit + * + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @returns unknown job result + * @throws ApiError + */ + static openaiSyncFlowByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/openai_sync/f/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + include_header: data.includeHeader, + queue_limit: data.queueLimit, + job_id: data.jobId, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run flow by path and wait until completion + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody script args + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.queueLimit The maximum size of the queue for which the request would get rejected if that job would push it above that limit + * + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @returns unknown job result + * @throws ApiError + */ + static runWaitResultFlowByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run_wait_result/f/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + include_header: data.includeHeader, + queue_limit: data.queueLimit, + job_id: data.jobId, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get job result by id + * @param data The data for the request. + * @param data.workspace + * @param data.flowJobId + * @param data.nodeId + * @returns unknown job result + * @throws ApiError + */ + static resultById(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/result_by_id/{flow_job_id}/{node_id}", + path: { + workspace: data.workspace, + flow_job_id: data.flowJobId, + node_id: data.nodeId, + }, + }); + } + /** + * run flow by path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody flow args + * @param data.scheduledFor when to schedule this job (leave empty for immediate run) + * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.invisibleToOwner make the run invisible to the the flow owner (default false) + * @returns string job created + * @throws ApiError + */ + static runFlowByPath(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/f/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + scheduled_for: data.scheduledFor, + scheduled_in_secs: data.scheduledInSecs, + parent_job: data.parentJob, + tag: data.tag, + job_id: data.jobId, + include_header: data.includeHeader, + invisible_to_owner: data.invisibleToOwner, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * restart a completed flow at a given step + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.stepId step id to restart the flow from + * @param data.branchOrIterationN for branchall or loop, the iteration at which the flow should restart + * @param data.requestBody flow args + * @param data.scheduledFor when to schedule this job (leave empty for immediate run) + * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.invisibleToOwner make the run invisible to the the flow owner (default false) + * @returns string job created + * @throws ApiError + */ + static restartFlowAtStep(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/restart/f/{id}/from/{step_id}/{branch_or_iteration_n}", + path: { + workspace: data.workspace, + id: data.id, + step_id: data.stepId, + branch_or_iteration_n: data.branchOrIterationN, + }, + query: { + scheduled_for: data.scheduledFor, + scheduled_in_secs: data.scheduledInSecs, + parent_job: data.parentJob, + tag: data.tag, + job_id: data.jobId, + include_header: data.includeHeader, + invisible_to_owner: data.invisibleToOwner, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run script by hash + * @param data The data for the request. + * @param data.workspace + * @param data.hash + * @param data.requestBody Partially filled args + * @param data.scheduledFor when to schedule this job (leave empty for immediate run) + * @param data.scheduledInSecs schedule the script to execute in the number of seconds starting now + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.tag Override the tag to use + * @param data.cacheTtl Override the cache time to live (in seconds). Can not be used to disable caching, only override with a new cache ttl + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.invisibleToOwner make the run invisible to the the script owner (default false) + * @returns string job created + * @throws ApiError + */ + static runScriptByHash(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/h/{hash}", + path: { + workspace: data.workspace, + hash: data.hash, + }, + query: { + scheduled_for: data.scheduledFor, + scheduled_in_secs: data.scheduledInSecs, + parent_job: data.parentJob, + tag: data.tag, + cache_ttl: data.cacheTtl, + job_id: data.jobId, + include_header: data.includeHeader, + invisible_to_owner: data.invisibleToOwner, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run script preview + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody preview + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.invisibleToOwner make the run invisible to the the script owner (default false) + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @returns string job created + * @throws ApiError + */ + static runScriptPreview(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/preview", + path: { + workspace: data.workspace, + }, + query: { + include_header: data.includeHeader, + invisible_to_owner: data.invisibleToOwner, + job_id: data.jobId, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run code-workflow task + * @param data The data for the request. + * @param data.workspace + * @param data.jobId + * @param data.entrypoint + * @param data.requestBody preview + * @returns string job created + * @throws ApiError + */ + static runCodeWorkflowTask(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/workflow_as_code/{job_id}/{entrypoint}", + path: { + workspace: data.workspace, + job_id: data.jobId, + entrypoint: data.entrypoint, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run a one-off dependencies job + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody raw script content + * @returns unknown dependency job result + * @throws ApiError + */ + static runRawScriptDependencies(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/dependencies", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * run flow preview + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody preview + * @param data.includeHeader List of headers's keys (separated with ',') whove value are added to the args + * Header's key lowercased and '-'' replaced to '_' such that 'Content-Type' becomes the 'content_type' arg key + * + * @param data.invisibleToOwner make the run invisible to the the script owner (default false) + * @param data.jobId The job id to assign to the created job. if missing, job is chosen randomly using the ULID scheme. If a job id already exists in the queue or as a completed job, the request to create one will fail (Bad Request) + * @returns string job created + * @throws ApiError + */ + static runFlowPreview(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/run/preview_flow", + path: { + workspace: data.workspace, + }, + query: { + include_header: data.includeHeader, + invisible_to_owner: data.invisibleToOwner, + job_id: data.jobId, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * list all queued jobs + * @param data The data for the request. + * @param data.workspace + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.scriptPathExact mask to filter exact matching path + * @param data.scriptPathStart mask to filter matching starting path + * @param data.schedulePath mask to filter by schedule path + * @param data.scriptHash mask to filter exact matching path + * @param data.startedBefore filter on started before (inclusive) timestamp + * @param data.startedAfter filter on started after (exclusive) timestamp + * @param data.success filter on successful jobs + * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) + * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, + * @param data.suspended filter on suspended jobs + * @param data.running filter on running jobs + * @param data.args filter on jobs containing those args as a json subset (@> in postgres) + * @param data.result filter on jobs containing those result as a json subset (@> in postgres) + * @param data.tag filter on jobs with a given tag/worker group + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) + * @param data.isNotSchedule is not a scheduled job + * @returns QueuedJob All queued jobs + * @throws ApiError + */ + static listQueue(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/queue/list", + path: { + workspace: data.workspace, + }, + query: { + order_desc: data.orderDesc, + created_by: data.createdBy, + parent_job: data.parentJob, + script_path_exact: data.scriptPathExact, + script_path_start: data.scriptPathStart, + schedule_path: data.schedulePath, + script_hash: data.scriptHash, + started_before: data.startedBefore, + started_after: data.startedAfter, + success: data.success, + scheduled_for_before_now: data.scheduledForBeforeNow, + job_kinds: data.jobKinds, + suspended: data.suspended, + running: data.running, + args: data.args, + result: data.result, + tag: data.tag, + page: data.page, + per_page: data.perPage, + all_workspaces: data.allWorkspaces, + is_not_schedule: data.isNotSchedule, + }, + }); + } + /** + * get queue count + * @param data The data for the request. + * @param data.workspace + * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) + * @returns unknown queue count + * @throws ApiError + */ + static getQueueCount(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/queue/count", + path: { + workspace: data.workspace, + }, + query: { + all_workspaces: data.allWorkspaces, + }, + }); + } + /** + * get completed count + * @param data The data for the request. + * @param data.workspace + * @returns unknown completed count + * @throws ApiError + */ + static getCompletedCount(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/completed/count", + path: { + workspace: data.workspace, + }, + }); + } + /** + * cancel all jobs + * @param data The data for the request. + * @param data.workspace + * @returns string uuids of canceled jobs + * @throws ApiError + */ + static cancelAll(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/queue/cancel_all", + path: { + workspace: data.workspace, + }, + }); + } + /** + * list all completed jobs + * @param data The data for the request. + * @param data.workspace + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.label mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.scriptPathExact mask to filter exact matching path + * @param data.scriptPathStart mask to filter matching starting path + * @param data.schedulePath mask to filter by schedule path + * @param data.scriptHash mask to filter exact matching path + * @param data.startedBefore filter on started before (inclusive) timestamp + * @param data.startedAfter filter on started after (exclusive) timestamp + * @param data.success filter on successful jobs + * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, + * @param data.args filter on jobs containing those args as a json subset (@> in postgres) + * @param data.result filter on jobs containing those result as a json subset (@> in postgres) + * @param data.tag filter on jobs with a given tag/worker group + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.isSkipped is the job skipped + * @param data.isFlowStep is the job a flow step + * @param data.hasNullParent has null parent + * @param data.isNotSchedule is not a scheduled job + * @returns CompletedJob All completed jobs + * @throws ApiError + */ + static listCompletedJobs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/completed/list", + path: { + workspace: data.workspace, + }, + query: { + order_desc: data.orderDesc, + created_by: data.createdBy, + label: data.label, + parent_job: data.parentJob, + script_path_exact: data.scriptPathExact, + script_path_start: data.scriptPathStart, + schedule_path: data.schedulePath, + script_hash: data.scriptHash, + started_before: data.startedBefore, + started_after: data.startedAfter, + success: data.success, + job_kinds: data.jobKinds, + args: data.args, + result: data.result, + tag: data.tag, + page: data.page, + per_page: data.perPage, + is_skipped: data.isSkipped, + is_flow_step: data.isFlowStep, + has_null_parent: data.hasNullParent, + is_not_schedule: data.isNotSchedule, + }, + }); + } + /** + * list all jobs + * @param data The data for the request. + * @param data.workspace + * @param data.createdBy mask to filter exact matching user creator + * @param data.label mask to filter exact matching job's label (job labels are completed jobs with as a result an object containing a string in the array at key 'wm_labels') + * @param data.parentJob The parent job that is at the origin and responsible for the execution of this script if any + * @param data.scriptPathExact mask to filter exact matching path + * @param data.scriptPathStart mask to filter matching starting path + * @param data.schedulePath mask to filter by schedule path + * @param data.scriptHash mask to filter exact matching path + * @param data.startedBefore filter on started before (inclusive) timestamp + * @param data.startedAfter filter on started after (exclusive) timestamp + * @param data.createdOrStartedBefore filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp + * @param data.running filter on running jobs + * @param data.scheduledForBeforeNow filter on jobs scheduled_for before now (hence waitinf for a worker) + * @param data.createdOrStartedAfter filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp + * @param data.jobKinds filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by, + * @param data.args filter on jobs containing those args as a json subset (@> in postgres) + * @param data.tag filter on jobs with a given tag/worker group + * @param data.result filter on jobs containing those result as a json subset (@> in postgres) + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.isSkipped is the job skipped + * @param data.isFlowStep is the job a flow step + * @param data.hasNullParent has null parent + * @param data.success filter on successful jobs + * @param data.allWorkspaces get jobs from all workspaces (only valid if request come from the `admins` workspace) + * @param data.isNotSchedule is not a scheduled job + * @returns Job All jobs + * @throws ApiError + */ + static listJobs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/list", + path: { + workspace: data.workspace, + }, + query: { + created_by: data.createdBy, + label: data.label, + parent_job: data.parentJob, + script_path_exact: data.scriptPathExact, + script_path_start: data.scriptPathStart, + schedule_path: data.schedulePath, + script_hash: data.scriptHash, + started_before: data.startedBefore, + started_after: data.startedAfter, + created_or_started_before: data.createdOrStartedBefore, + running: data.running, + scheduled_for_before_now: data.scheduledForBeforeNow, + created_or_started_after: data.createdOrStartedAfter, + job_kinds: data.jobKinds, + args: data.args, + tag: data.tag, + result: data.result, + page: data.page, + per_page: data.perPage, + is_skipped: data.isSkipped, + is_flow_step: data.isFlowStep, + has_null_parent: data.hasNullParent, + success: data.success, + all_workspaces: data.allWorkspaces, + is_not_schedule: data.isNotSchedule, + }, + }); + } + /** + * get db clock + * @returns number the timestamp of the db that can be used to compute the drift + * @throws ApiError + */ + static getDbClock() { + return request(OpenAPI, { + method: "GET", + url: "/jobs/db_clock", + }); + } + /** + * get job + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.noLogs + * @returns Job job details + * @throws ApiError + */ + static getJob(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + query: { + no_logs: data.noLogs, + }, + }); + } + /** + * get root job id + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns string get root job id + * @throws ApiError + */ + static getRootJobId(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get_root_job_id/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * get job logs + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns string job details + * @throws ApiError + */ + static getJobLogs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get_logs/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * get job updates + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.running + * @param data.logOffset + * @returns unknown job details + * @throws ApiError + */ + static getJobUpdates(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/getupdate/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + query: { + running: data.running, + log_offset: data.logOffset, + }, + }); + } + /** + * get log file from object store + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown job log + * @throws ApiError + */ + static getLogFileFromStore(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get_log_file/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get flow debug info + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns unknown flow debug info details + * @throws ApiError + */ + static getFlowDebugInfo(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get_flow_debug_info/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * get completed job + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns CompletedJob job details + * @throws ApiError + */ + static getCompletedJob(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/completed/get/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * get completed job result + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns unknown result + * @throws ApiError + */ + static getCompletedJobResult(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/completed/get_result/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * get completed job result if job is completed + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.getStarted + * @returns unknown result + * @throws ApiError + */ + static getCompletedJobResultMaybe(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/completed/get_result_maybe/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + query: { + get_started: data.getStarted, + }, + }); + } + /** + * delete completed job (erase content but keep run id) + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns CompletedJob job details + * @throws ApiError + */ + static deleteCompletedJob(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/completed/delete/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + }); + } + /** + * cancel queued job + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.requestBody reason + * @returns string job canceled + * @throws ApiError + */ + static cancelQueuedJob(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs_u/queue/cancel/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * cancel all queued jobs for persistent script + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody reason + * @returns string persistent job scaled down to zero + * @throws ApiError + */ + static cancelPersistentQueuedJobs(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs_u/queue/cancel_persistent/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * force cancel queued job + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.requestBody reason + * @returns string job canceled + * @throws ApiError + */ + static forceCancelQueuedJob(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs_u/queue/force_cancel/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * create an HMac signature given a job id and a resume id + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.approver + * @returns string job signature + * @throws ApiError + */ + static createJobSignature(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/job_signature/{id}/{resume_id}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + }, + query: { + approver: data.approver, + }, + }); + } + /** + * get resume urls given a job_id, resume_id and a nonce to resume a flow + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.approver + * @returns unknown url endpoints + * @throws ApiError + */ + static getResumeUrls(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/resume_urls/{id}/{resume_id}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + }, + query: { + approver: data.approver, + }, + }); + } + /** + * resume a job for a suspended flow + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.signature + * @param data.payload The base64 encoded payload that has been encoded as a JSON. e.g how to encode such payload encodeURIComponent + * `encodeURIComponent(btoa(JSON.stringify({a: 2})))` + * + * @param data.approver + * @returns string job resumed + * @throws ApiError + */ + static resumeSuspendedJobGet(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + signature: data.signature, + }, + query: { + payload: data.payload, + approver: data.approver, + }, + }); + } + /** + * resume a job for a suspended flow + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.signature + * @param data.requestBody + * @param data.approver + * @returns string job resumed + * @throws ApiError + */ + static resumeSuspendedJobPost(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs_u/resume/{id}/{resume_id}/{signature}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + signature: data.signature, + }, + query: { + approver: data.approver, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * set flow user state at a given key + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.key + * @param data.requestBody new value + * @returns string flow user state updated + * @throws ApiError + */ + static setFlowUserState(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/flow/user_states/{id}/{key}", + path: { + workspace: data.workspace, + id: data.id, + key: data.key, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get flow user state at a given key + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.key + * @returns unknown flow user state updated + * @throws ApiError + */ + static getFlowUserState(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs/flow/user_states/{id}/{key}", + path: { + workspace: data.workspace, + id: data.id, + key: data.key, + }, + }); + } + /** + * resume a job for a suspended flow as an owner + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.requestBody + * @returns string job resumed + * @throws ApiError + */ + static resumeSuspendedFlowAsOwner(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs/flow/resume/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * cancel a job for a suspended flow + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.signature + * @param data.approver + * @returns string job canceled + * @throws ApiError + */ + static cancelSuspendedJobGet(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + signature: data.signature, + }, + query: { + approver: data.approver, + }, + }); + } + /** + * cancel a job for a suspended flow + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.signature + * @param data.requestBody + * @param data.approver + * @returns string job canceled + * @throws ApiError + */ + static cancelSuspendedJobPost(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/jobs_u/cancel/{id}/{resume_id}/{signature}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + signature: data.signature, + }, + query: { + approver: data.approver, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * get parent flow job of suspended job + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.resumeId + * @param data.signature + * @param data.approver + * @returns unknown parent flow details + * @throws ApiError + */ + static getSuspendedJobFlow(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/jobs_u/get_flow/{id}/{resume_id}/{signature}", + path: { + workspace: data.workspace, + id: data.id, + resume_id: data.resumeId, + signature: data.signature, + }, + query: { + approver: data.approver, + }, + }); + } +}; +var RawAppService = class { + /** + * list all raw apps + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.orderDesc order by desc order (default true) + * @param data.createdBy mask to filter exact matching user creator + * @param data.pathStart mask to filter matching starting path + * @param data.pathExact mask to filter exact matching path + * @param data.starredOnly (default false) + * show only the starred items + * + * @returns ListableRawApp All raw apps + * @throws ApiError + */ + static listRawApps(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/raw_apps/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + order_desc: data.orderDesc, + created_by: data.createdBy, + path_start: data.pathStart, + path_exact: data.pathExact, + starred_only: data.starredOnly, + }, + }); + } + /** + * does an app exisst at path + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean app exists + * @throws ApiError + */ + static existsRawApp(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/raw_apps/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get app by path + * @param data The data for the request. + * @param data.workspace + * @param data.version + * @param data.path + * @returns string app details + * @throws ApiError + */ + static getRawAppData(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/apps/get_data/{version}/{path}", + path: { + workspace: data.workspace, + version: data.version, + path: data.path, + }, + }); + } + /** + * create raw app + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new raw app + * @returns string raw app created + * @throws ApiError + */ + static createRawApp(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/raw_apps/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update app + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updateraw app + * @returns string app updated + * @throws ApiError + */ + static updateRawApp(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/raw_apps/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete raw app + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string app deleted + * @throws ApiError + */ + static deleteRawApp(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/raw_apps/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } +}; +var ScheduleService = class { + /** + * preview schedule + * @param data The data for the request. + * @param data.requestBody schedule + * @returns string List of 5 estimated upcoming execution events (in UTC) + * @throws ApiError + */ + static previewSchedule(data) { + return request(OpenAPI, { + method: "POST", + url: "/schedules/preview", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * create schedule + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new schedule + * @returns string schedule created + * @throws ApiError + */ + static createSchedule(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/schedules/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update schedule + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated schedule + * @returns string schedule updated + * @throws ApiError + */ + static updateSchedule(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/schedules/update/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * set enabled schedule + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated schedule enable + * @returns string schedule enabled set + * @throws ApiError + */ + static setScheduleEnabled(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/schedules/setenabled/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete schedule + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string schedule deleted + * @throws ApiError + */ + static deleteSchedule(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/schedules/delete/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get schedule + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns Schedule schedule deleted + * @throws ApiError + */ + static getSchedule(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/schedules/get/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * does schedule exists + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean schedule exists + * @throws ApiError + */ + static existsSchedule(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/schedules/exists/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * list schedules + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.args filter on jobs containing those args as a json subset (@> in postgres) + * @param data.path filter by path + * @param data.isFlow + * @returns Schedule schedule list + * @throws ApiError + */ + static listSchedules(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/schedules/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + args: data.args, + path: data.path, + is_flow: data.isFlow, + }, + }); + } + /** + * list schedules with last 20 jobs + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns ScheduleWJobs schedule list + * @throws ApiError + */ + static listSchedulesWithJobs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/schedules/list_with_jobs", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * Set default error or recoevery handler + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Handler description + * @returns unknown default error handler set + * @throws ApiError + */ + static setDefaultErrorOrRecoveryHandler(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/schedules/setdefaulthandler", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var GroupService = class { + /** + * list instance groups + * @returns InstanceGroup instance group list + * @throws ApiError + */ + static listInstanceGroups() { + return request(OpenAPI, { + method: "GET", + url: "/groups/list", + }); + } + /** + * get instance group + * @param data The data for the request. + * @param data.name + * @returns InstanceGroup instance group + * @throws ApiError + */ + static getInstanceGroup(data) { + return request(OpenAPI, { + method: "GET", + url: "/groups/get/{name}", + path: { + name: data.name, + }, + }); + } + /** + * create instance group + * @param data The data for the request. + * @param data.requestBody create instance group + * @returns string instance group created + * @throws ApiError + */ + static createInstanceGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/groups/create", + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update instance group + * @param data The data for the request. + * @param data.name + * @param data.requestBody update instance group + * @returns string instance group updated + * @throws ApiError + */ + static updateInstanceGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/groups/update/{name}", + path: { + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete instance group + * @param data The data for the request. + * @param data.name + * @returns string instance group deleted + * @throws ApiError + */ + static deleteInstanceGroup(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/groups/delete/{name}", + path: { + name: data.name, + }, + }); + } + /** + * add user to instance group + * @param data The data for the request. + * @param data.name + * @param data.requestBody user to add to instance group + * @returns string user added to instance group + * @throws ApiError + */ + static addUserToInstanceGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/groups/adduser/{name}", + path: { + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * remove user from instance group + * @param data The data for the request. + * @param data.name + * @param data.requestBody user to remove from instance group + * @returns string user removed from instance group + * @throws ApiError + */ + static removeUserFromInstanceGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/groups/removeuser/{name}", + path: { + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * list groups + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Group group list + * @throws ApiError + */ + static listGroups(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/groups/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * list group names + * @param data The data for the request. + * @param data.workspace + * @param data.onlyMemberOf only list the groups the user is member of (default false) + * @returns string group list + * @throws ApiError + */ + static listGroupNames(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/groups/listnames", + path: { + workspace: data.workspace, + }, + query: { + only_member_of: data.onlyMemberOf, + }, + }); + } + /** + * create group + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody create group + * @returns string group created + * @throws ApiError + */ + static createGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/groups/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update group + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody updated group + * @returns string group updated + * @throws ApiError + */ + static updateGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/groups/update/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete group + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns string group deleted + * @throws ApiError + */ + static deleteGroup(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/groups/delete/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * get group + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns Group group + * @throws ApiError + */ + static getGroup(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/groups/get/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * add user to group + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody added user to group + * @returns string user added to group + * @throws ApiError + */ + static addUserToGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/groups/adduser/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * remove user to group + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody added user to group + * @returns string user removed from group + * @throws ApiError + */ + static removeUserToGroup(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/groups/removeuser/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var FolderService = class { + /** + * list folders + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Folder folder list + * @throws ApiError + */ + static listFolders(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/folders/list", + path: { + workspace: data.workspace, + }, + query: { + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * list folder names + * @param data The data for the request. + * @param data.workspace + * @param data.onlyMemberOf only list the folders the user is member of (default false) + * @returns string folder list + * @throws ApiError + */ + static listFolderNames(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/folders/listnames", + path: { + workspace: data.workspace, + }, + query: { + only_member_of: data.onlyMemberOf, + }, + }); + } + /** + * create folder + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody create folder + * @returns string folder created + * @throws ApiError + */ + static createFolder(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/folders/create", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * update folder + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody update folder + * @returns string folder updated + * @throws ApiError + */ + static updateFolder(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/folders/update/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * delete folder + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns string folder deleted + * @throws ApiError + */ + static deleteFolder(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/folders/delete/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * get folder + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns Folder folder + * @throws ApiError + */ + static getFolder(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/folders/get/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * get folder usage + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @returns unknown folder + * @throws ApiError + */ + static getFolderUsage(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/folders/getusage/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + }); + } + /** + * add owner to folder + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody owner user to folder + * @returns string owner added to folder + * @throws ApiError + */ + static addOwnerToFolder(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/folders/addowner/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * remove owner to folder + * @param data The data for the request. + * @param data.workspace + * @param data.name + * @param data.requestBody added owner to folder + * @returns string owner removed from folder + * @throws ApiError + */ + static removeOwnerToFolder(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/folders/removeowner/{name}", + path: { + workspace: data.workspace, + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var ConfigService = class { + /** + * list worker groups + * @returns unknown a list of worker group configs + * @throws ApiError + */ + static listWorkerGroups() { + return request(OpenAPI, { + method: "GET", + url: "/configs/list_worker_groups", + }); + } + /** + * get config + * @param data The data for the request. + * @param data.name + * @returns unknown a config + * @throws ApiError + */ + static getConfig(data) { + return request(OpenAPI, { + method: "GET", + url: "/configs/get/{name}", + path: { + name: data.name, + }, + }); + } + /** + * Update config + * @param data The data for the request. + * @param data.name + * @param data.requestBody worker group + * @returns string Update a worker group + * @throws ApiError + */ + static updateConfig(data) { + return request(OpenAPI, { + method: "POST", + url: "/configs/update/{name}", + path: { + name: data.name, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Delete Config + * @param data The data for the request. + * @param data.name + * @returns string Delete config + * @throws ApiError + */ + static deleteConfig(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/configs/update/{name}", + path: { + name: data.name, + }, + }); + } +}; +var GranularAclService = class { + /** + * get granular acls + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.kind + * @returns boolean acls + * @throws ApiError + */ + static getGranularAcls(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/acls/get/{kind}/{path}", + path: { + workspace: data.workspace, + path: data.path, + kind: data.kind, + }, + }); + } + /** + * add granular acls + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.kind + * @param data.requestBody acl to add + * @returns string granular acl added + * @throws ApiError + */ + static addGranularAcls(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/acls/add/{kind}/{path}", + path: { + workspace: data.workspace, + path: data.path, + kind: data.kind, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * remove granular acls + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.kind + * @param data.requestBody acl to add + * @returns string granular acl removed + * @throws ApiError + */ + static removeGranularAcls(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/acls/remove/{kind}/{path}", + path: { + workspace: data.workspace, + path: data.path, + kind: data.kind, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var CaptureService = class { + /** + * update flow preview capture + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns void flow preview captured + * @throws ApiError + */ + static updateCapture(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/capture_u/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * create flow preview capture + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown flow preview capture created + * @throws ApiError + */ + static createCapture(data) { + return request(OpenAPI, { + method: "PUT", + url: "/w/{workspace}/capture/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + }); + } + /** + * get flow preview capture + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns unknown captured flow preview + * @throws ApiError + */ + static getCapture(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/capture/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + errors: { + 404: "capture does not exist for this flow", + }, + }); + } +}; +var FavoriteService = class { + /** + * star item + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns unknown star item + * @throws ApiError + */ + static star(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/favorites/star", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * unstar item + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns unknown unstar item + * @throws ApiError + */ + static unstar(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/favorites/unstar", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var InputService = class { + /** + * List Inputs used in previously completed jobs + * @param data The data for the request. + * @param data.workspace + * @param data.runnableId + * @param data.runnableType + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Input Input history for completed jobs + * @throws ApiError + */ + static getInputHistory(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/inputs/history", + path: { + workspace: data.workspace, + }, + query: { + runnable_id: data.runnableId, + runnable_type: data.runnableType, + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * Get args from history or saved input + * @param data The data for the request. + * @param data.workspace + * @param data.jobOrInputId + * @returns unknown args + * @throws ApiError + */ + static getArgsFromHistoryOrSavedInput(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/inputs/{jobOrInputId}/args", + path: { + workspace: data.workspace, + jobOrInputId: data.jobOrInputId, + }, + }); + } + /** + * List saved Inputs for a Runnable + * @param data The data for the request. + * @param data.workspace + * @param data.runnableId + * @param data.runnableType + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @returns Input Saved Inputs for a Runnable + * @throws ApiError + */ + static listInputs(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/inputs/list", + path: { + workspace: data.workspace, + }, + query: { + runnable_id: data.runnableId, + runnable_type: data.runnableType, + page: data.page, + per_page: data.perPage, + }, + }); + } + /** + * Create an Input for future use in a script or flow + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody Input + * @param data.runnableId + * @param data.runnableType + * @returns string Input created + * @throws ApiError + */ + static createInput(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/inputs/create", + path: { + workspace: data.workspace, + }, + query: { + runnable_id: data.runnableId, + runnable_type: data.runnableType, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Update an Input + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody UpdateInput + * @returns string Input updated + * @throws ApiError + */ + static updateInput(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/inputs/update", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Delete a Saved Input + * @param data The data for the request. + * @param data.workspace + * @param data.input + * @returns string Input deleted + * @throws ApiError + */ + static deleteInput(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/inputs/delete/{input}", + path: { + workspace: data.workspace, + input: data.input, + }, + }); + } +}; +var HelpersService = class { + /** + * Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody S3 resource to connect to + * @returns unknown Connection settings + * @throws ApiError + */ + static duckdbConnectionSettings(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/duckdb_connection_settings", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Converts an S3 resource to the set of instructions necessary to connect DuckDB to an S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used + * @returns unknown Connection settings + * @throws ApiError + */ + static duckdbConnectionSettingsV2(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/v2/duckdb_connection_settings", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody S3 resource to connect to + * @returns unknown Connection settings + * @throws ApiError + */ + static polarsConnectionSettings(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/polars_connection_settings", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Converts an S3 resource to the set of arguments necessary to connect Polars to an S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody S3 resource path to use to generate the connection settings. If empty, the S3 resource defined in the workspace settings will be used + * @returns unknown Connection settings + * @throws ApiError + */ + static polarsConnectionSettingsV2(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/v2/polars_connection_settings", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Returns the s3 resource associated to the provided path, or the workspace default S3 resource + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody S3 resource path to use. If empty, the S3 resource defined in the workspace settings will be used + * @returns S3Resource Connection settings + * @throws ApiError + */ + static s3ResourceInfo(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/v2/s3_resource_info", + path: { + workspace: data.workspace, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } + /** + * Test connection to the workspace datasets storage + * @param data The data for the request. + * @param data.workspace + * @returns unknown Connection settings + * @throws ApiError + */ + static datasetStorageTestConnection(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/test_connection", + path: { + workspace: data.workspace, + }, + }); + } + /** + * List the file keys available in the workspace files storage (S3) + * @param data The data for the request. + * @param data.workspace + * @param data.maxKeys + * @param data.marker + * @param data.prefix + * @returns unknown List of file keys + * @throws ApiError + */ + static listStoredFiles(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/list_stored_files", + path: { + workspace: data.workspace, + }, + query: { + max_keys: data.maxKeys, + marker: data.marker, + prefix: data.prefix, + }, + }); + } + /** + * Load metadata of the file + * @param data The data for the request. + * @param data.workspace + * @param data.fileKey + * @returns WindmillFileMetadata FileMetadata + * @throws ApiError + */ + static loadFileMetadata(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/load_file_metadata", + path: { + workspace: data.workspace, + }, + query: { + file_key: data.fileKey, + }, + }); + } + /** + * Load a preview of the file + * @param data The data for the request. + * @param data.workspace + * @param data.fileKey + * @param data.fileSizeInBytes + * @param data.fileMimeType + * @param data.csvSeparator + * @param data.csvHasHeader + * @param data.readBytesFrom + * @param data.readBytesLength + * @returns WindmillFilePreview FilePreview + * @throws ApiError + */ + static loadFilePreview(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/load_file_preview", + path: { + workspace: data.workspace, + }, + query: { + file_key: data.fileKey, + file_size_in_bytes: data.fileSizeInBytes, + file_mime_type: data.fileMimeType, + csv_separator: data.csvSeparator, + csv_has_header: data.csvHasHeader, + read_bytes_from: data.readBytesFrom, + read_bytes_length: data.readBytesLength, + }, + }); + } + /** + * Load a preview of a parquet file + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.offset + * @param data.limit + * @param data.sortCol + * @param data.sortDesc + * @param data.searchCol + * @param data.searchTerm + * @returns unknown Parquet Preview + * @throws ApiError + */ + static loadParquetPreview(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/load_parquet_preview/{path}", + path: { + workspace: data.workspace, + path: data.path, + }, + query: { + offset: data.offset, + limit: data.limit, + sort_col: data.sortCol, + sort_desc: data.sortDesc, + search_col: data.searchCol, + search_term: data.searchTerm, + }, + }); + } + /** + * Permanently delete file from S3 + * @param data The data for the request. + * @param data.workspace + * @param data.fileKey + * @returns unknown Confirmation + * @throws ApiError + */ + static deleteS3File(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/w/{workspace}/job_helpers/delete_s3_file", + path: { + workspace: data.workspace, + }, + query: { + file_key: data.fileKey, + }, + }); + } + /** + * Move a S3 file from one path to the other within the same bucket + * @param data The data for the request. + * @param data.workspace + * @param data.srcFileKey + * @param data.destFileKey + * @returns unknown Confirmation + * @throws ApiError + */ + static moveS3File(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/move_s3_file", + path: { + workspace: data.workspace, + }, + query: { + src_file_key: data.srcFileKey, + dest_file_key: data.destFileKey, + }, + }); + } + /** + * Upload file to S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody File content + * @param data.fileKey + * @param data.fileExtension + * @param data.s3ResourcePath + * @param data.resourceType + * @returns unknown File upload status + * @throws ApiError + */ + static fileUpload(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_helpers/upload_s3_file", + path: { + workspace: data.workspace, + }, + query: { + file_key: data.fileKey, + file_extension: data.fileExtension, + s3_resource_path: data.s3ResourcePath, + resource_type: data.resourceType, + }, + body: data.requestBody, + mediaType: "application/octet-stream", + }); + } + /** + * Download file to S3 bucket + * @param data The data for the request. + * @param data.workspace + * @param data.fileKey + * @param data.s3ResourcePath + * @param data.resourceType + * @returns binary Chunk of the downloaded file + * @throws ApiError + */ + static fileDownload(data) { + return request(OpenAPI, { + method: "GET", + url: "/w/{workspace}/job_helpers/download_s3_file", + path: { + workspace: data.workspace, + }, + query: { + file_key: data.fileKey, + s3_resource_path: data.s3ResourcePath, + resource_type: data.resourceType, + }, + }); + } +}; +var MetricsService = class { + /** + * get job metrics + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @param data.requestBody parameters for statistics retrieval + * @returns unknown job details + * @throws ApiError + */ + static getJobMetrics(data) { + return request(OpenAPI, { + method: "POST", + url: "/w/{workspace}/job_metrics/get/{id}", + path: { + workspace: data.workspace, + id: data.id, + }, + body: data.requestBody, + mediaType: "application/json", + }); + } +}; +var ConcurrencyGroupsService = class { + /** + * List all concurrency groups + * @returns ConcurrencyGroup all concurrency groups + * @throws ApiError + */ + static listConcurrencyGroups() { + return request(OpenAPI, { + method: "GET", + url: "/concurrency_groups/list", + }); + } + /** + * Delete concurrency group + * @param data The data for the request. + * @param data.concurrencyId + * @returns unknown concurrency group removed + * @throws ApiError + */ + static deleteConcurrencyGroup(data) { + return request(OpenAPI, { + method: "DELETE", + url: "/concurrency_groups/{concurrency_id}", + path: { + concurrency_id: data.concurrencyId, + }, + }); + } +}; + +// src/client.ts +var clientSet = false; +function setClient(token, baseUrl) { + if (baseUrl === void 0) { + baseUrl = + getEnv("BASE_INTERNAL_URL") ?? + getEnv("BASE_URL") ?? + "http://localhost:8000"; + } + if (token === void 0) { + token = getEnv("WM_TOKEN") ?? "no_token"; + } + OpenAPI.WITH_CREDENTIALS = true; + OpenAPI.TOKEN = token; + OpenAPI.BASE = baseUrl + "/api"; + clientSet = true; +} +var getEnv = (key) => { + if (typeof window === "undefined") { + return process.env[key]; + } + return window.process.env[key]; +}; +function getWorkspace() { + return getEnv("WM_WORKSPACE") ?? "no_workspace"; +} +async function getResource(path, undefinedIfEmpty) { + !clientSet && setClient(); + const workspace = getWorkspace(); + path = path ?? getStatePath(); + try { + return await ResourceService.getResourceValueInterpolated({ + workspace, + path, + }); + } catch (e) { + if (undefinedIfEmpty && e.status === 404) { + return void 0; + } else { + throw Error( + `Resource not found at ${path} or not visible to you: ${e.body}` + ); + } + } +} +async function getRootJobId(jobId) { + !clientSet && setClient(); + const workspace = getWorkspace(); + jobId = jobId ?? getEnv("WM_JOB_ID"); + if (jobId === void 0) { + throw Error("Job ID not set"); + } + return await JobService.getRootJobId({ workspace, id: jobId }); +} +async function runScript( + path = null, + hash_ = null, + args = null, + verbose = false +) { + args = args || {}; + if (verbose) { + console.info(`running \`${path}\` synchronously with args:`, args); + } + const jobId = await runScriptAsync(path, hash_, args); + return await waitJob(jobId, verbose); +} +async function waitJob(jobId, verbose = false) { + while (true) { + const resultRes = await getResultMaybe(jobId); + const started = resultRes.started; + const completed = resultRes.completed; + const success = resultRes.success; + if (!started && verbose) { + console.info(`job ${jobId} has not started yet`); + } + if (completed) { + const result = resultRes.result; + if (success) { + return result; + } else { + const error = result.error; + throw new Error( + `Job ${jobId} was not successful: ${JSON.stringify(error)}` + ); + } + } + if (verbose) { + console.info(`sleeping 0.5 seconds for jobId: ${jobId}`); + } + await new Promise((resolve2) => setTimeout(resolve2, 500)); + } +} +async function getResultMaybe(jobId) { + !clientSet && setClient(); + const workspace = getWorkspace(); + return await JobService.getCompletedJobResultMaybe({ workspace, id: jobId }); +} +var STRIP_COMMENTS = + /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/gm; +var ARGUMENT_NAMES = /([^\s,]+)/g; +function getParamNames(func) { + const fnStr = func.toString().replace(STRIP_COMMENTS, ""); + let result = fnStr + .slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")) + .match(ARGUMENT_NAMES); + if (result === null) result = []; + return result; +} +function task(f) { + !clientSet && setClient(); + return async (...y) => { + const args = {}; + const paramNames = getParamNames(f); + y.forEach((x, i) => (args[paramNames[i]] = x)); + let req = await fetch( + `${OpenAPI.BASE}/w/${getWorkspace()}/jobs/run/workflow_as_code/${getEnv( + "WM_JOB_ID" + )}/${f.name}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getEnv("WM_TOKEN")}`, + }, + body: JSON.stringify({ args }), + } + ); + let jobId = await req.text(); + console.log(`Started task ${f.name} as job ${jobId}`); + let r = await waitJob(jobId); + console.log(`Task ${f.name} (${jobId}) completed`); + return r; + }; +} +async function runScriptAsync(path, hash_, args, scheduledInSeconds = null) { + !clientSet && setClient(); + if (path && hash_) { + throw new Error("path and hash_ are mutually exclusive"); + } + args = args || {}; + const params = {}; + if (scheduledInSeconds) { + params["scheduled_in_secs"] = scheduledInSeconds; + } + let parentJobId = getEnv("WM_JOB_ID"); + if (parentJobId !== void 0) { + params["parent_job"] = parentJobId; + } + let rootJobId = getEnv("WM_ROOT_FLOW_JOB_ID"); + if (rootJobId != void 0 && rootJobId != "") { + params["root_job"] = rootJobId; + } + let endpoint; + if (path) { + endpoint = `/w/${getWorkspace()}/jobs/run/p/${path}`; + } else if (hash_) { + endpoint = `/w/${getWorkspace()}/jobs/run/h/${hash_}`; + } else { + throw new Error("path or hash_ must be provided"); + } + let url = new URL(OpenAPI.BASE + endpoint); + url.search = new URLSearchParams(params).toString(); + return fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${OpenAPI.TOKEN}`, + }, + body: JSON.stringify(args), + }).then((res) => res.text()); +} +function getStatePath() { + const state_path = getEnv("WM_STATE_PATH_NEW") ?? getEnv("WM_STATE_PATH"); + if (state_path === void 0) { + throw Error("State path not set"); + } + return state_path; +} +async function setResource(value, path, initializeToTypeIfNotExist) { + !clientSet && setClient(); + path = path ?? getStatePath(); + const workspace = getWorkspace(); + if (await ResourceService.existsResource({ workspace, path })) { + await ResourceService.updateResourceValue({ + workspace, + path, + requestBody: { value }, + }); + } else if (initializeToTypeIfNotExist) { + await ResourceService.createResource({ + workspace, + requestBody: { path, value, resource_type: initializeToTypeIfNotExist }, + }); + } else { + throw Error( + `Resource at path ${path} does not exist and no type was provided to initialize it` + ); + } +} +async function setState(state) { + await setResource(state, void 0, "state"); +} +async function setFlowUserState(key, value, errorIfNotPossible) { + !clientSet && setClient(); + if (value === void 0) { + value = null; + } + const workspace = getWorkspace(); + try { + await JobService.setFlowUserState({ + workspace, + id: await getRootJobId(), + key, + requestBody: value, + }); + } catch (e) { + if (errorIfNotPossible) { + throw Error(`Error setting flow user state at ${key}: ${e.body}`); + } else { + console.error(`Error setting flow user state at ${key}: ${e.body}`); + } + } +} +async function getFlowUserState(key, errorIfNotPossible) { + !clientSet && setClient(); + const workspace = getWorkspace(); + try { + return await JobService.getFlowUserState({ + workspace, + id: await getRootJobId(), + key, + }); + } catch (e) { + if (errorIfNotPossible) { + throw Error(`Error setting flow user state at ${key}: ${e.body}`); + } else { + console.error(`Error setting flow user state at ${key}: ${e.body}`); + } + } +} +async function getState() { + return await getResource(getStatePath(), true); +} +async function getVariable(path) { + !clientSet && setClient(); + const workspace = getWorkspace(); + try { + return await VariableService.getVariableValue({ workspace, path }); + } catch (e) { + throw Error( + `Variable not found at ${path} or not visible to you: ${e.body} ${e}` + ); + } +} +async function setVariable( + path, + value, + isSecretIfNotExist, + descriptionIfNotExist +) { + !clientSet && setClient(); + const workspace = getWorkspace(); + if (await VariableService.existsVariable({ workspace, path })) { + await VariableService.updateVariable({ + workspace, + path, + requestBody: { value }, + }); + } else { + await VariableService.createVariable({ + workspace, + requestBody: { + path, + value, + is_secret: isSecretIfNotExist ?? false, + description: descriptionIfNotExist ?? "", + }, + }); + } +} +async function denoS3LightClientSettings(s3_resource_path) { + !clientSet && setClient(); + const workspace = getWorkspace(); + const s3Resource = await HelpersService.s3ResourceInfo({ + workspace, + requestBody: { + s3_resource_path, + }, + }); + let settings = { + ...s3Resource, + }; + return settings; +} +async function loadS3File(s3object, s3ResourcePath = void 0) { + !clientSet && setClient(); + const fileContentBlob = await loadS3FileStream(s3object, s3ResourcePath); + if (fileContentBlob === void 0) { + return void 0; + } + const reader = fileContentBlob.stream().getReader(); + const chunks = []; + while (true) { + const { value: chunk, done } = await reader.read(); + if (done) { + break; + } + chunks.push(chunk); + } + let fileContentLength = 0; + chunks.forEach((item) => { + fileContentLength += item.length; + }); + let fileContent = new Uint8Array(fileContentLength); + let offset = 0; + chunks.forEach((chunk) => { + fileContent.set(chunk, offset); + offset += chunk.length; + }); + return fileContent; +} +async function loadS3FileStream(s3object, s3ResourcePath = void 0) { + !clientSet && setClient(); + let params = {}; + params["file_key"] = s3object.s3; + if (s3ResourcePath !== void 0) { + params["s3_resource_path"] = s3ResourcePath; + } + const queryParams = new URLSearchParams(params); + const fileContentBlob = await fetch( + `${ + OpenAPI.BASE + }/w/${getWorkspace()}/job_helpers/download_s3_file?${queryParams}`, + { + method: "GET", + headers: { + Authorization: `Bearer ${OpenAPI.TOKEN}`, + }, + } + ); + return fileContentBlob.blob(); +} +async function writeS3File(s3object, fileContent, s3ResourcePath = void 0) { + !clientSet && setClient(); + let fileContentBlob; + if (typeof fileContent === "string") { + fileContentBlob = new Blob([fileContent], { + type: "text/plain", + }); + } else { + fileContentBlob = fileContent; + } + const response = await HelpersService.fileUpload({ + workspace: getWorkspace(), + fileKey: s3object?.s3, + fileExtension: void 0, + s3ResourcePath, + requestBody: fileContentBlob, + }); + return { + s3: response.file_key, + }; +} +async function getResumeUrls(approver) { + const nonce = Math.floor(Math.random() * 4294967295); + !clientSet && setClient(); + const workspace = getWorkspace(); + return await JobService.getResumeUrls({ + workspace, + resumeId: nonce, + approver, + id: getEnv("WM_JOB_ID") ?? "NO_JOB_ID", + }); +} +async function getIdToken(audience) { + !clientSet && setClient(); + const workspace = getWorkspace(); + return await OidcService.getOidcToken({ + workspace, + audience, + }); +} +export { + $AppHistory, + $AppWithLastVersion, + $AppWithLastVersionWDraft, + $AuditLog, + $BranchAll, + $BranchOne, + $CompletedJob, + $ConcurrencyGroup, + $ContextualVariable, + $CreateInput, + $CreateResource, + $CreateVariable, + $CreateWorkspace, + $EditResource, + $EditResourceType, + $EditSchedule, + $EditVariable, + $EditWorkspaceUser, + $ExtraPerms, + $Flow, + $FlowMetadata, + $FlowModule, + $FlowModuleValue, + $FlowPreview, + $FlowStatus, + $FlowStatusModule, + $FlowValue, + $Folder, + $ForloopFlow, + $GitRepositorySettings, + $GlobalUserInfo, + $Group, + $HubScriptKind, + $Identity, + $Input, + $InputTransform, + $InstanceGroup, + $JavascriptTransform, + $Job, + $LargeFileStorage, + $ListableApp, + $ListableRawApp, + $ListableResource, + $ListableVariable, + $Login, + $MainArgSignature, + $MetricDataPoint, + $MetricMetadata, + $NewSchedule, + $NewScript, + $NewScriptWithDraft, + $NewToken, + $NewTokenImpersonate, + $OpenFlow, + $OpenFlowWPath, + $PathFlow, + $PathScript, + $PolarsClientKwargs, + $Policy, + $Preview, + $QueuedJob, + $RawScript, + $RawScriptForDependencies, + $Resource, + $ResourceType, + $RestartedFrom, + $Retry, + $RunnableType, + $S3Resource, + $ScalarMetric, + $Schedule, + $ScheduleWJobs, + $Script, + $ScriptArgs, + $ScriptHistory, + $SlackToken, + $StaticTransform, + $TimeseriesMetric, + $TokenResponse, + $TruncatedToken, + $UpdateInput, + $UploadFilePart, + $User, + $UserUsage, + $UserWorkspaceList, + $WhileloopFlow, + $WindmillFileMetadata, + $WindmillFilePreview, + $WindmillLargeFile, + $WorkerPing, + $WorkflowStatus, + $WorkflowStatusRecord, + $WorkflowTask, + $Workspace, + $WorkspaceDefaultScripts, + $WorkspaceGitSyncSettings, + $WorkspaceInvite, + AdminService, + ApiError, + AppService, + AuditService, + CancelError, + CancelablePromise, + CaptureService, + ConcurrencyGroupsService, + ConfigService, + DraftService, + FavoriteService, + FlowService, + FolderService, + GranularAclService, + GroupService, + HelpersService, + InputService, + IntegrationService, + JobService, + MetricsService, + OauthService, + OidcService, + OpenAPI, + RawAppService, + ResourceService, + ScheduleService, + ScriptService, + SettingService, + SettingsService, + UserService, + VariableService, + WorkerService, + WorkspaceService, + denoS3LightClientSettings, + getFlowUserState, + getIdToken, + getResource, + getResumeUrls, + getRootJobId, + getState, + getVariable, + loadS3File, + loadS3FileStream, + runScript, + runScriptAsync, + setClient, + setFlowUserState, + setResource, + setState, + setVariable, + task, + waitJob, + writeS3File, +}; diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 95dcaf41fa..ab6a4989b8 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -6,6 +6,8 @@ * LICENSE-AGPL for a copy of the license. */ +use windmill_common::worker::TMP_DIR; + use anyhow::Result; use const_format::concatcp; #[cfg(feature = "prometheus")] @@ -41,7 +43,7 @@ use windmill_common::{ flows::{FlowModule, FlowModuleValue, FlowValue}, get_latest_deployed_hash_for_path, jobs::{JobKind, JobPayload, QueuedJob}, - scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, + scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang, PREVIEW_IS_CODEBASE_HASH}, users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL, SUPERADMIN_SYNC_EMAIL}, utils::{rd_string, StripPath}, worker::{ @@ -185,10 +187,9 @@ pub async fn create_token_for_owner( Ok(token) } -pub const TMP_DIR: &str = "/tmp/windmill"; -pub const TMP_LOGS_DIR: &str = "/tmp/windmill/logs"; +pub const TMP_LOGS_DIR: &str = concatcp!(TMP_DIR, "/logs"); -pub const ROOT_CACHE_DIR: &str = "/tmp/windmill/cache/"; +pub const ROOT_CACHE_DIR: &str = concatcp!(TMP_DIR, "/cache/"); pub const LOCK_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "lock"); pub const PIP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "pip"); pub const TAR_PIP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "tar/pip"); @@ -198,6 +199,7 @@ pub const DENO_CACHE_DIR_NPM: &str = concatcp!(ROOT_CACHE_DIR, "deno/npm"); pub const GO_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "go"); pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun"); + pub const HUB_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "hub"); pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin"); pub const POWERSHELL_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "powershell"); @@ -606,7 +608,6 @@ pub async fn run_worker path.to_string(), SpawnWorker::Script { path, .. } => path.to_string(), }; + let path2 = path.clone(); let w_id = w_id.to_string(); - let (content, lock, language, envs) = match sw { + let (content, lock, language, envs, codebase) = match sw { SpawnWorker::Script { path, hash } => { let q = if let Some(hash) = hash { get_script_content_by_hash(&hash, &w_id, &db).await.map( - |r: ContentReqLangEnvs| Some((r.content, r.lockfile, r.language, r.envs)), + |r: ContentReqLangEnvs| { + Some((r.content, r.lockfile, r.language, r.envs, r.codebase)) + }, ) } else { - sqlx::query_as::<_, (String, Option, Option, Option>)>( - "SELECT content, lock, language, envs FROM script WHERE path = $1 AND workspace_id = $2 AND + sqlx::query_as::<_, (String, Option, Option, Option>, bool, Option)>( + "SELECT content, lock, language, envs, codebase IS NOT NULL, hash FROM script WHERE path = $1 AND workspace_id = $2 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND deleted = false AND lock IS not NULL AND lock_error_logs IS NULL)", ) @@ -2046,6 +2063,7 @@ async fn spawn_dedicated_worker( .fetch_optional(&db) .await .map_err(|e| Error::InternalErr(format!("expected content and lock: {e}"))) + .map(|x| x.map(|y| (y.0, y.1, y.2, y.3, if y.4 { y.5.map(|z| z.to_string()) } else { None }))) }; if let Ok(q) = q { if let Some(wp) = q { @@ -2064,7 +2082,9 @@ async fn spawn_dedicated_worker( return None; } } - SpawnWorker::RawScript { content, lock, lang, .. } => (content, lock, Some(lang), None), + SpawnWorker::RawScript { content, lock, lang, .. } => { + (content, lock, Some(lang), None, None) + } }; match language { @@ -2119,6 +2139,7 @@ async fn spawn_dedicated_worker( Some(ScriptLang::Bun) => { crate::bun_executor::start_worker( lock, + codebase, &db, &content, &base_internal_url, @@ -2177,6 +2198,7 @@ async fn queue_init_bash_maybe<'c, R: rsmq_async::RsmqConnection + Send + 'c>( tx, "admins", windmill_common::jobs::JobPayload::Code(windmill_common::jobs::RawCode { + hash: None, content: content.clone(), path: Some(format!("init_script_{worker_name}")), language: ScriptLang::Bash, @@ -2535,6 +2557,7 @@ pub struct JobCompleted { async fn do_nativets( job: &QueuedJob, client: &AuthedClientBackgroundTask, + env_code: String, code: String, db: &Pool, mem_peak: &mut i32, @@ -2549,6 +2572,7 @@ async fn do_nativets( }; let result = eval_fetch_timeout( + env_code, code.clone(), transpile_ts(code)?, job_args, @@ -2582,6 +2606,7 @@ async fn handle_queued_job( base_internal_url: &str, rsmq: Option, job_completed_tx: JobCompletedSender, + worker_code_execution_metric: &mut f32, _worker_flow_initial_transition_duration: Option, _worker_code_execution_duration: Option, ) -> windmill_common::error::Result<()> { @@ -2808,6 +2833,7 @@ async fn handle_queued_job( _ => { #[cfg(feature = "prometheus")] let timer = _worker_code_execution_duration.map(|x| x.start_timer()); + let metric_timer = Instant::now(); let r = handle_code_execution_job( job.as_ref(), db, @@ -2821,6 +2847,7 @@ async fn handle_queued_job( &mut column_order, ) .await; + *worker_code_execution_metric += metric_timer.elapsed().as_secs_f32(); #[cfg(feature = "prometheus")] timer.map(|x| x.stop_and_record()); r @@ -2831,6 +2858,14 @@ async fn handle_queued_job( if job.as_ref().workspace_id == "" { return Ok(()); } + + if result + .as_ref() + .is_err_and(|err| matches!(err, &Error::AlreadyCompleted(_))) + { + return Ok(()); + } + process_result( job, result, @@ -2975,6 +3010,7 @@ struct ContentReqLangEnvs { lockfile: Option, language: Option, envs: Option>, + codebase: Option, } async fn get_hub_script_content_and_requirements( @@ -3011,6 +3047,7 @@ async fn get_hub_script_content_and_requirements( lockfile: script.lockfile, language: Some(script.language), envs: None, + codebase: None, }) } @@ -3043,16 +3080,27 @@ async fn get_script_content_by_hash( Option, Option, Option>, + bool, ), >( - "SELECT content, lock, language, envs FROM script WHERE hash = $1 AND workspace_id = $2", + "SELECT content, lock, language, envs, codebase IS NOT NULL FROM script WHERE hash = $1 AND workspace_id = $2", ) .bind(script_hash.0) .bind(w_id) .fetch_optional(db) .await? .ok_or_else(|| Error::InternalErr(format!("expected content and lock")))?; - Ok(ContentReqLangEnvs { content: r.0, lockfile: r.1, language: r.2, envs: r.3 }) + Ok(ContentReqLangEnvs { + content: r.0, + lockfile: r.1, + language: r.2, + envs: r.3, + codebase: if r.4 { + Some(script_hash.to_string()) + } else { + None + }, + }) } #[tracing::instrument(level = "trace", skip_all)] @@ -3068,35 +3116,48 @@ async fn handle_code_execution_job( worker_name: &str, column_order: &mut Option>, ) -> error::Result> { - let ContentReqLangEnvs { content: inner_content, lockfile: requirements_o, language, envs } = - match job.job_kind { - JobKind::Preview => ContentReqLangEnvs { - content: job - .raw_code - .clone() - .unwrap_or_else(|| "no raw code".to_owned()), - lockfile: job.raw_lock.clone(), - language: job.language.to_owned(), - envs: None, + let ContentReqLangEnvs { + content: inner_content, + lockfile: requirements_o, + language, + envs, + codebase, + } = match job.job_kind { + JobKind::Preview => ContentReqLangEnvs { + content: job + .raw_code + .clone() + .unwrap_or_else(|| "no raw code".to_owned()), + lockfile: job.raw_lock.clone(), + language: job.language.to_owned(), + envs: None, + codebase: if job + .script_hash + .is_some_and(|y| y.0 == PREVIEW_IS_CODEBASE_HASH) + { + Some(job.id.to_string()) + } else { + None }, - JobKind::Script_Hub => { - get_hub_script_content_and_requirements(job.script_path.clone(), db).await? - } - JobKind::Script => { - get_script_content_by_hash( - &job.script_hash.unwrap_or(ScriptHash(0)), - &job.workspace_id, - db, - ) - .await? - } - JobKind::DeploymentCallback => { - get_script_content_by_path(job.script_path.clone(), &job.workspace_id, db).await? - } - _ => unreachable!( - "handle_code_execution_job should never be reachable with a non-code execution job" - ), - }; + }, + JobKind::Script_Hub => { + get_hub_script_content_and_requirements(job.script_path.clone(), db).await? + } + JobKind::Script => { + get_script_content_by_hash( + &job.script_hash.unwrap_or(ScriptHash(0)), + &job.workspace_id, + db, + ) + .await? + } + JobKind::DeploymentCallback => { + get_script_content_by_path(job.script_path.clone(), &job.workspace_id, db).await? + } + _ => unreachable!( + "handle_code_execution_job should never be reachable with a non-code execution job" + ), + }; if language == Some(ScriptLang::Postgresql) { return do_postgresql( @@ -3209,17 +3270,24 @@ async fn handle_code_execution_job( let reserved_variables = get_reserved_variables(job, &client.get_token().await, db).await?; - let code = format!( - "const BASE_URL = '{base_internal_url}';\nconst BASE_INTERNAL_URL = '{base_internal_url}';\n{}\n{}", + let env_code = format!( + "const process = {{ env: {{}} }};\nconst BASE_URL = '{base_internal_url}';\nconst BASE_INTERNAL_URL = '{base_internal_url}';\nprocess.env['BASE_URL'] = BASE_URL;process.env['BASE_INTERNAL_URL'] = BASE_INTERNAL_URL;\n{}", reserved_variables .iter() - .map(|(k, v)| format!("const {} = '{}';\n", k, v)) + .map(|(k, v)| format!("const {} = '{}';\nprocess.env['{}'] = '{}';\n", k, v, k, v)) .collect::>() - .join("\n"), - inner_content - ); - let (result, ts_logs) = - do_nativets(job, &client, code, db, mem_peak, canceled_by, worker_name).await?; + .join("\n")); + let (result, ts_logs) = do_nativets( + job, + &client, + env_code, + inner_content, + db, + mem_peak, + canceled_by, + worker_name, + ) + .await?; append_logs(job.id, job.workspace_id.clone(), ts_logs, db).await; return Ok(result); } @@ -3299,6 +3367,7 @@ mount {{ Some(ScriptLang::Bun) => { handle_bun_job( requirements_o, + codebase, mem_peak, canceled_by, job, diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index a0b2789774..92cb019089 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -3124,6 +3124,7 @@ fn raw_script_to_payload( ) -> JobPayloadWithTag { JobPayloadWithTag { payload: JobPayload::Code(RawCode { + hash: None, path, content: content.clone(), language: language.clone(), diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 464d354e29..92880e3da6 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.323.6"; +export const VERSION = "v1.327.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/codebase.ts b/cli/codebase.ts new file mode 100644 index 0000000000..05b3c0138f --- /dev/null +++ b/cli/codebase.ts @@ -0,0 +1,21 @@ +import { Codebase, SyncOptions } from "./conf.ts"; +import { log } from "./deps.ts"; +import { digestDir } from "./utils.ts"; + +export type SyncCodebase = Codebase & { digest: string }; +export async function listSyncCodebases( + options: SyncOptions +): Promise { + const res: SyncCodebase[] = []; + const nb_codebase = options?.codebases?.length ?? 0; + if (nb_codebase > 0) { + log.info(`Found ${nb_codebase} codebases:`); + } + for (const codebase of options?.codebases ?? []) { + const digest = await digestDir(codebase.relative_path); + log.info(`Codebase ${codebase.relative_path}, digest: ${digest}`); + res.push({ ...codebase, digest }); + } + + return res; +} diff --git a/cli/conf.ts b/cli/conf.ts index 30439bf217..d21d9c6256 100644 --- a/cli/conf.ts +++ b/cli/conf.ts @@ -20,6 +20,13 @@ export interface SyncOptions { extraIncludes?: string[]; excludes?: string[]; defaultTs?: "bun" | "deno"; + codebases?: Codebase[]; +} + +export interface Codebase { + relative_path: string; + includes?: string[]; + excludes?: string[]; } export async function readConfigFile(): Promise { diff --git a/cli/flow.ts b/cli/flow.ts index 731026ba7a..1d4fb69249 100644 --- a/cli/flow.ts +++ b/cli/flow.ts @@ -71,6 +71,8 @@ export async function pushFlow( } } else if (m.value.type == "forloopflow") { replaceInlineScripts(m.value.modules); + } else if (m.value.type == "whileloopflow") { + replaceInlineScripts(m.value.modules); } else if (m.value.type == "branchall") { m.value.branches.forEach((b) => replaceInlineScripts(b.modules)); } else if (m.value.type == "branchone") { diff --git a/cli/main.ts b/cli/main.ts index ea1206b44c..fcf8851b79 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -3,7 +3,9 @@ import { CompletionsCommand, DenoLandProvider, UpgradeCommand, + colors, log, + yamlStringify, } from "./deps.ts"; import flow from "./flow.ts"; import app from "./apps.ts"; @@ -31,7 +33,7 @@ addEventListener("error", (event) => { } }); -export const VERSION = "v1.323.6"; +export const VERSION = "v1.327.0"; let command: any = new Command() .name("wmill") @@ -58,6 +60,23 @@ let command: any = new Command() "Specify headers to use for all requests. e.g: \"HEADERS='h1: v1, h2: v2'\"" ) .version(VERSION) + .command("init", "Bootstrap a windmill project with a wmill.yaml file") + .action(async () => { + if (await Deno.stat("wmill.yaml").catch(() => null)) { + log.error(colors.red("wmill.yaml already exists")); + return; + } + await Deno.writeTextFile( + "wmill.yaml", + yamlStringify({ + defaultTs: "bun", + includes: [], + excludes: [], + codebases: [], + }) + ); + log.info(colors.green("wmill.yaml created")); + }) .command("app", app) .command("flow", flow) .command("script", script) @@ -70,6 +89,7 @@ let command: any = new Command() .command("schedule", schedule) .command("dev", dev) .command("sync", sync) + .command("version", "Show version information") .action(async (opts) => { console.log("CLI build against " + VERSION); diff --git a/cli/metadata.ts b/cli/metadata.ts index bf65713339..b448dd0559 100644 --- a/cli/metadata.ts +++ b/cli/metadata.ts @@ -1,6 +1,6 @@ // deno-lint-ignore-file no-explicit-any import { GlobalOptions } from "./types.ts"; -import { colors, encodeHex, log, yamlParse, yamlStringify } from "./deps.ts"; +import { colors, log, yamlParse, yamlStringify } from "./deps.ts"; import { ScriptMetadata, defaultScriptMetadata, @@ -25,6 +25,7 @@ import { ScriptLanguage } from "./script_common.ts"; import { inferContentTypeFromFilePath } from "./script_common.ts"; import { GlobalDeps } from "./script.ts"; import { yamlOptions } from "./sync.ts"; +import { generateHash } from "./utils.ts"; export async function generateAllMetadata() {} @@ -457,7 +458,11 @@ export async function replaceLock(o?: { lock?: string | string[] }) { export async function parseMetadataFile( scriptPath: string, generateMetadataIfMissing: - | (GlobalOptions & { path: string; workspaceRemote: Workspace }) + | (GlobalOptions & { + path: string; + workspaceRemote: Workspace; + schemaOnly?: boolean; + }) | undefined, globalDeps: GlobalDeps ): Promise<{ isJson: boolean; payload: any; path: string }> { @@ -509,7 +514,6 @@ export async function parseMetadataFile( false, globalDeps ); - scriptInitialMetadata = yamlParse( await Deno.readTextFile(metadataFilePath) ) as ScriptMetadata; @@ -552,12 +556,6 @@ export async function readLockfile(): Promise { } } -async function generateHash(content: string): Promise { - const messageBuffer = new TextEncoder().encode(content); - const hashBuffer = await crypto.subtle.digest("SHA-256", messageBuffer); - return encodeHex(hashBuffer); -} - export async function checkifMetadataUptodate( path: string, requirement: string diff --git a/cli/script.ts b/cli/script.ts index a0cada5706..ff68ba19f6 100644 --- a/cli/script.ts +++ b/cli/script.ts @@ -7,7 +7,6 @@ import { Confirm, JobService, log, - NewScript, readAll, Script, ScriptService, @@ -29,6 +28,7 @@ import { } from "./script_common.ts"; import { elementsToMap, + findCodebase, readDirRecursiveWithIgnore, yamlOptions, } from "./sync.ts"; @@ -39,6 +39,7 @@ import { mergeConfigWithConfigFile, readConfigFile, } from "./conf.ts"; +import { SyncCodebase, listSyncCodebases } from "./codebase.ts"; export interface ScriptFile { parent_hash?: string; @@ -71,8 +72,18 @@ async function push(opts: PushOptions, filePath: string) { } await requireLogin(opts); - const globalDeps = await findGlobalDeps(); - await handleFile(filePath, workspace, [], undefined, opts, globalDeps); + const codebases = await listSyncCodebases(opts as SyncOptions); + + const globalDeps = await findGlobalDeps(codebases); + await handleFile( + filePath, + workspace, + [], + undefined, + opts, + globalDeps, + codebases + ); log.info(colors.bold.underline.green(`Script ${filePath} pushed`)); } @@ -81,7 +92,9 @@ export async function handleScriptMetadata( workspace: Workspace, alreadySynced: string[], message: string | undefined, - globalDeps: GlobalDeps + globalDeps: GlobalDeps, + codebases: SyncCodebase[], + opts: GlobalOptions ): Promise { if ( path.endsWith(".script.json") || @@ -94,8 +107,9 @@ export async function handleScriptMetadata( workspace, alreadySynced, message, - undefined, - globalDeps + opts, + globalDeps, + codebases ); } else { return false; @@ -108,7 +122,8 @@ export async function handleFile( alreadySynced: string[], message: string | undefined, opts: (GlobalOptions & { defaultTs?: "bun" | "deno" }) | undefined, - globalDeps: GlobalDeps + globalDeps: GlobalDeps, + codebases: SyncCodebase[] ): Promise { if ( !path.includes(".inline_script.") && @@ -123,14 +138,40 @@ export async function handleFile( const remotePath = path .substring(0, path.indexOf(".")) .replaceAll("\\", "/"); + + const language = inferContentTypeFromFilePath(path, opts?.defaultTs); + + const codebase = + language == "bun" ? findCodebase(path, codebases) : undefined; + + let bundleContent: string | undefined = undefined; + + if (codebase) { + const esbuild = await import("npm:esbuild"); + log.info(`Starting building the bundle for ${path}`); + const out = await esbuild.build({ + entryPoints: [path], + format: "esm", + bundle: true, + write: false, + }); + bundleContent = out.outputFiles[0].text; + log.info(`Finished building the bundle for ${path}`); + } const typed = ( await parseMetadataFile( remotePath, - opts ? { ...opts, path, workspaceRemote: workspace } : undefined, + opts + ? { + ...opts, + path, + workspaceRemote: workspace, + schemaOnly: codebase ? true : undefined, + } + : undefined, globalDeps ) )?.payload; - const language = inferContentTypeFromFilePath(path, opts?.defaultTs); const workspaceId = workspace.workspaceId; @@ -146,6 +187,30 @@ export async function handleFile( } const content = await Deno.readTextFile(path); + const requestBodyCommon = { + content, + description: typed?.description ?? "", + language: language, + path: remotePath.replaceAll("\\", "/"), + summary: typed?.summary ?? "", + kind: typed?.kind, + lock: typed?.lock, + schema: typed?.schema, + tag: typed?.tag, + ws_error_handler_muted: typed?.ws_error_handler_muted, + dedicated_worker: typed?.dedicated_worker, + cache_ttl: typed?.cache_ttl, + concurrency_time_window_s: typed?.concurrency_time_window_s, + concurrent_limit: typed?.concurrent_limit, + deployment_message: message, + restart_unless_cancelled: typed?.restart_unless_cancelled, + visible_to_runner_only: typed?.visible_to_runner_only, + no_main_func: typed?.no_main_func, + priority: typed?.priority, + codebase: codebase?.digest, + timeout: typed?.timeout, + }; + if (remote) { if (content === remote.content) { if ( @@ -172,7 +237,8 @@ export async function handleFile( Boolean(typed.visible_to_runner_only) == Boolean(remote.visible_to_runner_only) && Boolean(typed.no_main_func) == Boolean(remote.no_main_func) && - typed.priority == Boolean(remote.priority)) + typed.priority == Boolean(remote.priority) && + typed.timeout == remote.timeout) ) { log.info(colors.green(`Script ${remotePath} is up to date`)); return true; @@ -182,67 +248,85 @@ export async function handleFile( log.info( colors.yellow.bold(`Creating script with a parent ${remotePath}`) ); - await ScriptService.createScript({ - workspace: workspaceId, - requestBody: { - content, - description: typed?.description ?? "", - language: language, - path: remotePath.replaceAll("\\", "/"), - summary: typed?.summary ?? "", - kind: typed?.kind, - lock: typed?.lock, - parent_hash: remote.hash, - schema: typed?.schema, - tag: typed?.tag, - ws_error_handler_muted: typed?.ws_error_handler_muted, - dedicated_worker: typed?.dedicated_worker, - cache_ttl: typed?.cache_ttl, - concurrency_time_window_s: typed?.concurrency_time_window_s, - concurrent_limit: typed?.concurrent_limit, - deployment_message: message, - restart_unless_cancelled: typed?.restart_unless_cancelled, - visible_to_runner_only: typed?.visible_to_runner_only, - no_main_func: typed?.no_main_func, - priority: typed?.priority, - }, - }); + const body = { + ...requestBodyCommon, + parent_hash: remote.hash, + }; + await createScript(bundleContent, workspaceId, body, workspace); } else { log.info( colors.yellow.bold(`Creating script without parent ${remotePath}`) ); - // no parent hash - await ScriptService.createScript({ - workspace: workspaceId, - requestBody: { - content, - description: typed?.description ?? "", - language: language, - path: remotePath.replaceAll("\\", "/"), - summary: typed?.summary ?? "", - kind: typed?.kind, - lock: typed?.lock, - parent_hash: undefined, - schema: typed?.schema, - tag: typed?.tag, - ws_error_handler_muted: typed?.ws_error_handler_muted, - dedicated_worker: typed?.dedicated_worker, - cache_ttl: typed?.cache_ttl, - concurrency_time_window_s: typed?.concurrency_time_window_s, - concurrent_limit: typed?.concurrent_limit, - deployment_message: message, - restart_unless_cancelled: typed?.restart_unless_cancelled, - visible_to_runner_only: typed?.visible_to_runner_only, - no_main_func: typed?.no_main_func, - priority: typed?.priority, - }, - }); + + const body = { + ...requestBodyCommon, + parent_hash: undefined, + }; + await createScript(bundleContent, workspaceId, body, workspace); } return true; } return false; } +async function createScript( + bundleContent: string | undefined, + workspaceId: string, + body: { + parent_hash: string | undefined; + content: string; + description: any; + language: ScriptLanguage; + path: string; + summary: any; + kind: any; + lock: any; + schema: any; + tag: any; + ws_error_handler_muted: any; + dedicated_worker: any; + cache_ttl: any; + concurrency_time_window_s: any; + concurrent_limit: any; + deployment_message: string | undefined; + restart_unless_cancelled: any; + visible_to_runner_only: any; + no_main_func: any; + priority: any; + }, + workspace: Workspace +) { + if (!bundleContent) { + // no parent hash + await ScriptService.createScript({ + workspace: workspaceId, + requestBody: body, + }); + } else { + const form = new FormData(); + form.append("script", JSON.stringify(body)); + form.append("file", bundleContent); + + const url = + workspace.remote + + "api/w/" + + workspace.workspaceId + + "/scripts/create_snapshot"; + const req = await fetch(url, { + method: "POST", + headers: { Authorization: `Bearer ${workspace.token}` }, + body: form, + }); + if (req.status != 201) { + throw Error( + `Script snapshot creation was not successful: ${req.status} - ${ + req.statusText + } - ${await req.text()}` + ); + } + } +} + export async function findContentFile(filePath: string) { const candidates = filePath.endsWith("script.json") ? exts.map((x) => filePath.replace(".script.json", x)) @@ -588,10 +672,12 @@ export type GlobalDeps = { pkgs: Record; reqs: Record; }; -export async function findGlobalDeps(): Promise { +export async function findGlobalDeps( + codebases: SyncCodebase[] +): Promise { const pkgs: { [key: string]: string } = {}; const reqs: { [key: string]: string } = {}; - const els = await FSFSElement(Deno.cwd()); + const els = await FSFSElement(Deno.cwd(), codebases); for await (const entry of readDirRecursiveWithIgnore((p, isDir) => { p = "/" + p; return ( @@ -614,7 +700,7 @@ async function generateMetadata( schemaOnly?: boolean; yes?: boolean; } & SyncOptions, - scriptPath?: string + scriptPath: string | undefined ) { if (scriptPath == "") { scriptPath = undefined; @@ -626,8 +712,9 @@ async function generateMetadata( const workspace = await resolveWorkspace(opts); await requireLogin(opts); opts = await mergeConfigWithConfigFile(opts); + const codebases = await listSyncCodebases(opts); - const globalDeps = await findGlobalDeps(); + const globalDeps = await findGlobalDeps(codebases); if (scriptPath) { // read script metadata file await generateMetadataInternal( @@ -641,7 +728,7 @@ async function generateMetadata( } else { const ignore = await ignoreF(opts); const elems = await elementsToMap( - await FSFSElement(Deno.cwd()), + await FSFSElement(Deno.cwd(), codebases), (p, isD) => { return ( (!isD && !exts.some((ext) => p.endsWith(ext))) || diff --git a/cli/sync.ts b/cli/sync.ts index 08f72f283d..1b2340e9fd 100644 --- a/cli/sync.ts +++ b/cli/sync.ts @@ -45,6 +45,7 @@ import { handleFile } from "./script.ts"; import { deepEqual } from "./utils.ts"; import { SyncOptions, mergeConfigWithConfigFile } from "./conf.ts"; import { removePathPrefix } from "./types.ts"; +import { SyncCodebase, listSyncCodebases } from "./codebase.ts"; type DynFSElement = { isDirectory: boolean; @@ -54,8 +55,85 @@ type DynFSElement = { getChildren(): AsyncIterable; }; -export async function FSFSElement(p: string): Promise { - function _internal_element(localP: string, isDir: boolean): DynFSElement { +export function findCodebase( + path: string, + codebases: SyncCodebase[] +): SyncCodebase | undefined { + for (const c of codebases) { + let included = false; + let excluded = false; + if (c.includes == undefined || c.includes == null) { + included = true; + } + if (typeof c.includes == "string") { + c.includes = [c.includes]; + } + for (const r of c.includes ?? []) { + if (included) { + break; + } + if (minimatch(path, r)) { + included = true; + } + } + if (typeof c.excludes == "string") { + c.excludes = [c.excludes]; + } + for (const r of c.excludes ?? []) { + if (minimatch(path, r)) { + excluded = true; + } + } + if (included && !excluded) { + return c; + } + } +} +async function addCodebaseDigestIfRelevant( + path: string, + content: string, + codebases: SyncCodebase[] +): Promise { + const isScript = path.endsWith(".script.yaml"); + if (!isScript) { + return content; + } + let isTs = true; + try { + await Deno.stat(path.replace(".script.yaml", ".ts")); + } catch { + isTs = false; + } + if (!isTs) { + return content; + } + log.info(`isScript: ${isScript}, isTs: ${isTs}; ${path}`); + if (isTs) { + const c = findCodebase(path, codebases); + if (c) { + const parsed: any = yamlParse(content); + if (parsed && typeof parsed == "object") { + parsed["codebase"] = c.digest; + return yamlStringify(parsed, yamlOptions); + } else { + throw Error( + `Expected local yaml ${path} to be an object, found: ${content} instead` + ); + } + } + } + return content; +} + +export async function FSFSElement( + p: string, + codebases: SyncCodebase[] +): Promise { + function _internal_element( + localP: string, + isDir: boolean, + codebases: SyncCodebase[] + ): DynFSElement { return { isDirectory: isDir, path: localP.substring(p.length + 1), @@ -63,7 +141,11 @@ export async function FSFSElement(p: string): Promise { if (!isDir) return []; try { for await (const e of Deno.readDir(localP)) { - yield _internal_element(path.join(localP, e.name), e.isDirectory); + yield _internal_element( + path.join(localP, e.name), + e.isDirectory, + codebases + ); } } catch (e) { log.warning(`Error reading dir: ${localP}, ${e}`); @@ -74,11 +156,12 @@ export async function FSFSElement(p: string): Promise { // }, async getContentText(): Promise { const content = await Deno.readTextFile(localP); - return content; + + return await addCodebaseDigestIfRelevant(localP, content, codebases); }, }; } - return _internal_element(p, (await Deno.stat(p)).isDirectory); + return _internal_element(p, (await Deno.stat(p)).isDirectory, codebases); } function prioritizeName(name: string): string { @@ -727,6 +810,8 @@ async function pull(opts: GlobalOptions & SyncOptions) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); + const codebases = await listSyncCodebases(opts); + log.info( colors.gray( "Computing the files to update locally to match remote (taking wmill.yaml into account)" @@ -748,8 +833,8 @@ async function pull(opts: GlobalOptions & SyncOptions) { !opts.json ); const local = !opts.stateful - ? await FSFSElement(Deno.cwd()) - : await FSFSElement(path.join(Deno.cwd(), ".wmill")); + ? await FSFSElement(Deno.cwd(), codebases) + : await FSFSElement(path.join(Deno.cwd(), ".wmill"), []); const changes = await compareDynFSElement( remote, local, @@ -924,6 +1009,7 @@ function removeSuffix(str: string, suffix: string) { async function push(opts: GlobalOptions & SyncOptions) { opts = await mergeConfigWithConfigFile(opts); + const codebases = await listSyncCodebases(opts); if (opts.raw) { log.info("--raw is now the default, you can remove it as a flag"); } @@ -962,7 +1048,7 @@ async function push(opts: GlobalOptions & SyncOptions) { !opts.json ); - const local = await FSFSElement(path.join(Deno.cwd(), "")); + const local = await FSFSElement(path.join(Deno.cwd(), ""), codebases); const changes = await compareDynFSElement( local, remote, @@ -991,10 +1077,11 @@ async function push(opts: GlobalOptions & SyncOptions) { ) { return; } + log.info(colors.gray(`Applying changes to files ...`)); const alreadySynced: string[] = []; - const globalDeps = await findGlobalDeps(); + const globalDeps = await findGlobalDeps(codebases); for await (const change of changes) { const stateTarget = path.join(Deno.cwd(), ".wmill", change.path); @@ -1012,7 +1099,9 @@ async function push(opts: GlobalOptions & SyncOptions) { workspace, alreadySynced, opts.message, - globalDeps + globalDeps, + codebases, + opts ) ) { if (opts.stateful && stateExists) { @@ -1026,7 +1115,8 @@ async function push(opts: GlobalOptions & SyncOptions) { alreadySynced, opts.message, opts, - globalDeps + globalDeps, + codebases ) ) { if (opts.stateful && stateExists) { @@ -1067,7 +1157,8 @@ async function push(opts: GlobalOptions & SyncOptions) { alreadySynced, opts.message, opts, - globalDeps + globalDeps, + codebases ) ) { continue; diff --git a/cli/utils.ts b/cli/utils.ts index 080337d39d..185fe50144 100644 --- a/cli/utils.ts +++ b/cli/utils.ts @@ -2,7 +2,7 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck This file is copied from a JS project, so it's not type-safe. -import { log } from "./deps.ts"; +import { log, encodeHex } from "./deps.ts"; export function deepEqual(a: T, b: T): boolean { if (a === b) return true; @@ -92,3 +92,28 @@ export function getHeaders(): Record | undefined { return undefined; } } + +export async function digestDir(path: string) { + const hashes: string = []; + for await (const e of Deno.readDir(path)) { + const npath = path + "/" + e.name; + if (e.isFile) { + hashes.push(await generateHashFromBuffer(await Deno.readFile(npath))); + } else if (e.isDirectory && !e.isSymlink) { + hashes.push(await digestDir(npath)); + } + } + return await generateHash(hashes.join("")); +} + +export async function generateHash(content: string): Promise { + const messageBuffer = new TextEncoder().encode(content); + return await generateHashFromBuffer(messageBuffer); +} + +export async function generateHashFromBuffer( + content: BufferSource +): Promise { + const hashBuffer = await crypto.subtle.digest("SHA-256", content); + return encodeHex(hashBuffer); +} diff --git a/docker-compose.yml b/docker-compose.yml index 7b7d9d58cf..d5053ecd54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,6 +37,8 @@ services: depends_on: db: condition: service_healthy + volumes: + - worker_logs:/tmp/windmill/logs windmill_worker: image: ${WM_IMAGE} @@ -60,6 +62,7 @@ services: # mount the docker socket to allow to run docker containers from within the workers - /var/run/docker.sock:/var/run/docker.sock - worker_dependency_cache:/tmp/windmill/cache + - worker_logs:/tmp/windmill/logs ## This worker is specialized for "native" jobs. Native jobs run in-process and thus are much more lightweight than other jobs windmill_worker_native: @@ -80,7 +83,8 @@ services: depends_on: db: condition: service_healthy - + volumes: + - worker_logs:/tmp/windmill/logs ## This worker is specialized for reports or scraping jobs. It is assigned the "reports" worker group which has an init script that installs chromium and can be targeted by using the "chromium" worker tag. # windmill_worker_reports: # image: ${WM_IMAGE} @@ -142,4 +146,5 @@ services: volumes: db_data: null worker_dependency_cache: null + worker_logs: null lsp_cache: null diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4501d4a177..301c85f4ee 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.323.6", + "version": "1.327.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.323.6", + "version": "1.327.0", "license": "AGPL-3.0", "dependencies": { "@aws-crypto/sha256-js": "^4.0.0", diff --git a/frontend/package.json b/frontend/package.json index 5d2080ccb8..4ac1bfcc91 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.323.6", + "version": "1.327.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/frontend/src/lib/assets/app.css b/frontend/src/lib/assets/app.css index 8602ed3479..4a58b1c648 100644 --- a/frontend/src/lib/assets/app.css +++ b/frontend/src/lib/assets/app.css @@ -157,4 +157,5 @@ .app-editor-input { @apply rounded-component border border-gray-300 dark:border-gray-500 focus:border-gray-300 focus:dark:border-gray-500 focus:!ring-1 focus:!ring-blue-300; + @apply placeholder:text-gray-400 dark:placeholder:text-gray-600; } diff --git a/frontend/src/lib/autosize.ts b/frontend/src/lib/autosize.ts index 4caf5acf23..16ed752110 100644 --- a/frontend/src/lib/autosize.ts +++ b/frontend/src/lib/autosize.ts @@ -44,7 +44,7 @@ export const action = (node) => { const setHeight = () => { node.style.height = '0px' - node.style.height = Math.max(node.scrollHeight ?? 0, 40) + 2 + 'px' + node.style.height = Math.max(node.scrollHeight ?? 0, 30) + 2 + 'px' } const addStyles = () => { diff --git a/frontend/src/lib/components/AppConnect.svelte b/frontend/src/lib/components/AppConnect.svelte index c4940a88a3..dc127172d6 100644 --- a/frontend/src/lib/components/AppConnect.svelte +++ b/frontend/src/lib/components/AppConnect.svelte @@ -447,9 +447,17 @@

Others

{#if connectsManual && connectsManual?.length < 10} -
+
Resource Types have not been synced with the hub. Go to the admins workspace to sync them - (and add a schedule to do daily). + (and add a schedule to do daily): +

1. Go to the "admins" workspaces: + sync resource types +

+

+ 2: Run the synchronization script: + sync resource types +

{/if} diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index e4ec4165d1..d7be76dab6 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -550,6 +550,7 @@ {#if properties && Object.keys(properties).length > 0}
Now +
diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index b568dd37a1..ae2a9b62bf 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -159,6 +159,8 @@ path: string language: Preview['language'] lock?: string + isCodebase?: boolean + tag?: string } let currentScript: LastEditScript | undefined = undefined @@ -177,13 +179,28 @@ let lockChanges = false let timeout: NodeJS.Timeout | undefined = undefined + let loadingCodebaseButton = false + let lastBundleCommandId = '' + const el = (event) => { // sendUserToast(`Received message from parent ${event.data.type}`, true) if (event.data.type == 'runTest') { runTest() + event.preventDefault() } else if (event.data.type == 'replaceScript') { mode = 'script' replaceScript(event.data) + } else if (event.data.type == 'testBundle') { + if (event.data.id == lastBundleCommandId) { + testBundle(event.data.file) + } else { + sendUserToast(`Bundle received ${lastBundleCommandId} was obsolete, ignoring`, true) + } + } else if (event.data.type == 'testBundleError') { + sendUserToast( + typeof event.data.error == 'object' ? JSON.stringify(event.data.error) : event.data.error, + true + ) } else if (event.data.type == 'replaceFlow') { mode = 'flow' lockChanges = true @@ -223,6 +240,47 @@ window.parent?.postMessage({ type: 'refresh' }, '*') }) + async function testBundle(file: string) { + testJobLoader?.abstractRun(async () => { + try { + const form = new FormData() + form.append( + 'preview', + JSON.stringify({ + content: currentScript?.content, + kind: 'bundle', + path: currentScript?.path, + args, + language: currentScript?.language, + tag: currentScript?.tag + }) + ) + form.append('file', file) + + const url = '/api/w/' + workspace + '/jobs/run/preview_bundle' + + const req = await fetch(url, { + method: 'POST', + body: form, + headers: { + Authorization: 'Bearer ' + token + } + }) + if (req.status != 201) { + throw Error( + `Script snapshot creation was not successful: ${req.status} - ${ + req.statusText + } - ${await req.text()}` + ) + } + return await req.text() + } catch (e) { + sendUserToast(`Failed to send bundle ${e}`, true) + throw Error(e) + } + }) + loadingCodebaseButton = false + } onDestroy(() => { window.removeEventListener('message', el) if (socket && socket.readyState === WebSocket.OPEN) { @@ -261,15 +319,21 @@ if (!currentScript) { return } - //@ts-ignore - testJobLoader.runPreview( - currentScript.path, - currentScript.content, - currentScript.language, - args, - undefined, - useLock ? currentScript.lock : undefined - ) + if (currentScript.isCodebase) { + loadingCodebaseButton = true + lastBundleCommandId = Math.random().toString(36).substring(7) + window.parent?.postMessage({ type: 'testBundle', id: lastBundleCommandId }, '*') + } else { + //@ts-ignore + testJobLoader.runPreview( + currentScript.path, + currentScript.content, + currentScript.language, + args, + currentScript.tag, + useLock ? currentScript.lock : undefined + ) + } } else { flowPreviewButtons?.openPreview() } @@ -288,10 +352,11 @@ }) } - function onKeyDown(event: KeyboardEvent) { - if ((event.ctrlKey || event.metaKey) && event.key == 'Enter') { - event.preventDefault() - runTest() + async function onKeyDown(event: KeyboardEvent) { + if ((event.ctrlKey || event.metaKey) && event.code === 'KeyC') { + document.execCommand('copy') + } else if ((event.ctrlKey || event.metaKey) && event.code === 'KeyX') { + document.execCommand('cut') } } @@ -445,7 +510,9 @@
{currentScript?.path ?? 'Not editing a script'} - {currentScript?.language ?? ''} + {currentScript?.language ?? ''}{currentScript?.isCodebase ? ' (codebase)' : ''}
@@ -482,7 +549,7 @@ {/if}
@@ -598,7 +604,7 @@ {#if !disableExpand && !noControls} - + + {/if} {:else} diff --git a/frontend/src/lib/components/FlowMetadata.svelte b/frontend/src/lib/components/FlowMetadata.svelte index b6810b335c..1587acbc37 100644 --- a/frontend/src/lib/components/FlowMetadata.svelte +++ b/frontend/src/lib/components/FlowMetadata.svelte @@ -11,11 +11,9 @@ export let job: Job const SMALL_ICON_SIZE = 14 - let scheduleEditor: ScheduleEditor + export let scheduleEditor: ScheduleEditor - -
+
{#if isScript} {:else} {/if} - + {isScript ? job?.script_hash : job?.script_path}
{/if} -
+
diff --git a/frontend/src/lib/components/JobStatus.svelte b/frontend/src/lib/components/JobStatus.svelte index 601e559afe..d6525e721b 100644 --- a/frontend/src/lib/components/JobStatus.svelte +++ b/frontend/src/lib/components/JobStatus.svelte @@ -41,7 +41,7 @@
{:else if job && 'running' in job}
- Queued + Queued
{:else} diff --git a/frontend/src/lib/components/LightweightArgInput.svelte b/frontend/src/lib/components/LightweightArgInput.svelte index 8250b357ec..b3899861a1 100644 --- a/frontend/src/lib/components/LightweightArgInput.svelte +++ b/frontend/src/lib/components/LightweightArgInput.svelte @@ -49,6 +49,7 @@ export let customErrorMessage: string | undefined = undefined export let hideResourceInput: boolean = false export let resourceInputUnsupported: boolean = false + export let render = true const dispatch = createEventDispatcher() @@ -60,7 +61,7 @@ $: inputCat = computeInputCat(type, format, itemsType?.type, enum_, contentEncoding) - $: changeDefaultValue(inputCat, defaultValue) + $: render && changeDefaultValue(inputCat, defaultValue) $: rawValue && evalRawValueToValue() @@ -111,6 +112,10 @@ } function validateInput(pattern: string | undefined, v: any, required: boolean): void { + if (!render) { + error = '' + value = true + } if (required && (v == undefined || v == null || v === '')) { error = 'Required' valid && (valid = false) @@ -158,304 +163,320 @@ evalValueToRaw() } } + + $: render == false && onRenderFalse() + + function onRenderFalse() { + value = undefined + valid = true + error = '' + } -{#if !(hideResourceInput && inputCat === 'resource-object')} -
-
- {#if displayHeader} - - {/if} - - {#if description} -
-
{description}
-
- {/if} - -
- {#if inputCat == 'number'} - {#if extra['min'] != undefined && extra['max'] != undefined} -
- {extra['min']} -
- -
- {extra['max']} - {value} -
- {:else if extra?.currency} - - {:else} - { - dispatch('focus') - }} - type="number" - class={twMerge( - valid && error == '' - ? '' - : 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30' - )} - placeholder={defaultValue ?? ''} - bind:value - min={extra['min']} - max={extra['max']} - /> - {/if} - {:else if inputCat == 'boolean'} - { - e?.stopPropagation() - }} - class={valid && error == '' - ? '' - : 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30'} - bind:checked={value} +{#if render} + {#if !(hideResourceInput && inputCat === 'resource-object')} +
+
+ {#if displayHeader} + - {#if type == 'boolean' && value == undefined} -   Not set - {/if} - {:else if inputCat == 'list'} -
- {#if Array.isArray(itemsType?.multiselect) && Array.isArray(value)} -
- + {/if} + + {#if description} +
+
{description}
+
+ {/if} + +
+ {#if inputCat == 'number'} + {#if extra['min'] != undefined && extra['max'] != undefined} +
+ {extra['min']} +
+ +
+ {extra['max']} + {value}
- {:else if Array.isArray(itemsType?.enum) && Array.isArray(value)} -
- -
- {:else if Array.isArray(enum_) && Array.isArray(value)} -
- + {:else} + { + dispatch('focus') + }} + type="number" + class={twMerge( + valid && error == '' + ? '' + : 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30' + )} + placeholder={defaultValue ?? ''} + bind:value + min={extra['min']} + max={extra['max']} + /> + {/if} + {:else if inputCat == 'boolean'} + { + e?.stopPropagation() + }} + class={valid && error == '' + ? '' + : 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30'} + bind:checked={value} + /> + {#if type == 'boolean' && value == undefined} +   Not set + {/if} + {:else if inputCat == 'list'} +
+ {#if Array.isArray(itemsType?.multiselect) && Array.isArray(value)} +
+ +
+ {:else if Array.isArray(itemsType?.enum) && Array.isArray(value)} +
+ +
+ {:else if Array.isArray(enum_) && Array.isArray(value)} +
+ +
+ {:else} +
+ {#if Array.isArray(value)} + {#each value ?? [] as v, i} +
+ {#if itemsType?.type == 'number'} + + {:else if itemsType?.type == 'string' && itemsType?.contentEncoding == 'base64'} + fileChanged(x, (val) => (value[i] = val))} + multiple={false} + /> + {:else if Array.isArray(itemsType?.enum)} + + {:else} + + {/if} + +
+ {/each} + {:else if value != undefined} + List is not an array + {/if} +
+
+ +
+ + {(value ?? []).length} item{(value ?? []).length != 1 ? 's' : ''} + + {/if} +
+ {:else if inputCat == 'resource-object'} + + {:else if inputCat == 'object'} + {#if properties && Object.keys(properties).length > 0} +
+
{:else} -
- {#if Array.isArray(value)} - {#each value ?? [] as v, i} -
- {#if itemsType?.type == 'number'} - - {:else if itemsType?.type == 'string' && itemsType?.contentEncoding == 'base64'} - fileChanged(x, (val) => (value[i] = val))} - multiple={false} - /> - {:else if Array.isArray(itemsType?.enum)} - - {:else} - - {/if} - -
- {/each} - {:else if value != undefined} - List is not an array - {/if} -
-
- -
- - {(value ?? []).length} item{(value ?? []).length != 1 ? 's' : ''} - - {/if} -
- {:else if inputCat == 'resource-object'} - - {:else if inputCat == 'object'} - {#if properties && Object.keys(properties).length > 0} -
- { + dispatch('focus') + }} + use:autosize + style="min-height: 5px;" + class="col-span-10 {valid && error == '' + ? '' + : 'border !border-red-700 !border-opacity-70 focus:!border-red-700 focus:!border-opacity-30'}" + placeholder={defaultValue ? JSON.stringify(defaultValue, null, 4) : ''} + bind:value={rawValue} /> -
- {:else} -