feat: generate and fix scripts using Autopilot powered by OpenAI #1827)

* feat(frontend): generate simple script from prompt

* feat(backend): add OpenAI key workspace settings

* feat(frontend): add OpenAI key workplace settings

* feat: proxy to backend + support all languages

* fix: openapi spec

* feat: ScriptGen new UI + gpt 4

* fix: openai key sqlx data

* fix: diffEditor optional

* feat: beta + minor improvements

* feat: resource type support

* feat: edit with AI

* feat: AI fix

* feat: minor improvements
content max length, format, env info

* fix: minor
This commit is contained in:
HugoCasa
2023-07-12 17:07:45 +02:00
committed by GitHub
parent f5c8dd02b5
commit 1f86d79137
21 changed files with 1651 additions and 72 deletions
@@ -0,0 +1,2 @@
-- Add down migration script here
ALTER TABLE workspace_settings DROP COLUMN openai_key;
@@ -0,0 +1,2 @@
-- Add up migration script here
ALTER TABLE workspace_settings ADD COLUMN openai_key VARCHAR(255);
+65 -4
View File
@@ -734,6 +734,11 @@
"name": "error_handler",
"ordinal": 11,
"type_info": "Varchar"
},
{
"name": "openai_key",
"ordinal": 12,
"type_info": "Varchar"
}
],
"nullable": [
@@ -748,6 +753,7 @@
true,
true,
true,
true,
true
],
"parameters": {
@@ -1257,7 +1263,8 @@
"bash",
"postgresql",
"nativets",
"bun"
"bun",
"mysql"
]
},
"name": "script_lang"
@@ -2449,6 +2456,11 @@
"name": "error_handler",
"ordinal": 11,
"type_info": "Varchar"
},
{
"name": "openai_key",
"ordinal": 12,
"type_info": "Varchar"
}
],
"nullable": [
@@ -2463,6 +2475,7 @@
true,
true,
true,
true,
true
],
"parameters": {
@@ -2550,7 +2563,8 @@
"bash",
"postgresql",
"nativets",
"bun"
"bun",
"mysql"
]
},
"name": "script_lang"
@@ -2616,6 +2630,18 @@
},
"query": "UPDATE schedule SET error = NULL WHERE workspace_id = $1 AND path = $2"
},
"57ab0eabaaef495e12c4f04cb8e3073772f611283c0a424fd8c8f1368742d310": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "UPDATE workspace_settings SET openai_key = NULL WHERE workspace_id = $1"
},
"5850e0c1f7dee98455027a4c0db6ccea0af2c58aee656ef43964b515de054888": {
"describe": {
"columns": [
@@ -2794,7 +2820,8 @@
"bash",
"postgresql",
"nativets",
"bun"
"bun",
"mysql"
]
},
"name": "script_lang"
@@ -3832,6 +3859,26 @@
},
"query": "DELETE FROM resource WHERE resource_type = 'cache' AND to_timestamp((value->>'expire')::int) < now() RETURNING path"
},
"87874d29c44f03ff4d7d9cb9dc92900fb5eb6ce38408fe7f8e0f3455c77c3590": {
"describe": {
"columns": [
{
"name": "openai_key",
"ordinal": 0,
"type_info": "Varchar"
}
],
"nullable": [
true
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "SELECT openai_key FROM workspace_settings WHERE workspace_id = $1"
},
"8876fa929ffb175cd976a2bca1195704aa9fe7215013ae29e49ef15cb201ba57": {
"describe": {
"columns": [],
@@ -3895,7 +3942,8 @@
"bash",
"postgresql",
"nativets",
"bun"
"bun",
"mysql"
]
},
"name": "script_lang"
@@ -6166,6 +6214,19 @@
},
"query": "SELECT name, (EXISTS (SELECT 1 FROM (SELECT key, value FROM jsonb_each_text(extra_perms) WHERE key = ANY($1)) t WHERE value::boolean IS true)) as write, $1 && owners::text[] as owner FROM folder\n WHERE extra_perms ?| $1 AND workspace_id = $2"
},
"d2fefff37e6e0d1c8061f3215cb740b9af6798bf1605e79be0786b1f08b55f02": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
}
},
"query": "UPDATE workspace_settings SET openai_key = $1 WHERE workspace_id = $2"
},
"d4bf68b4c2bfd11db84787e41e92c13612fda7352e841f3cdef45ea762588f04": {
"describe": {
"columns": [],
+45
View File
@@ -971,6 +971,8 @@ paths:
type: string
deploy_to:
type: string
openai_key:
type: string
error_handler:
type: string
@@ -1124,6 +1126,49 @@ paths:
text/plain:
schema:
type: string
/w/{workspace}/workspaces/edit_openai_key:
post:
summary: edit OpenAI key
operationId: editOpenaiKey
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: WorkspaceOpenAIKey
required: true
content:
application/json:
schema:
type: object
properties:
openai_key:
type: string
responses:
"200":
description: status
content:
text/plain:
schema:
type: string
/w/{workspace}/workspaces/exists_openai_key:
get:
summary: OpenAI key exists
operationId: existsOpenaiKey
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
responses:
"200":
description: status
content:
text/plain:
schema:
type: boolean
/w/{workspace}/workspaces/edit_error_handler:
post:
+3 -1
View File
@@ -46,6 +46,7 @@ mod groups;
mod inputs;
pub mod jobs;
mod oauth2;
mod openai;
mod raw_apps;
mod resources;
mod schedule;
@@ -199,7 +200,8 @@ pub async fn run_server(
users::workspaced_service().layer(Extension(argon2.clone())),
)
.nest("/variables", variables::workspaced_service())
.nest("/workspaces", workspaces::workspaced_service()),
.nest("/workspaces", workspaces::workspaced_service())
.nest("/openai", openai::workspaced_service()),
)
.nest("/workspaces", workspaces::global_service())
.nest(
+82
View File
@@ -0,0 +1,82 @@
use crate::{db::DB, users::Authed, HTTP_CLIENT};
use axum::{
body::{Bytes, StreamBody},
extract::{Extension, Path},
http::HeaderMap,
response::IntoResponse,
routing::post,
Router,
};
use windmill_audit::{audit_log, ActionKind};
use windmill_common::error::{to_anyhow, Error};
pub fn workspaced_service() -> Router {
let router = Router::new().route("/proxy/*openai_path", post(proxy));
router
}
struct OpenAIKey {
openai_key: Option<String>,
}
async fn proxy(
authed: Authed,
Extension(db): Extension<DB>,
Path((w_id, openai_path)): Path<(String, String)>,
body: Bytes,
) -> impl IntoResponse {
tracing::info!("reached openai endpoint");
let mut tx = db.begin().await?;
let settings = sqlx::query_as!(
OpenAIKey,
"SELECT openai_key FROM workspace_settings WHERE workspace_id = $1",
&w_id
)
.fetch_one(&mut tx)
.await
.map_err(|e| Error::InternalErr(format!("getting openai_key: {e}")))?;
tx.commit().await?;
let openai_key = match settings.openai_key {
Some(key) => key,
None => {
return Err(Error::BadRequest(
"openai_key is not set for this workspace".to_string(),
))
}
};
let resp = HTTP_CLIENT
.post(String::from("https://api.openai.com/v1/") + &openai_path)
.header("content-type", "application/json")
.header("authorization", format!("Bearer {}", openai_key))
.body(body)
.send()
.await
.map_err(to_anyhow)?;
let mut tx = db.begin().await?;
audit_log(
&mut tx,
&authed.username,
"openai.request",
ActionKind::Update,
&w_id,
Some(&authed.email),
Some([("openai_path", &format!("{:?}", openai_path)[..])].into()),
)
.await?;
tx.commit().await?;
let mut headers = HeaderMap::new();
for (k, v) in resp.headers().iter() {
headers.insert(k, v.clone());
}
let stream = resp.bytes_stream();
Ok((headers, StreamBody::new(stream)))
}
+73
View File
@@ -66,6 +66,8 @@ pub fn workspaced_service() -> Router {
.route("/edit_deploy_to", post(edit_deploy_to))
.route("/tarball", get(tarball_workspace))
.route("/premium_info", get(premium_info))
.route("/edit_openai_key", post(edit_openai_key))
.route("/exists_openai_key", get(exists_openai_key) )
.route("/edit_error_handler", post(edit_error_handler));
#[cfg(feature = "enterprise")]
@@ -113,6 +115,7 @@ pub struct WorkspaceSettings {
pub plan: Option<String>,
pub webhook: Option<String>,
pub deploy_to: Option<String>,
pub openai_key: Option<String>,
pub error_handler: Option<String>,
}
@@ -153,6 +156,11 @@ struct EditWebhook {
webhook: Option<String>,
}
#[derive(Deserialize)]
struct EditOpenAIKey {
openai_key: Option<String>,
}
#[derive(Deserialize)]
struct CreateWorkspace {
id: String,
@@ -652,6 +660,71 @@ async fn edit_webhook(
Ok(format!("Edit webhook for workspace {}", &w_id))
}
async fn edit_openai_key(
authed: Authed,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Authed { is_admin, username, .. }: Authed,
Json(eo): Json<EditOpenAIKey>,
) -> Result<String> {
require_admin(is_admin, &username)?;
let mut tx = db.begin().await?;
if let Some(openai_key) = &eo.openai_key {
sqlx::query!(
"UPDATE workspace_settings SET openai_key = $1 WHERE workspace_id = $2",
openai_key,
&w_id
)
.execute(&mut tx)
.await?;
} else {
sqlx::query!(
"UPDATE workspace_settings SET openai_key = NULL WHERE workspace_id = $1",
&w_id,
)
.execute(&mut tx)
.await?;
}
audit_log(
&mut tx,
&authed.username,
"workspaces.edit_openai_key",
ActionKind::Update,
&w_id,
Some(&authed.email),
Some([("openai_key", &format!("{:?}", eo.openai_key)[..])].into()),
)
.await?;
tx.commit().await?;
Ok(format!("Edit openai_key for workspace {}", &w_id))
}
async fn exists_openai_key(
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Authed { is_admin, username, .. }: Authed,
) -> JsonResult<bool> {
require_admin(is_admin, &username)?;
let mut tx = db.begin().await?;
let openai_key = sqlx::query_scalar!(
"SELECT openai_key FROM workspace_settings WHERE workspace_id = $1",
&w_id
)
.fetch_one(&mut tx)
.await
.map_err(|e| Error::InternalErr(format!("getting openai_key: {e}")))?;
tx.commit().await?;
let exists = openai_key.is_some();
Ok(Json(exists))
}
async fn edit_error_handler(
authed: Authed,
+509 -64
View File
@@ -30,6 +30,7 @@
"lodash": "^4.17.21",
"lucide-svelte": "^0.246.0",
"monaco-languageclient": "~6.0.3",
"openai": "^4.0.0-beta.4",
"quill": "^1.3.7",
"svelte-autosize": "^1.0.1",
"svelte-chartjs": "^3.1.0",
@@ -663,6 +664,33 @@
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"optional": true,
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"optional": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
@@ -1218,8 +1246,16 @@
"node_modules/@types/node": {
"version": "20.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
"integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
"dev": true
"integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw=="
},
"node_modules/@types/node-fetch": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
"integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
"dependencies": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
@@ -1234,6 +1270,11 @@
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
},
"node_modules/@types/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
@@ -1628,6 +1669,17 @@
"dev": true,
"optional": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/abstract-leveldown": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
@@ -1706,17 +1758,17 @@
"ag-grid-community": "^28"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"optional": true,
"node_modules/agentkeepalive": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz",
"integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==",
"dependencies": {
"debug": "4"
"debug": "^4.1.0",
"depd": "^2.0.0",
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 6.0.0"
"node": ">= 8.0.0"
}
},
"node_modules/ajv": {
@@ -1854,6 +1906,11 @@
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"optional": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -1897,6 +1954,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -2152,6 +2214,14 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
"engines": {
"node": "*"
}
},
"node_modules/chart.js": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz",
@@ -2262,6 +2332,17 @@
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
@@ -2331,6 +2412,14 @@
"node": ">= 8"
}
},
"node_modules/crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
"engines": {
"node": "*"
}
},
"node_modules/css-declaration-sorter": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
@@ -2659,7 +2748,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2787,6 +2875,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -2794,6 +2890,14 @@
"dev": true,
"optional": true
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@@ -2833,6 +2937,15 @@
"node": ">=0.3.1"
}
},
"node_modules/digest-fetch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz",
"integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==",
"dependencies": {
"base-64": "^0.1.0",
"md5": "^2.3.0"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -3280,6 +3393,14 @@
"node": ">=0.10.0"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
@@ -3430,6 +3551,36 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
"node_modules/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data-encoder": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
},
"node_modules/formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
"dependencies": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.3"
},
"engines": {
"node": ">= 12.20"
}
},
"node_modules/fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -3861,18 +4012,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"optional": true,
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
"ms": "^2.0.0"
}
},
"node_modules/ieee754": {
@@ -4027,6 +4172,11 @@
"node": ">=8"
}
},
"node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"node_modules/is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
@@ -4659,6 +4809,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
"dependencies": {
"charenc": "0.0.2",
"crypt": "0.0.2",
"is-buffer": "~1.1.6"
}
},
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -4739,6 +4899,25 @@
"node": ">=10.0.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
@@ -4927,8 +5106,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mz": {
"version": "2.7.0",
@@ -5000,12 +5178,28 @@
"tslib": "^2.0.3"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dev": true,
"optional": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -5131,6 +5325,14 @@
"node": ">= 6"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
@@ -5191,6 +5393,28 @@
"wrappy": "1"
}
},
"node_modules/openai": {
"version": "4.0.0-beta.4",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.0.0-beta.4.tgz",
"integrity": "sha512-8U1E4IHxemQE/VePwOpd4kaq0/vRTK9GJpHWrF6KQ3GEshWkUN///gCA+PtVFyf+irZrDGqjBAb3C02zn0UZBg==",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"@types/qs": "^6.9.7",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"qs": "^6.10.3"
}
},
"node_modules/openai/node_modules/@types/node": {
"version": "18.16.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.19.tgz",
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA=="
},
"node_modules/openapi-typescript-codegen": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.24.0.tgz",
@@ -6119,6 +6343,20 @@
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.11.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/quadprog": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/quadprog/-/quadprog-1.6.1.tgz",
@@ -6630,6 +6868,19 @@
"node": ">=8"
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -7606,9 +7857,7 @@
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true,
"optional": true
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/trim-newlines": {
"version": "3.0.1",
@@ -7937,6 +8186,14 @@
"npm": ">=8.0.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
"engines": {
"node": ">= 14"
}
},
"node_modules/web-worker": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
@@ -7946,16 +8203,12 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true,
"optional": true
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dev": true,
"optional": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
@@ -8572,6 +8825,29 @@
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"dependencies": {
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"optional": true,
"requires": {
"debug": "4"
}
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"optional": true,
"requires": {
"agent-base": "6",
"debug": "4"
}
}
}
},
"@mapbox/point-geometry": {
@@ -9043,8 +9319,16 @@
"@types/node": {
"version": "20.3.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz",
"integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==",
"dev": true
"integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw=="
},
"@types/node-fetch": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
"integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"@types/normalize-package-data": {
"version": "2.4.1",
@@ -9059,6 +9343,11 @@
"integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==",
"dev": true
},
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
},
"@types/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
@@ -9288,6 +9577,14 @@
"dev": true,
"optional": true
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"abstract-leveldown": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
@@ -9340,14 +9637,14 @@
"svelte": "^3.55.0"
}
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"optional": true,
"agentkeepalive": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz",
"integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==",
"requires": {
"debug": "4"
"debug": "^4.1.0",
"depd": "^2.0.0",
"humanize-ms": "^1.2.1"
}
},
"ajv": {
@@ -9454,6 +9751,11 @@
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"optional": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -9478,6 +9780,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base-64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -9642,6 +9949,11 @@
"supports-color": "^7.1.0"
}
},
"charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
},
"chart.js": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz",
@@ -9720,6 +10032,14 @@
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
@@ -9774,6 +10094,11 @@
"which": "^2.0.1"
}
},
"crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
},
"css-declaration-sorter": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz",
@@ -10009,7 +10334,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -10101,6 +10425,11 @@
"object-keys": "^1.1.1"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -10108,6 +10437,11 @@
"dev": true,
"optional": true
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@@ -10138,6 +10472,15 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
"integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw=="
},
"digest-fetch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz",
"integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==",
"requires": {
"base-64": "^0.1.0",
"md5": "^2.3.0"
}
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -10477,6 +10820,11 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
@@ -10603,6 +10951,30 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"form-data-encoder": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
},
"formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
"requires": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.3"
}
},
"fraction.js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -10920,15 +11292,12 @@
"dev": true,
"peer": true
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"optional": true,
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"requires": {
"agent-base": "6",
"debug": "4"
"ms": "^2.0.0"
}
},
"ieee754": {
@@ -11039,6 +11408,11 @@
"binary-extensions": "^2.0.0"
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
@@ -11509,6 +11883,16 @@
"dev": true,
"peer": true
},
"md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
"integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
"requires": {
"charenc": "0.0.2",
"crypt": "0.0.2",
"is-buffer": "~1.1.6"
}
},
"mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
@@ -11567,6 +11951,19 @@
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
"dev": true
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
@@ -11700,8 +12097,7 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mz": {
"version": "2.7.0",
@@ -11761,12 +12157,15 @@
"tslib": "^2.0.3"
}
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dev": true,
"optional": true,
"requires": {
"whatwg-url": "^5.0.0"
}
@@ -11852,6 +12251,11 @@
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true
},
"object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
},
"object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
@@ -11899,6 +12303,30 @@
"wrappy": "1"
}
},
"openai": {
"version": "4.0.0-beta.4",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.0.0-beta.4.tgz",
"integrity": "sha512-8U1E4IHxemQE/VePwOpd4kaq0/vRTK9GJpHWrF6KQ3GEshWkUN///gCA+PtVFyf+irZrDGqjBAb3C02zn0UZBg==",
"requires": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"@types/qs": "^6.9.7",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"qs": "^6.10.3"
},
"dependencies": {
"@types/node": {
"version": "18.16.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.19.tgz",
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA=="
}
}
},
"openapi-typescript-codegen": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.24.0.tgz",
@@ -12506,6 +12934,14 @@
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true
},
"qs": {
"version": "6.11.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
"requires": {
"side-channel": "^1.0.4"
}
},
"quadprog": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/quadprog/-/quadprog-1.6.1.tgz",
@@ -12874,6 +13310,16 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"requires": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
}
},
"signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -13605,9 +14051,7 @@
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true,
"optional": true
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"trim-newlines": {
"version": "3.0.1",
@@ -13822,6 +14266,11 @@
"vscode-jsonrpc": "~8.1.0"
}
},
"web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="
},
"web-worker": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz",
@@ -13831,16 +14280,12 @@
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true,
"optional": true
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dev": true,
"optional": true,
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
+1
View File
@@ -92,6 +92,7 @@
"lodash": "^4.17.21",
"lucide-svelte": "^0.246.0",
"monaco-languageclient": "~6.0.3",
"openai": "^4.0.0-beta.4",
"quill": "^1.3.7",
"svelte-autosize": "^1.0.1",
"svelte-chartjs": "^3.1.0",
@@ -0,0 +1,97 @@
<script lang="ts">
import { BROWSER } from 'esm-env'
import { onMount } from 'svelte'
import 'monaco-editor/esm/vs/editor/edcore.main'
import { editor as meditor } from 'monaco-editor/esm/vs/editor/editor.api'
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'
import 'monaco-editor/esm/vs/basic-languages/go/go.contribution'
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution'
import { Breakpoints } from './apps/gridUtils'
meditor.defineTheme('myTheme', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editorLineNumber.foreground': '#999',
'editorGutter.background': '#F9FAFB'
}
})
meditor.setTheme('myTheme')
export let automaticLayout = true
export let fixedOverflowWidgets = true
export let sideBySideMinWidth = Breakpoints.lg
let diffEditor: meditor.IStandaloneDiffEditor
let diffDivEl: HTMLDivElement | null = null
function loadDiffEditor() {
diffEditor = meditor.createDiffEditor(diffDivEl!, {
automaticLayout,
renderSideBySide: window.innerWidth >= sideBySideMinWidth,
domReadOnly: true,
readOnly: true,
minimap: {
enabled: false
},
fixedOverflowWidgets,
scrollBeyondLastLine: false,
lineDecorationsWidth: 15,
lineNumbersMinChars: 2,
autoDetectHighContrast: true,
scrollbar: { alwaysConsumeMouseWheel: false }
})
}
export function setDiff(
original: string,
modified: string,
lang: 'typescript' | 'python' | 'go' | 'shell' | 'sql' | 'graphql'
): void {
diffEditor.setModel({
original: meditor.createModel(original, lang),
modified: meditor.createModel(modified, lang)
})
if (lang !== 'shell') {
diffEditor?.getModifiedEditor().getAction('editor.action.formatDocument')?.run()
}
}
export function getOriginal(): string {
return diffEditor.getModel()?.original.getValue() ?? ''
}
export function getModified(): string {
return diffEditor.getModel()?.modified.getValue() ?? ''
}
export function show(): void {
diffDivEl?.classList.remove('hidden')
}
export function hide(): void {
diffDivEl?.classList.add('hidden')
}
function onResize() {
diffEditor.updateOptions({ renderSideBySide: window.innerWidth >= sideBySideMinWidth })
}
onMount(() => {
if (BROWSER) {
loadDiffEditor()
window.addEventListener('resize', onResize)
return () => {
diffEditor?.dispose()
window.removeEventListener('resize', onResize)
}
}
})
</script>
<div bind:this={diffDivEl} class="{$$props.class} editor" />
@@ -7,6 +7,10 @@
import { ClipboardCopy, Download, Expand } from 'lucide-svelte'
import Portal from 'svelte-portal'
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
import ScriptFix from './codeGen/ScriptFix.svelte'
import type { Preview } from '$lib/gen'
import type Editor from './Editor.svelte'
import type DiffEditor from './DiffEditor.svelte'
export let result: any
export let requireHtmlApproval = false
@@ -14,6 +18,9 @@
export let disableExpand = false
export let jobId: string | undefined = undefined
export let workspaceId: string | undefined = undefined
export let editor: Editor | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
export let lang: Preview.language | undefined = undefined
let resultKind:
| 'json'
@@ -223,12 +230,15 @@
href="data:application/octet-stream;base64,{result.file}">Download</a
>
</div>
{:else if !forceJson && resultKind == 'error'}<div>
{:else if !forceJson && resultKind == 'error'}<div class="flex flex-col items-start">
<span class="text-red-500 font-semibold text-sm whitespace-pre-wrap"
>{#if result.error.name || result.error.message}{result.error.name}: {result.error
.message}{:else}{JSON.stringify(result.error, null, 4)}{/if}</span
>
<pre class="text-sm whitespace-pre-wrap text-gray-900">{result.error.stack ?? ''}</pre>
{#if lang && editor && diffEditor}
<ScriptFix error={JSON.stringify(result.error)} {lang} {editor} {diffEditor} />
{/if}
</div>
{:else if !forceJson && resultKind == 'approval'}<div class="flex flex-col gap-3 mt-8 mx-4">
<Button
+31 -1
View File
@@ -13,7 +13,8 @@
KeyCode,
KeyMod,
Uri as mUri,
languages
languages,
type IRange
} from 'monaco-editor/esm/vs/editor/editor.api'
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'
import 'monaco-editor/esm/vs/basic-languages/go/go.contribution'
@@ -145,6 +146,35 @@
}
}
export function getSelectedLines(): string | undefined {
if (editor) {
const selection = editor.getSelection()
if (selection) {
const range: IRange = {
startLineNumber: selection.startLineNumber,
startColumn: 1,
endLineNumber: selection.endLineNumber + 1,
endColumn: 1
}
return editor.getModel()?.getValueInRange(range)
}
}
}
export function onDidChangeCursorSelection(f: (e: meditor.ICursorSelectionChangedEvent) => void) {
if (editor) {
return editor.onDidChangeCursorSelection(f)
}
}
export function show(): void {
divEl?.classList.remove('hidden')
}
export function hide(): void {
divEl?.classList.add('hidden')
}
export function setCode(ncode: string, noHistory: boolean = false): void {
code = ncode
if (noHistory) {
@@ -37,6 +37,8 @@
import { capitalize } from '$lib/utils'
import type { Schema, SchemaProperty, SupportedLanguage } from '$lib/common'
import ScriptVersionHistory from './ScriptVersionHistory.svelte'
import { ScriptGen } from './codeGen'
import type DiffEditor from './DiffEditor.svelte'
export let lang: SupportedLanguage
export let editor: Editor | undefined
@@ -55,6 +57,7 @@
export let collabLive = false
export let collabUsers: { name: string }[] = []
export let scriptPath: string | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
let contextualVariablePicker: ItemPicker
let variablePicker: ItemPicker
@@ -462,6 +465,8 @@
</div>
{/if}
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} />
<!-- <Popover
notClickable
placement="bottom"
@@ -24,6 +24,7 @@
import { WebsocketProvider } from 'y-websocket'
import Modal from './common/modal/Modal.svelte'
import { Icon } from 'svelte-awesome'
import DiffEditor from './DiffEditor.svelte'
// Exported
export let schema: Schema | any = emptySchema()
@@ -36,6 +37,7 @@
export let fixedOverflowWidgets = true
export let noSyncFromGithub = false
export let editor: Editor | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
export let collabMode = false
export let edit = true
@@ -242,6 +244,7 @@
{websocketAlive}
collabUsers={peers}
kind={asKind(kind)}
{diffEditor}
/>
{#if !noSyncFromGithub}
<div class="py-1">
@@ -296,6 +299,12 @@
automaticLayout={true}
{fixedOverflowWidgets}
/>
<DiffEditor
bind:this={diffEditor}
automaticLayout
{fixedOverflowWidgets}
class="hidden flex-1 h-full !overflow-visible"
/>
{/key}
</div>
</Pane>
@@ -344,7 +353,14 @@
</div>
</Pane>
<Pane size={67}>
<LogPanel {lang} previewJob={testJob} {pastPreviews} previewIsLoading={testIsLoading} />
<LogPanel
{lang}
previewJob={testJob}
{pastPreviews}
previewIsLoading={testIsLoading}
{editor}
{diffEditor}
/>
</Pane>
</Splitpanes>
</div>
@@ -0,0 +1,131 @@
<script lang="ts">
import { Button } from '../common'
import { SUPPORTED_LANGUAGES, fixScript } from './lib'
import type { SupportedLanguage } from '$lib/common'
import { sendUserToast } from '$lib/toast'
import type Editor from '../Editor.svelte'
import { faCheck, faClose, faMagicWandSparkles } from '@fortawesome/free-solid-svg-icons'
import { WorkspaceService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import type DiffEditor from '../DiffEditor.svelte'
import { scriptLangToEditorLang } from '$lib/scripts'
// props
export let lang: SupportedLanguage
export let editor: Editor | undefined
export let diffEditor: DiffEditor | undefined
export let error: string
// state
let genLoading: boolean = false
let openAIAvailable: boolean | undefined = undefined
let generatedCode = ''
async function onFix() {
if (!error) {
return
}
try {
// close popup ^^
const elem = document.activeElement as HTMLElement
if (elem.blur) {
elem.blur()
}
genLoading = true
generatedCode = await fixScript({
language: lang,
code: editor?.getCode() || '',
error
})
} catch (err) {
sendUserToast('Failed to generate code', true)
console.error(err)
} finally {
genLoading = false
}
}
function acceptDiff() {
editor?.setCode(diffEditor?.getModified() || '')
editor?.format()
generatedCode = ''
error = ''
}
function rejectDiff() {
generatedCode = ''
}
async function checkIfOpenAIAvailable(lang: SupportedLanguage) {
try {
const exists = await WorkspaceService.existsOpenaiKey({ workspace: $workspaceStore! })
openAIAvailable = exists && SUPPORTED_LANGUAGES.has(lang)
} catch (err) {
console.error(err)
sendUserToast('Failed to check if OpenAI is available', true)
}
}
function showDiff() {
diffEditor?.setDiff(editor?.getCode() || '', generatedCode, scriptLangToEditorLang(lang))
diffEditor?.show()
editor?.hide()
}
function hideDiff() {
editor?.show()
diffEditor?.hide()
}
$: checkIfOpenAIAvailable(lang)
$: lang && (generatedCode = '')
$: generatedCode && showDiff()
$: !generatedCode && hideDiff()
</script>
{#if error}
{#if openAIAvailable}
<div class="mt-2">
{#if generatedCode}
<div class="flex gap-1">
<Button
title="Discard generated code"
size="xs"
color="red"
spacingSize="xs2"
on:click={rejectDiff}
variant="contained"
startIcon={{ icon: faClose }}
>
Discard
</Button><Button
title="Accept generated code"
size="xs"
color="green"
spacingSize="xs2"
on:click={acceptDiff}
startIcon={{ icon: faCheck }}
>
Accept
</Button>
</div>
{:else}
<Button
title="Generate code from prompt"
size="xs"
color="blue"
spacingSize="xs2"
startIcon={{ icon: faMagicWandSparkles }}
loading={genLoading}
on:click={onFix}
>
AI Fix
</Button>
{/if}</div
>
{/if}
{/if}
@@ -0,0 +1,199 @@
<script lang="ts">
import { Button } from '../common'
import { SUPPORTED_LANGUAGES, editScript, generateScript } from './lib'
import type { SupportedLanguage } from '$lib/common'
import { sendUserToast } from '$lib/toast'
import type Editor from '../Editor.svelte'
import { faCheck, faClose, faMagicWandSparkles } from '@fortawesome/free-solid-svg-icons'
import Popup from '../common/popup/Popup.svelte'
import { fade } from 'svelte/transition'
import { Icon } from 'svelte-awesome'
import { WorkspaceService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import type DiffEditor from '../DiffEditor.svelte'
import { scriptLangToEditorLang } from '$lib/scripts'
import type { Selection } from 'monaco-editor/esm/vs/editor/editor.api'
// props
export let iconOnly: boolean = false
export let lang: SupportedLanguage
export let editor: Editor | undefined
export let diffEditor: DiffEditor | undefined
// state
let funcDesc: string = ''
let genLoading: boolean = false
let openAIAvailable: boolean | undefined = undefined
let button: HTMLButtonElement | undefined
let input: HTMLInputElement | undefined
let generatedCode = ''
let selection: Selection | undefined
let isEdit = false
async function onGenerate() {
try {
// close popup ^^
const elem = document.activeElement as HTMLElement
if (elem.blur) {
elem.blur()
}
genLoading = true
if (isEdit && selection) {
const selectedCode = editor?.getSelectedLines() || ''
const originalCode = editor?.getCode() || ''
const selectionGenCode = await editScript({
language: lang,
description: funcDesc,
selectedCode
})
generatedCode = originalCode.replace(selectedCode, selectionGenCode + '\n')
} else {
generatedCode = await generateScript({
language: lang,
description: funcDesc
})
}
funcDesc = ''
} catch (err) {
sendUserToast('Failed to generate code', true)
console.error(err)
} finally {
genLoading = false
}
}
function acceptDiff() {
editor?.setCode(diffEditor?.getModified() || '')
editor?.format()
generatedCode = ''
}
function rejectDiff() {
generatedCode = ''
}
async function checkIfOpenAIAvailable(lang: SupportedLanguage) {
try {
const exists = await WorkspaceService.existsOpenaiKey({ workspace: $workspaceStore! })
openAIAvailable = exists && SUPPORTED_LANGUAGES.has(lang)
} catch (err) {
console.error(err)
sendUserToast('Failed to check if OpenAI is available', true)
}
}
function showDiff() {
diffEditor?.setDiff(editor?.getCode() || '', generatedCode, scriptLangToEditorLang(lang))
diffEditor?.show()
editor?.hide()
}
function hideDiff() {
editor?.show()
diffEditor?.hide()
}
function setSelectionHandler() {
editor?.onDidChangeCursorSelection((e) => {
selection = e.selection
})
}
$: checkIfOpenAIAvailable(lang)
$: input?.focus()
$: lang && (generatedCode = '')
$: generatedCode && showDiff()
$: !generatedCode && hideDiff()
$: editor && setSelectionHandler()
$: selection && (isEdit = !selection.isEmpty())
</script>
{#if openAIAvailable}
{#if generatedCode}
<div class="flex gap-1 px-2">
<Button
title="Discard generated code"
btnClasses="!font-medium"
size="xs"
color="red"
spacingSize="xs2"
on:click={rejectDiff}
variant="contained"
startIcon={{ icon: faClose }}
{iconOnly}
>
Discard
</Button><Button
title="Accept generated code"
btnClasses="!font-medium"
size="xs"
color="green"
spacingSize="xs2"
on:click={acceptDiff}
startIcon={{ icon: faCheck }}
{iconOnly}
>
Accept
</Button>
</div>
{:else}
<Button
title="Generate code from prompt"
btnClasses="!font-medium text-gray-600"
size="xs"
color="light"
spacingSize="md"
bind:element={button}
startIcon={{ icon: faMagicWandSparkles }}
{iconOnly}
loading={genLoading}
>
{isEdit ? 'AI Edit' : 'AI Gen'}
</Button>
{/if}
{#if !generatedCode}
<Popup
ref={button}
options={{ placement: 'top-start' }}
transition={fade}
closeOn={[]}
wrapperClasses="!z-[1002]"
outerClasses="rounded shadow-xl bg-white border p-3 w-96"
>
<label class="block text-gray-900">
<div class="pb-1 text-sm text-gray-600">Prompt</div>
<div class="flex w-full">
<input
type="text"
bind:this={input}
bind:value={funcDesc}
class="!w-auto grow"
on:keypress={({ key }) => {
if (key === 'Enter') {
onGenerate()
}
}}
placeholder={isEdit
? 'Describe the changes you want'
: 'Describe what the script should do'}
/>
<Button
size="xs"
color="blue"
buttonType="button"
btnClasses="!p-1 !w-[34px] !ml-1"
aria-label="Generate"
on:click={onGenerate}
>
<Icon data={faMagicWandSparkles} />
</Button>
</div>
</label>
</Popup>
{/if}
{/if}
@@ -0,0 +1 @@
export { default as ScriptGen } from './ScriptGen.svelte'
+280
View File
@@ -0,0 +1,280 @@
import { OpenAI } from 'openai'
import { OpenAPI } from '../../gen/core/OpenAPI'
import { ResourceService, Script } from '../../gen'
import { workspaceStore } from '$lib/stores'
import { formatResourceTypes } from './utils'
import { scriptLangToEditorLang } from '$lib/scripts'
const WARNING_MSG =
'WARNING: this code was generated by OpenAI, it might not be accurate. Use at your own risk.'
const COMMENT_TYPES = {
[Script.language.PYTHON3]: '#',
[Script.language.DENO]: '//',
[Script.language.GO]: '//',
[Script.language.BASH]: '#',
[Script.language.POSTGRESQL]: '--',
[Script.language.MYSQL]: '--',
[Script.language.NATIVETS]: '//',
[Script.language.BUN]: '//'
}
const PROMPTS = {
[Script.language
.PYTHON3]: `Write a function in python called "main". The function should {description}. Specify the parameter types. Do not call the main function.
You have access to the following resource types, if you need them, you have to define a TypedDict with the name specified (DO NOT CAPITALIZE) and add them as parameters: {resourceTypes}`,
[Script.language
.DENO]: `Write a function in typescript called "main". The function should {description}. Specify the parameter types. You are in a Deno environment. You can import deno libraries or you can also import npm libraries like that: "import ... from "npm:{package}";". Export the "main" function like this: "export function main(...)". Do not call the main function.
You have access to the following resource types, if you need them, you have to define the type with the name specified and add them as parameters: {resourceTypes}
If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.`,
[Script.language.GO]:
'Write a function in go called "main". The function should {description}. Import the packages you need. The return type of the function has to be ({return_type}, error). The file package has to be "inner".',
[Script.language.BASH]:
'Write bash code that should {description}. Do not include "#!/bin/bash". Arguments are always string and can only be obtained with "var1="$1"", "var2="$2"", etc... You do not need to check if the arguments are present.',
[Script.language.POSTGRESQL]:
'Write SQL code for a PostgreSQL that should {description}. Arguments can be obtained directly in the statement with $1::{type}, $2::{type}, etc... Name the parameters by adding comments before the command like that: -- $1 name1 (one per row, do not include the type)',
[Script.language.MYSQL]:
'Write SQL code for MySQL that should {description}. Arguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the command like that: -- ? name1 ({type}) (one per row)',
[Script.language
.NATIVETS]: `Write a function in typescript called "main". The function should {description}. Specify the parameter types. You should use fetch and are not allowed to import any libraries. Export the "main" function like this: "export function main(...)". Do not call the main function.
You have access to the following resource types, if you need them, you have to define the type with the name specified and add them as parameters: {resourceTypes}
If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.`,
[Script.language
.BUN]: `Write a function in typescript called "main". The function should {description}. Specify the parameter types. You are in a Node.js environment. You can import npm libraries. Export the "main" function like this: "export function main(...)". Do not call the main function.
You have access to the following resource types, if you need them, you have to define the type with the name specified and add them as parameters: {resourceTypes}
If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.`
}
function scriptLangToEnvironment(lang: Script.language) {
if (lang === Script.language.DENO) {
return 'typescript in a deno running environment'
} else if (lang === Script.language.BUN) {
return 'typescript in a node.js running environment'
} else if (lang === Script.language.NATIVETS) {
return 'typescript where you should use fetch and are not allowed to import any libraries'
} else {
return lang
}
}
const SYSTEM_PROMPT =
'You write code as queried by the user. Only output code. Wrap the code like that: ```language\n{code}\n```. Put any explanation directly in the code as comments.'
const EDIT_SYSTEM_PROMPT =
'You edit the code as queried by the user. Only output code. Wrap the code like that: ```language\n{code}\n```. Put any explanation directly in the code as comments.'
const EDIT_PROMPT =
"Here's my environement: {environment}\nHere's my code: ```{lang}\n{code}\n```\nMy instructions: {description}"
const FIX_PROMPT =
"Here's my environement: {environment}\nHere's my code: ```{lang}\n{code}\n```\nI get the following error: {error}\nFix it for me."
const FIX_SYSTEM_PROMPT =
'You fix the code shared by the user. Only output code. Wrap the code like that: ```language\n{code}\n```. Put explanations directly in the code as comments.'
export const SUPPORTED_LANGUAGES = new Set(Object.keys(PROMPTS))
let workspace: string | undefined = undefined
workspaceStore.subscribe((value) => {
workspace = value
})
interface BaseOptions {
language: Script.language
}
interface ScriptGenerationOptions extends BaseOptions {
language: Script.language
description: string
}
interface EditScriptOptions extends ScriptGenerationOptions {
selectedCode: string
}
interface FixScriptOpions extends BaseOptions {
code: string
error: string
}
export async function generateScript(scriptOptions: ScriptGenerationOptions): Promise<string> {
if (!workspace) {
throw new Error('No workspace selected')
}
const baseURL = `${location.origin}${OpenAPI.BASE}/w/${workspace}/openai/proxy`
const openai = new OpenAI({
baseURL,
apiKey: 'fakekey',
defaultHeaders: {
Authorization: ''
}
})
let prompt = PROMPTS[scriptOptions.language].replace('{description}', scriptOptions.description)
if (
[Script.language.DENO, Script.language.BUN, Script.language.NATIVETS].includes(
scriptOptions.language
)
) {
const resourceTypes = await ResourceService.listResourceType({ workspace })
const resourceTypesText = formatResourceTypes(resourceTypes, true)
prompt = prompt.replace('{resourceTypes}', resourceTypesText)
} else if (scriptOptions.language == Script.language.PYTHON3) {
const resourceTypes = await ResourceService.listResourceType({ workspace })
const resourceTypesText = formatResourceTypes(resourceTypes, false)
prompt = prompt.replace('{resourceTypes}', resourceTypesText)
}
const completion = await openai.chat.completions.create({
model: 'gpt-4',
max_tokens: 2048,
messages: [
{
role: 'system',
content: SYSTEM_PROMPT
},
{
role: 'user',
content: prompt
}
]
})
let result = completion.choices[0]?.message?.content
if (!result) {
throw new Error('No result from OpenAI')
}
const match = result.match(/```[a-zA-z]+\n([\s\S]*?)\n```/)
if (!match || match.length < 2) {
throw new Error('No code block found')
}
result = match[1]
if (scriptOptions.language == Script.language.GO) {
const warning = COMMENT_TYPES[scriptOptions.language] + ' ' + WARNING_MSG + '\n'
return result.trim().replace('package inner\n', 'package inner\n' + warning)
} else if (scriptOptions.language == Script.language.BASH) {
return (
'# shellcheck shell=bash\n' +
COMMENT_TYPES[scriptOptions.language] +
' ' +
WARNING_MSG +
'\n\n' +
result.trim()
)
} else {
return COMMENT_TYPES[scriptOptions.language] + ' ' + WARNING_MSG + '\n\n' + result.trim()
}
}
export async function editScript(scriptOptions: EditScriptOptions): Promise<string> {
if (!workspace) {
throw new Error('No workspace selected')
}
const baseURL = `${location.origin}${OpenAPI.BASE}/w/${workspace}/openai/proxy`
const openai = new OpenAI({
baseURL,
apiKey: 'fakekey',
defaultHeaders: {
Authorization: ''
}
})
let prompt = EDIT_PROMPT.replace('{lang}', scriptLangToEditorLang(scriptOptions.language))
.replace('{code}', scriptOptions.selectedCode)
.replace('{description}', scriptOptions.description)
.replace('{environment}', scriptLangToEnvironment(scriptOptions.language))
const completion = await openai.chat.completions.create({
model: 'gpt-4',
max_tokens: 512,
messages: [
{
role: 'system',
content: EDIT_SYSTEM_PROMPT
},
{
role: 'user',
content: prompt
}
],
temperature: 0.5
})
let result = completion.choices[0]?.message?.content
if (!result) {
throw new Error('No result from OpenAI')
}
const match = result.match(/```[a-zA-z]+\n([\s\S]*?)\n```/)
if (!match || match.length < 2) {
throw new Error('No code block found')
}
result = match[1]
return result
}
export async function fixScript(scriptOptions: FixScriptOpions) {
if (!workspace) {
throw new Error('No workspace selected')
}
const baseURL = `${location.origin}${OpenAPI.BASE}/w/${workspace}/openai/proxy`
const openai = new OpenAI({
baseURL,
apiKey: 'fakekey',
defaultHeaders: {
Authorization: ''
}
})
let prompt = FIX_PROMPT.replace('{lang}', scriptLangToEditorLang(scriptOptions.language))
.replace('{code}', scriptOptions.code)
.replace('{error}', scriptOptions.error)
.replace('{environment}', scriptLangToEnvironment(scriptOptions.language))
const completion = await openai.chat.completions.create({
model: 'gpt-4',
max_tokens: 512,
messages: [
{
role: 'system',
content: FIX_SYSTEM_PROMPT
},
{
role: 'user',
content: prompt
}
]
})
let result = completion.choices[0]?.message?.content
if (!result) {
throw new Error('No result from OpenAI')
}
const match = result.match(/```[a-zA-z]+\n([\s\S]*?)\n```/)
if (!match || match.length < 2) {
throw new Error('No code block found')
}
result = match[1]
return result
}
@@ -0,0 +1,52 @@
import type { Schema, SchemaProperty } from '../../common'
import type { ResourceType } from '../../gen'
import { capitalize } from '$lib/utils'
function compile(schema: Schema) {
function rec(x: { [name: string]: SchemaProperty }, root = false) {
let res = '{ '
const entries = Object.entries(x)
if (entries.length == 0) {
return 'any'
}
let i = 0
for (let [name, prop] of entries) {
if (prop.type == 'object') {
res += `${name}: ${rec(prop.properties ?? {})}`
} else if (prop.type == 'array') {
res += `${name}: ${prop?.items?.type ?? 'any'}[]`
} else {
let typ = prop?.type ?? 'any'
if (typ == 'integer') {
typ = 'number'
}
res += `${name}: ${typ}`
}
i++
if (i < entries.length) {
res += ', '
}
}
res += ' }'
return res
}
return rec(schema.properties, true)
}
function toCamel(s) {
return s.replace(/([-_][a-z])/gi, ($1) => {
return $1.toUpperCase().replace('-', '').replace('_', '')
})
}
export function formatResourceTypes(resourceTypes: ResourceType[], camel = false) {
const result = resourceTypes.map((resourceType) => {
return `${camel ? toCamel(capitalize(resourceType.name)) : resourceType.name} ${compile(
resourceType.schema
)}`
})
return result.join(', ')
}
@@ -19,11 +19,15 @@
import { Pane, Splitpanes } from 'svelte-splitpanes'
import SplitPanesWrapper from '../splitPanes/SplitPanesWrapper.svelte'
import { Loader2 } from 'lucide-svelte'
import type Editor from '../Editor.svelte'
import type DiffEditor from '../DiffEditor.svelte'
export let lang: Preview.language | undefined
export let previewIsLoading = false
export let previewJob: Job | undefined
export let pastPreviews: CompletedJob[] = []
export let editor: Editor | undefined = undefined
export let diffEditor: DiffEditor | undefined = undefined
type DrawerContent = {
mode: 'json' | Preview.language | 'plain'
@@ -92,6 +96,9 @@
workspaceId={previewJob?.workspace_id}
jobId={previewJob?.id}
result={previewJob.result}
{editor}
{diffEditor}
{lang}
/>
</div>
{:else}
@@ -53,6 +53,7 @@
let customer_id: string | undefined = undefined
let webhook: string | undefined = undefined
let workspaceToDeployTo: string | undefined = undefined
let openAIKey: string | undefined = undefined
let errorHandlerInitialPath: string
let errorHandlerScriptPath: string
let errorHandlerItemKind: 'script' = 'script'
@@ -125,6 +126,23 @@
}
}
async function editOpenAIKey(): Promise<void> {
// in JS, an empty string is also falsy
if (openAIKey) {
await WorkspaceService.editOpenaiKey({
workspace: $workspaceStore!,
requestBody: { openai_key: openAIKey }
})
sendUserToast('OpenAI key set')
} else {
await WorkspaceService.editOpenaiKey({
workspace: $workspaceStore!,
requestBody: { openai_key: undefined }
})
sendUserToast(`OpenAI key removed`)
}
}
async function loadSettings(): Promise<void> {
const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
team_name = settings.slack_name
@@ -139,6 +157,7 @@
customer_id = settings.customer_id
workspaceToDeployTo = settings.deploy_to
webhook = settings.webhook
openAIKey = settings.openai_key
errorHandlerScriptPath = (settings.error_handler ?? '').split('/').slice(1).join('/')
errorHandlerInitialPath = errorHandlerScriptPath
}
@@ -275,6 +294,14 @@
<Tab size="md" value="error_handler">
<div class="flex gap-2 items-center my-1">Error Handler</div>
</Tab>
<Tab size="md" value="openai">
<div class="flex gap-2 items-center my-1"
>OpenAI Credentials <span class="text-white px-2 py-1 rounded-full text-xs bg-red-500"
>Beta</span
></div
>
</Tab>
</Tabs>
</div>
{#if tab == 'users'}
@@ -848,6 +875,17 @@
></div
>
</div>
{:else if tab == 'openai'}
<PageHeader title="OpenAI Credentials" primary={false} />
<div class="mt-2"
><Alert type="info" title="Experimental feature"
>Enter your OpenAI api key to unlock Windmill's AI features!</Alert
></div
>
<div class="flex gap-2 mt-5">
<input type="text" placeholder="Secret API key" bind:value={openAIKey} />
<Button size="md" on:click={editOpenAIKey}>Save</Button>
</div>
{/if}
{:else}
<div class="bg-red-100 border-l-4 border-red-600 text-orange-700 p-4 m-4" role="alert">