Revert "chore: revert non-migration files to main branch state"

This reverts commit 38ba77db06.
This commit is contained in:
Diego Imbert
2026-03-02 10:32:01 +01:00
parent 38ba77db06
commit dd63dac840
185 changed files with 11447 additions and 6545 deletions
+1 -1
View File
@@ -238,4 +238,4 @@ jobs:
run: |
deno --version && bun -v && node --version && go version && python3 --version && php --version && ruby --version && pwsh --version && dotnet --version
cd windmill-duckdb-ffi-internal && ./build_dev.sh && cd ..
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp,run_inline --all -- --nocapture --test-threads=10
DENO_PATH=$(which deno) BUN_PATH=$(which bun) NODE_BIN_PATH=$(which node) GO_PATH=$(which go) UV_PATH=$(which uv) PHP_PATH=$(which php) COMPOSER_PATH=$(which composer) RUBY_PATH=$(which ruby) RUBY_BUNDLE_PATH=$(which bundle) RUBY_GEM_PATH=$(which gem) POWERSHELL_PATH=$(which pwsh) DOTNET_PATH=$(which dotnet) cargo test --features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,private_registry_test,csharp,php,ruby,mysql,quickjs,mcp --all -- --nocapture --test-threads=10
+20 -2
View File
@@ -9,7 +9,9 @@ on:
issue_comment:
types:
- created
- edited
pull_request_review_comment:
types:
- created
jobs:
notify_discord_when_pr_opened:
@@ -51,7 +53,23 @@ jobs:
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
COMMENT_URL: ${{ github.event.comment.html_url }}
COMMENT_IS_EDIT: ${{ github.event.action == 'edited' }}
DISCORD_CHANNEL_ID: "1372204995868491786"
DISCORD_GUILD_ID: "930051556043276338"
secrets:
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_AI_BOT_TOKEN }}
notify_discord_on_review_comment:
if: >
github.event_name == 'pull_request_review_comment'
&& github.event.comment.user.login != 'cloudflare-workers-and-pages[bot]'
&& github.event.comment.user.login != 'ellipsis-dev[bot]'
uses: ./.github/workflows/shareable-discord-notification.yml
with:
PR_STATUS: "comment"
PR_NUMBER: ${{ github.event.pull_request.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
COMMENT_URL: ${{ github.event.comment.html_url }}
DISCORD_CHANNEL_ID: "1372204995868491786"
DISCORD_GUILD_ID: "930051556043276338"
secrets:
@@ -36,10 +36,6 @@ on:
description: "The comment URL"
type: string
default: ""
COMMENT_IS_EDIT:
description: "Whether this is an edit of an existing comment"
type: string
default: "false"
secrets:
DISCORD_WEBHOOK_URL:
description: "Discord Webhook URL"
@@ -139,7 +135,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ inputs.PR_STATUS == 'comment' }}
steps:
- name: Post or update comment in Discord thread
- name: Post comment to Discord thread
env:
BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
CHANNEL_ID: ${{ inputs.DISCORD_CHANNEL_ID }}
@@ -148,7 +144,6 @@ jobs:
COMMENT_BODY: ${{ inputs.COMMENT_BODY }}
COMMENT_AUTHOR: ${{ inputs.COMMENT_AUTHOR }}
COMMENT_URL: ${{ inputs.COMMENT_URL }}
COMMENT_IS_EDIT: ${{ inputs.COMMENT_IS_EDIT }}
run: |
# 1) Find the thread by PR number
threads=$(curl -s -H "Authorization: Bot $BOT_TOKEN" \
@@ -177,36 +172,10 @@ jobs:
truncated_body="$COMMENT_BODY"
fi
# 3) Build the message content
if [ "$COMMENT_IS_EDIT" = "true" ]; then
message=$(printf '**%s** [edited comment](%s):\n%s' "$COMMENT_AUTHOR" "$COMMENT_URL" "$truncated_body")
else
message=$(printf '**%s** [commented](%s):\n%s' "$COMMENT_AUTHOR" "$COMMENT_URL" "$truncated_body")
fi
# 3) Post the comment to the thread
message=$(printf '**%s** [commented](%s):\n%s' "$COMMENT_AUTHOR" "$COMMENT_URL" "$truncated_body")
payload=$(jq -n --arg content "$message" '{content: $content, flags: 4, allowed_mentions: {parse: []}}')
# 4) If this is an edit, try to find and update the existing Discord message
if [ "$COMMENT_IS_EDIT" = "true" ]; then
# Search recent messages in the thread for one containing the comment URL
messages=$(curl -s -H "Authorization: Bot $BOT_TOKEN" \
"https://discord.com/api/v10/channels/${thread_id}/messages?limit=100")
existing_msg_id=$(echo "$messages" | jq -r \
--arg url "$COMMENT_URL" \
'[.[] | select(.content | contains($url))] | first | .id // empty')
if [ -n "$existing_msg_id" ]; then
echo "Updating existing Discord message $existing_msg_id"
curl -s -X PATCH \
-H "Authorization: Bot $BOT_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload" \
"https://discord.com/api/v10/channels/${thread_id}/messages/${existing_msg_id}"
exit 0
fi
echo "Original Discord message not found, posting as new message"
fi
# 5) Post a new message to the thread
curl -s -X POST \
-H "Authorization: Bot $BOT_TOKEN" \
-H "Content-Type: application/json" \
-1
View File
@@ -16167,7 +16167,6 @@ dependencies = [
"windmill-common",
"windmill-native-triggers",
"windmill-test-utils",
"windmill-worker",
]
[[package]]
+2 -2
View File
@@ -159,12 +159,12 @@ all_languages = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "
# For windows we have another set of languages enabled
all_languages_windows = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql-winauth", "bigquery", "csharp", "nu", "php", "java"]
# Edition meta-features: shared groups
run_inline = ["windmill-api/run_inline"]
inline_preview = ["windmill-api/inline_preview"]
oss_core = [
"embedding", "parquet", "openidconnect", "license",
"http_trigger", "zip", "oauth2", "postgres_trigger",
"mqtt_trigger", "websocket", "smtp", "native_trigger",
"static_frontend", "mcp", "bedrock", "run_inline",
"static_frontend", "mcp", "bedrock", "inline_preview",
"quickjs"
]
ce_core = ["oss_core", "private", "operator"]
+1 -1
View File
@@ -13,7 +13,7 @@ default = []
enterprise = ["dep:windmill-autoscaling"]
private = []
python = []
run_inline = ["dep:windmill-worker", "dep:itertools"]
inline_preview = ["dep:windmill-worker", "dep:itertools"]
[dependencies]
windmill-api-auth.workspace = true
+3 -3
View File
@@ -283,14 +283,14 @@ async fn native_kubernetes_autoscaling_healthcheck() -> Result<(), error::Error>
}
async fn list_available_python_versions() -> error::JsonResult<Vec<String>> {
#[cfg(not(all(feature = "python", feature = "run_inline")))]
#[cfg(not(all(feature = "python", feature = "inline_preview")))]
return Err(error::Error::BadRequest(
"Python listing available only with 'python' feature enabled".to_string(),
));
#[cfg(all(feature = "python", feature = "run_inline"))]
#[cfg(all(feature = "python", feature = "inline_preview"))]
use itertools::Itertools;
#[cfg(all(feature = "python", feature = "run_inline"))]
#[cfg(all(feature = "python", feature = "inline_preview"))]
return Ok(Json(
windmill_worker::PyV::list_available_python_versions()
.await
@@ -14,7 +14,6 @@ private = ["windmill-test-utils/private", "dep:aws-config", "dep:aws-credential-
enterprise = ["windmill-test-utils/enterprise", "dep:base64"]
deno_core = ["windmill-test-utils/deno_core"]
mcp = []
run_inline = ["dep:windmill-worker", "windmill-test-utils/run_inline", "windmill-test-utils/duckdb"]
[dependencies]
windmill-test-utils.workspace = true
@@ -22,7 +21,6 @@ windmill-api-client.workspace = true
windmill-common = { workspace = true, default-features = false }
windmill-native-triggers = { workspace = true, features = ["native_trigger"] }
windmill-api-auth.workspace = true
windmill-worker = { workspace = true, optional = true }
sqlx.workspace = true
serde_json.workspace = true
serde.workspace = true
+1 -1
View File
@@ -13,7 +13,7 @@ default = []
private = ["windmill-audit/private", "windmill-common/private", "windmill-api-auth/private", "windmill-store/private", "windmill-api-users/private", "windmill-api-workspaces/private", "windmill-api-groups/private", "windmill-api-configs/private", "windmill-api-settings/private", "windmill-api-agent-workers?/private", "windmill-trigger-kafka?/private", "windmill-trigger-postgres?/private", "windmill-trigger-mqtt?/private", "windmill-trigger-websocket?/private", "windmill-trigger-nats?/private", "windmill-trigger-sqs?/private", "windmill-trigger-gcp?/private", "windmill-trigger-email?/private"]
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-flows/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-api-configs/enterprise", "windmill-api-settings/enterprise", "windmill-api-agent-workers?/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
stripe = []
run_inline = ["dep:windmill-worker", "windmill-api-configs/run_inline"]
inline_preview = ["dep:windmill-worker", "windmill-api-configs/inline_preview"]
agent_worker_server = ["dep:windmill-worker", "dep:windmill-api-agent-workers"]
enterprise_saml = ["dep:samael", "dep:libxml"]
benchmark = []
-54
View File
@@ -9173,54 +9173,6 @@ paths:
application/json:
schema: {}
/w/{workspace}/jobs/run_inline/p/{path}:
post:
summary: run script by path without starting a new job
operationId: runScriptByPathInline
tags:
- job
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptPath"
requestBody:
description: script args
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/InlineScriptArgs"
responses:
"200":
description: script result
content:
application/json:
schema: {}
/w/{workspace}/jobs/run_inline/h/{hash}:
post:
summary: run script by hash without starting a new job
operationId: runScriptByHashInline
tags:
- job
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/ScriptHash"
requestBody:
description: script args
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/InlineScriptArgs"
responses:
"200":
description: script result
content:
application/json:
schema: {}
/w/{workspace}/jobs/run_wait_result/preview:
post:
summary: run script preview and wait for result
@@ -19859,12 +19811,6 @@ components:
$ref: "#/components/schemas/ScriptLang"
required: [content, args, language]
InlineScriptArgs:
type: object
properties:
args:
$ref: "#/components/schemas/ScriptArgs"
WorkflowTask:
type: object
properties:
+11 -130
View File
@@ -27,22 +27,20 @@ use url::Url;
#[cfg(all(feature = "enterprise", feature = "smtp"))]
use windmill_common::auth::is_super_admin_email;
use windmill_common::auth::TOKEN_PREFIX_LEN;
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
use windmill_common::client::AuthedClient;
use windmill_common::db::UserDbWithAuthed;
use windmill_common::error::JsonResult;
use windmill_common::flow_status::{JobResult, RestartedFrom};
#[cfg(feature = "inline_preview")]
use windmill_common::jobs::RunInlinePreviewScriptFnParams;
use windmill_common::jobs::{
format_completed_job_result, format_result, DynamicInput, ENTRYPOINT_OVERRIDE,
};
#[cfg(feature = "run_inline")]
use windmill_common::jobs::{
InlineScriptTarget, RunInlinePreviewScriptFnParams, RunInlineScriptFnParams,
};
use windmill_common::runnable_settings::{
ConcurrencySettings, ConcurrencySettingsWithCustom, DebouncingSettings,
};
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
use windmill_common::runtime_assets::{register_runtime_asset, InsertRuntimeAssetParams};
use windmill_common::scripts::ScriptRunnableSettingsInline;
use windmill_common::triggers::TriggerMetadata;
@@ -55,15 +53,15 @@ use windmill_common::DYNAMIC_INPUT_CACHE;
#[cfg(all(feature = "enterprise", feature = "smtp"))]
use windmill_common::{email_oss::send_email_html, server::load_smtp_config};
use windmill_object_store::upload_artifact_to_store;
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
use windmill_parser::asset_parser::AssetKind;
use windmill_types::s3::BundleFormat;
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
use windmill_worker::get_worker_internal_server_inline_utils;
use windmill_common::variables::get_workspace_key;
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
use crate::db::OptJobAuthed;
use crate::triggers::trigger_helpers::{FlowId, ScriptId};
use crate::{
@@ -244,11 +242,6 @@ pub fn workspaced_service() -> Router {
)
.route("/run/preview", post(run_preview_script))
.route("/run_inline/preview", post(run_inline_preview_script))
.route(
"/run_inline/p/*script_path",
post(run_inline_script_by_path),
)
.route("/run_inline/h/:hash", post(run_inline_script_by_hash))
.route(
"/run_wait_result/preview",
post(run_wait_result_preview_script),
@@ -2860,7 +2853,7 @@ struct Preview {
flow_path: Option<String>,
}
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
#[derive(Debug, Deserialize)]
struct PreviewInline {
content: String,
@@ -2868,12 +2861,6 @@ struct PreviewInline {
language: ScriptLang,
}
#[cfg(feature = "run_inline")]
#[derive(Debug, Deserialize)]
struct InlineScriptArgs {
args: Option<HashMap<String, Box<JsonRawValue>>>,
}
#[derive(Deserialize)]
pub struct WorkflowTask {
pub args: Option<HashMap<String, Box<JsonRawValue>>>,
@@ -4586,7 +4573,7 @@ async fn run_preview_script(
Ok((StatusCode::CREATED, uuid.to_string()))
}
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
async fn run_inline_preview_script(
OptJobAuthed { authed, job_id }: OptJobAuthed,
Tokened { token }: Tokened,
@@ -4623,120 +4610,14 @@ async fn run_inline_preview_script(
Ok(Json(to_raw_value(&result)).into_response())
}
#[cfg(not(feature = "run_inline"))]
#[cfg(not(feature = "inline_preview"))]
async fn run_inline_preview_script() -> error::Result<Response> {
Err(error::Error::InternalErr(
"inline preview requires the worker feature".to_string(),
))
}
#[cfg(feature = "run_inline")]
async fn run_inline_script_by_path(
OptJobAuthed { authed, .. }: OptJobAuthed,
Tokened { token }: Tokened,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Path((w_id, script_path)): Path<(String, StripPath)>,
Json(body): Json<InlineScriptArgs>,
) -> error::Result<Response> {
let script_path_str = script_path.to_path();
check_scopes(&authed, || format!("jobs:run:scripts:{script_path_str}"))?;
run_inline_script_inner(
authed,
token,
db,
w_id,
InlineScriptTarget::Path(script_path.to_path().to_string()),
body.args,
Some(user_db),
)
.await
}
#[cfg(not(feature = "run_inline"))]
async fn run_inline_script_by_path() -> error::Result<Response> {
Err(error::Error::InternalErr(
"inline script by path requires the worker feature".to_string(),
))
}
#[cfg(feature = "run_inline")]
async fn run_inline_script_by_hash(
OptJobAuthed { authed, .. }: OptJobAuthed,
Tokened { token }: Tokened,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
Json(body): Json<InlineScriptArgs>,
) -> error::Result<Response> {
// Resolve the script path from the hash and check scopes properly
let hash = script_hash.0;
let userdb_authed = UserDbWithAuthed { db: user_db.clone(), authed: &authed.to_authed_ref() };
let ScriptHashInfo { path, .. } =
get_script_info_for_hash(Some(userdb_authed), &db, &w_id, hash)
.await?
.prefetch_cached(&db)
.await?;
check_scopes(&authed, || format!("jobs:run:scripts:{path}"))?;
run_inline_script_inner(
authed,
token,
db,
w_id,
InlineScriptTarget::Hash(hash),
body.args,
Some(user_db),
)
.await
}
#[cfg(not(feature = "run_inline"))]
async fn run_inline_script_by_hash() -> error::Result<Response> {
Err(error::Error::InternalErr(
"inline script by hash requires the worker feature".to_string(),
))
}
#[cfg(feature = "run_inline")]
async fn run_inline_script_inner(
authed: ApiAuthed,
token: String,
db: DB,
w_id: String,
target: InlineScriptTarget,
args: Option<HashMap<String, Box<JsonRawValue>>>,
user_db: Option<UserDB>,
) -> error::Result<Response> {
let utils = get_worker_internal_server_inline_utils()?;
let authed_owned: windmill_common::db::Authed = authed.clone().into();
let result = utils.run_inline_script.as_ref()(RunInlineScriptFnParams {
target,
args,
workspace_id: w_id.clone(),
base_internal_url: utils.base_internal_url.clone(),
killpill_rx: utils.killpill_rx.resubscribe(),
created_by: authed.display_username().to_string(),
permissioned_as: username_to_permissioned_as(&authed.username),
permissioned_as_email: authed.email.clone(),
job_dir: "".to_string(),
worker_name: "".to_string(),
worker_dir: "".to_string(),
client: AuthedClient {
base_internal_url: utils.base_internal_url.clone(),
force_client: None,
token,
workspace: w_id,
},
conn: windmill_common::worker::Connection::Sql(db),
user_db: user_db.map(|udb| (udb, authed_owned)),
})
.await?;
Ok(Json(to_raw_value(&result)).into_response())
}
#[cfg(feature = "run_inline")]
#[cfg(feature = "inline_preview")]
fn register_potential_assets_on_inline_execution(
job_id: Uuid,
w_id: &str,
-15
View File
@@ -17,21 +17,6 @@ pub struct Authed {
pub token_prefix: Option<String>,
}
impl Authed {
pub fn to_authed_ref(&self) -> AuthedRef<'_> {
AuthedRef {
email: &self.email,
username: &self.username,
is_admin: &self.is_admin,
is_operator: &self.is_operator,
groups: &self.groups,
folders: &self.folders,
scopes: &self.scopes,
token_prefix: &self.token_prefix,
}
}
}
#[derive(Clone, Debug, Hash)]
pub struct AuthedRef<'a> {
pub email: &'a str,
-29
View File
@@ -326,28 +326,6 @@ pub struct RunInlinePreviewScriptFnParams {
pub killpill_rx: tokio::sync::broadcast::Receiver<()>,
}
pub enum InlineScriptTarget {
Path(String),
Hash(i64),
}
pub struct RunInlineScriptFnParams {
pub workspace_id: String,
pub target: InlineScriptTarget,
pub args: Option<HashMap<String, Box<RawValue>>>,
pub created_by: String,
pub permissioned_as: String,
pub permissioned_as_email: String,
pub base_internal_url: String,
pub worker_name: String,
pub conn: crate::worker::Connection,
pub client: AuthedClient,
pub job_dir: String,
pub worker_dir: String,
pub killpill_rx: tokio::sync::broadcast::Receiver<()>,
pub user_db: Option<(crate::db::UserDB, crate::db::Authed)>,
}
#[derive(Clone)]
pub struct WorkerInternalServerInlineUtils {
pub killpill_rx: Arc<tokio::sync::broadcast::Receiver<()>>,
@@ -359,13 +337,6 @@ pub struct WorkerInternalServerInlineUtils {
+ Send
+ Sync,
>,
pub run_inline_script: Arc<
dyn Fn(
RunInlineScriptFnParams,
) -> Pin<Box<dyn Future<Output = error::Result<Box<RawValue>>> + Send>>
+ Send
+ Sync,
>,
}
// To run a script inline, bypassing the db and job queue, windmill-api uses these functions.
// They should only be called by the internal server of a worker.
-53
View File
@@ -2285,59 +2285,6 @@ pub struct MiniPulledJob {
pub runnable_settings_handle: Option<i64>,
}
impl MiniPulledJob {
pub fn new_inline(
workspace_id: String,
args: Option<HashMap<String, Box<RawValue>>>,
created_by: String,
permissioned_as: String,
permissioned_as_email: String,
runnable_path: Option<String>,
kind: JobKind,
runnable_id: Option<ScriptHash>,
tag: String,
script_lang: Option<ScriptLang>,
) -> Self {
Self {
workspace_id,
id: Uuid::new_v4(),
args: args.map(Json),
parent_job: None,
created_by,
scheduled_for: chrono::Utc::now(),
started_at: None,
runnable_path,
kind,
runnable_id,
canceled_reason: None,
canceled_by: None,
permissioned_as,
permissioned_as_email,
flow_status: None,
tag,
script_lang,
same_worker: true,
pre_run_error: None,
flow_innermost_root_job: None,
root_job: None,
timeout: None,
flow_step_id: None,
cache_ttl: None,
cache_ignore_s3_path: None,
priority: None,
preprocessed: None,
script_entrypoint_override: None,
trigger: None,
trigger_kind: None,
visible_to_owner: false,
permissioned_as_end_user_email: None,
runnable_settings_handle: None,
concurrent_limit: None,
concurrency_time_window_s: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MiniCompletedJob {
pub id: Uuid,
-2
View File
@@ -15,8 +15,6 @@ enterprise = []
python = ["windmill-common/python"]
deno_core = ["dep:windmill-runtime-nativets"]
agent_worker_server = ["dep:windmill-api-agent-workers"]
run_inline = ["windmill-api/run_inline"]
duckdb = ["windmill-worker/duckdb"]
[dependencies]
windmill-api = { workspace = true, default-features = false }
+37 -96
View File
@@ -14,10 +14,6 @@ use futures::TryFutureExt;
use tokio::sync::Mutex;
use tokio::time::timeout;
use windmill_common::client::AuthedClient;
use windmill_common::db::UserDbWithAuthed;
use windmill_common::get_latest_deployed_hash_for_path;
use windmill_common::jobs::InlineScriptTarget;
use windmill_common::jobs::RunInlineScriptFnParams;
use windmill_common::jobs::WorkerInternalServerInlineUtils;
use windmill_common::jobs::WORKER_INTERNAL_SERVER_INLINE_UTILS;
use windmill_common::runtime_assets::init_runtime_asset_loop;
@@ -4709,18 +4705,43 @@ pub fn init_worker_internal_server_inline_utils(
base_internal_url,
killpill_rx: Arc::new(killpill_rx),
run_inline_preview_script: Arc::new(|params| {
let job = MiniPulledJob::new_inline(
params.workspace_id,
params.args,
params.created_by,
params.permissioned_as,
params.permissioned_as_email,
None,
JobKind::Preview,
None,
"inline_preview".to_string(),
Some(params.lang),
);
let job = MiniPulledJob {
workspace_id: params.workspace_id,
id: Uuid::new_v4(),
args: params.args.map(Json),
parent_job: None,
created_by: params.created_by,
scheduled_for: chrono::Utc::now(),
started_at: None,
runnable_path: None,
kind: JobKind::Preview,
runnable_id: None,
canceled_reason: None,
canceled_by: None,
permissioned_as: params.permissioned_as,
permissioned_as_email: params.permissioned_as_email,
flow_status: None,
tag: "inline_preview".to_string(),
script_lang: Some(params.lang),
same_worker: true,
pre_run_error: None,
flow_innermost_root_job: None,
root_job: None,
timeout: None,
flow_step_id: None,
cache_ttl: None,
cache_ignore_s3_path: None,
priority: None,
preprocessed: None,
script_entrypoint_override: None,
trigger: None,
trigger_kind: None,
visible_to_owner: false,
permissioned_as_end_user_email: None,
runnable_settings_handle: None,
concurrent_limit: None,
concurrency_time_window_s: None,
};
Box::pin(async move {
let mut mem_peak: i32 = -1;
let mut canceled_by: Option<CanceledBy> = None;
@@ -4757,86 +4778,6 @@ pub fn init_worker_internal_server_inline_utils(
.await
})
}),
run_inline_script: Arc::new(|params: RunInlineScriptFnParams| {
Box::pin(async move {
let (script_hash, runnable_path) = match params.target {
InlineScriptTarget::Path(ref path) => {
let db = params
.conn
.as_sql()
.ok_or_else(|| {
error::Error::InternalErr(
"run_inline_script by path requires a SQL connection"
.to_string(),
)
})?
.clone();
let authed_ref = params.user_db.as_ref().map(|(_, a)| a.to_authed_ref());
let user_db_authed =
params.user_db.as_ref().zip(authed_ref.as_ref()).map(
|((udb, _), ar)| UserDbWithAuthed { db: udb.clone(), authed: ar },
);
let script_hash_info = get_latest_deployed_hash_for_path(
user_db_authed,
db,
&params.workspace_id,
path,
)
.await?;
(ScriptHash(script_hash_info.hash), Some(path.clone()))
}
InlineScriptTarget::Hash(hash) => (ScriptHash(hash), None),
};
let content_info =
get_script_content_by_hash(&script_hash, &params.workspace_id, &params.conn)
.await?;
let job = MiniPulledJob::new_inline(
params.workspace_id,
params.args,
params.created_by,
params.permissioned_as,
params.permissioned_as_email,
runnable_path,
JobKind::Script,
Some(script_hash),
"inline_run".to_string(),
content_info.language,
);
let mut mem_peak: i32 = -1;
let mut canceled_by: Option<CanceledBy> = None;
let mut column_order: Option<Vec<String>> = None;
let mut new_args: Option<HashMap<String, Box<RawValue>>> = None;
let mut occupancy_metrics = OccupancyMetrics::new(Instant::now());
let mut has_stream: bool = false;
let mut killpill_rx = params.killpill_rx;
run_language_executor(
&job,
&params.conn,
&params.client,
None,
&params.job_dir,
&params.worker_dir,
&mut mem_peak,
&mut canceled_by,
&params.base_internal_url,
&params.worker_name,
&mut column_order,
&mut new_args,
&mut occupancy_metrics,
&mut killpill_rx,
None,
&mut has_stream,
content_info.language,
&content_info.content,
&content_info.envs,
&content_info.codebase,
&content_info.lockfile,
true,
)
.await
})
}),
};
WORKER_INTERNAL_SERVER_INLINE_UTILS
.set(utils)
+3 -8
View File
@@ -3,10 +3,7 @@
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
"name": "Frontend container",
"dockerComposeFile": [
"../docker-compose.yml",
"../.devcontainer/docker-compose.yml"
],
"dockerComposeFile": ["../docker-compose.yml", "../.devcontainer/docker-compose.yml"],
"customizations": {
"vscode": {
"extensions": []
@@ -14,12 +11,10 @@
},
"service": "front",
"workspaceFolder": "/workspace",
"forwardPorts": [
8080
],
"forwardPorts": [8080],
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
// "shutdownAction": "none",
"postCreateCommand": "cd frontend && npm install && npm run generate-backend-client && wget https://github.com/caddyserver/caddy/releases/download/v2.6.0-beta.3/caddy_2.6.0-beta.3_linux_amd64.deb -O /tmp/caddy.deb && sudo dpkg -i /tmp/caddy.deb && rm /tmp/caddy.deb",
"postStartCommand": "nohup bash -c 'cd frontend && npm run dev &' && nohup bash -c 'caddy run --config frontend/CaddyfileDev &'",
"remoteUser": "node"
}
}
+1 -1
View File
@@ -5,4 +5,4 @@
"onAutoForward": "openPreview"
}
}
}
}
+7 -1
View File
@@ -47,6 +47,7 @@ function useLoader(argsGetter: () => Args) {
```
This pattern ensures:
- State responsibility is clearly owned by `useLoader`
- Data flows in one direction (parent → child)
- No ambiguity about where state can be modified
@@ -59,7 +60,10 @@ For async requests, **always use `resource()` from the Runed library** instead o
```typescript
import { resource } from 'runed'
let items = resource(() => args, (args) => YourService.route(args))
let items = resource(
() => args,
(args) => YourService.route(args)
)
// Access loading state
items.loading
@@ -69,6 +73,7 @@ items.current
```
The `resource()` utility:
- Automatically handles loading states
- Manages async lifecycle
- Provides reactive updates when dependencies change
@@ -102,6 +107,7 @@ npm run check:fast
```
At the end of a PR to do final validation, you can do the longer one (2s for fast vs 50s for the slow one):
```bash
npm run check
```
+1 -1
View File
@@ -1 +1 @@
{"started":true,"success":true,"completed":true,"result":""}
{ "started": true, "success": true, "completed": true, "result": "" }
+5 -8
View File
@@ -1,22 +1,20 @@
import path from 'path'
import fs from 'fs'
// Check if we're in node_modules (installed as dependency)
if (process.cwd().includes('node_modules')) {
console.log('Skipping postinstall - running as dependency');
process.exit(0);
console.log('Skipping postinstall - running as dependency')
process.exit(0)
}
// Check if we're in the root project
if (process.env.INIT_CWD && process.env.INIT_CWD !== process.cwd()) {
console.log('Skipping postinstall - not root project');
process.exit(0);
console.log('Skipping postinstall - not root project')
process.exit(0)
}
// Your actual postinstall logic here
console.log('Running postinstall for root project');
console.log('Running postinstall for root project')
import { x } from 'tar'
@@ -35,7 +33,6 @@ const response = await fetch(tarUrl)
const buffer = await response.arrayBuffer()
await fs.promises.writeFile(outputTarPath, Buffer.from(buffer))
// Create extract directory if it doesn't exist
try {
await fs.promises.mkdir(extractTo, { recursive: true })
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
+35 -22
View File
@@ -11,11 +11,17 @@ interface DelegateToGitRepoConfig {
* @param value - The value to set (or undefined to remove the field)
* @returns The modified YAML script content
*/
export function updateDelegateToGitRepoField(code: string, fieldName: string, value: string | undefined): string {
export function updateDelegateToGitRepoField(
code: string,
fieldName: string,
value: string | undefined
): string {
const lines = code.split('\n')
// Find delegate_to_git_repo section
const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
const delegateLineIndex = lines.findIndex((line) =>
line.trim().startsWith('delegate_to_git_repo:')
)
if (delegateLineIndex === -1) {
// If no delegate section exists and we're setting a value, create the whole section
@@ -26,8 +32,8 @@ export function updateDelegateToGitRepoField(code: string, fieldName: string, va
}
// Find the specific field line
const fieldLineIndex = lines.findIndex((line, index) =>
index > delegateLineIndex && line.trim().startsWith(`${fieldName}:`)
const fieldLineIndex = lines.findIndex(
(line, index) => index > delegateLineIndex && line.trim().startsWith(`${fieldName}:`)
)
if (fieldLineIndex !== -1) {
@@ -52,7 +58,10 @@ export function updateDelegateToGitRepoField(code: string, fieldName: string, va
* @param config - Configuration object with fields to update
* @returns The modified YAML script content
*/
export function updateDelegateToGitRepoConfig(code: string, config: DelegateToGitRepoConfig): string {
export function updateDelegateToGitRepoConfig(
code: string,
config: DelegateToGitRepoConfig
): string {
let updatedCode = code
// Update each field that's provided
@@ -121,7 +130,12 @@ function insertDelegateToGitRepoSection(code: string, config: DelegateToGitRepoC
// Find the end of inventories section
for (let j = i + 1; j < lines.length; j++) {
const nextLine = lines[j].trim()
if (nextLine && !nextLine.startsWith('-') && !nextLine.startsWith(' ') && !nextLine.startsWith('#')) {
if (
nextLine &&
!nextLine.startsWith('-') &&
!nextLine.startsWith(' ') &&
!nextLine.startsWith('#')
) {
insertionIndex = j
break
}
@@ -150,7 +164,9 @@ function extractDelegateToGitRepoField(code: string, fieldName: string): string
const lines = code.split('\n')
// Find delegate_to_git_repo section
const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
const delegateLineIndex = lines.findIndex((line) =>
line.trim().startsWith('delegate_to_git_repo:')
)
if (delegateLineIndex === -1) {
return undefined
@@ -222,7 +238,9 @@ export function insertAdditionalInventories(code: string, inventoryPaths: string
const lines = code.split('\n')
// Find and update existing additional_inventories section if it exists
const additionalInventoriesIndex = lines.findIndex(line => line.trim().startsWith('additional_inventories:'))
const additionalInventoriesIndex = lines.findIndex((line) =>
line.trim().startsWith('additional_inventories:')
)
if (additionalInventoriesIndex !== -1) {
// Determine the indentation level of the additional_inventories line
const sectionLine = lines[additionalInventoriesIndex]
@@ -292,7 +310,7 @@ export function insertAdditionalInventories(code: string, inventoryPaths: string
// Format the new options content
const optionsIndentation = ' ' // Standard 2-space indentation under additional_inventories
const formattedPaths = inventoryPaths.map(path => `"delegated_git_repository/${path}"`)
const formattedPaths = inventoryPaths.map((path) => `"delegated_git_repository/${path}"`)
const inlineFormat = `${optionsIndentation}- options: [${formattedPaths.join(', ')}]`
let newOptionsContent: string[]
@@ -302,7 +320,7 @@ export function insertAdditionalInventories(code: string, inventoryPaths: string
} else {
// Use dash format
newOptionsContent = [`${optionsIndentation}- options:`]
inventoryPaths.forEach(path => {
inventoryPaths.forEach((path) => {
newOptionsContent.push(`${optionsIndentation} - "delegated_git_repository/${path}"`)
})
}
@@ -319,29 +337,25 @@ export function insertAdditionalInventories(code: string, inventoryPaths: string
}
// Format the inventory paths based on length
const formattedPaths = inventoryPaths.map(path => `"delegated_git_repository/${path}"`)
const formattedPaths = inventoryPaths.map((path) => `"delegated_git_repository/${path}"`)
const inlineFormat = `options: [${formattedPaths.join(', ')}]`
let inventorySection: string[]
if (inlineFormat.length <= 100) {
// Use inline format
inventorySection = [
'additional_inventories:',
` ${inlineFormat}`
]
inventorySection = ['additional_inventories:', ` ${inlineFormat}`]
} else {
// Use dash format with each item on new line
inventorySection = [
'additional_inventories:',
' - options:'
]
inventoryPaths.forEach(path => {
inventorySection = ['additional_inventories:', ' - options:']
inventoryPaths.forEach((path) => {
inventorySection.push(` - "delegated_git_repository/${path}"`)
})
}
// Find insertion point (after the complete delegate_to_git_repo section)
const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
const delegateLineIndex = lines.findIndex((line) =>
line.trim().startsWith('delegate_to_git_repo:')
)
if (delegateLineIndex === -1) {
// If no delegate_to_git_repo section, insert at the beginning (after document marker if exists)
let insertionIndex = 0
@@ -381,4 +395,3 @@ export function insertAdditionalInventories(code: string, inventoryPaths: string
return lines.join('\n')
}
+29 -27
View File
@@ -1,29 +1,29 @@
export interface ATABootstrapConfig {
/** A object you pass in to get callbacks */
delegate: {
/** The callback which gets called when ATA decides a file needs to be written to your VFS */
receivedFile?: (code: string, path: string) => void
/** A way to display progress */
progress?: (downloaded: number, estimatedTotal: number) => void
/** Note: An error message does not mean ATA has stopped! */
errorMessage?: (userFacingMessage: string, error: Error) => void
/** A callback indicating that ATA actually has work to do */
started?: () => void
/** The callback when all ATA has finished */
finished?: (files: Map<string, string>) => void
}
/** Passed to fetch as the user-agent */
projectName: string
/** Your local copy of typescript */
depsParser: (code: string) => string[]
/** A object you pass in to get callbacks */
delegate: {
/** The callback which gets called when ATA decides a file needs to be written to your VFS */
receivedFile?: (code: string, path: string) => void
/** A way to display progress */
progress?: (downloaded: number, estimatedTotal: number) => void
/** Note: An error message does not mean ATA has stopped! */
errorMessage?: (userFacingMessage: string, error: Error) => void
/** A callback indicating that ATA actually has work to do */
started?: () => void
/** The callback when all ATA has finished */
finished?: (files: Map<string, string>) => void
}
/** Passed to fetch as the user-agent */
projectName: string
/** Your local copy of typescript */
depsParser: (code: string) => string[]
/** If you need a custom version of fetch */
fetcher?: typeof fetch
/** If you need a custom logger instead of the console global */
logger?: Logger
/** If you need a custom version of fetch */
fetcher?: typeof fetch
/** If you need a custom logger instead of the console global */
logger?: Logger
}
type ModuleMeta = { state: "loading" }
type ModuleMeta = { state: 'loading' }
/**
* The function which starts up type acquisition,
@@ -34,11 +34,13 @@ type ModuleMeta = { state: "loading" }
* basically exported for tests and should be considered
* implementation details by consumers.
*/
export const setupTypeAcquisition: (config: ATABootstrapConfig) => (initialSourceFile: string) => void
export const setupTypeAcquisition: (
config: ATABootstrapConfig
) => (initialSourceFile: string) => void
interface Logger {
log: (...args: any[]) => void
error: (...args: any[]) => void
groupCollapsed: (...args: any[]) => void
groupEnd: (...args: any[]) => void
log: (...args: any[]) => void
error: (...args: any[]) => void
groupCollapsed: (...args: any[]) => void
groupEnd: (...args: any[]) => void
}
+1 -1
View File
@@ -2,4 +2,4 @@ import { BROWSER } from 'esm-env'
export function isCloudHosted(): boolean {
return BROWSER && window.location.hostname == 'app.windmill.dev'
}
}
+14 -4
View File
@@ -1,12 +1,22 @@
<script lang="ts">
import Tooltip from './Tooltip.svelte'
export let twBgColor = 'bg-blue-200'
export let twTextColor = 'text-secondary'
export let tooltip: string | undefined = undefined
interface Props {
twBgColor?: string;
twTextColor?: string;
tooltip?: string | undefined;
children?: import('svelte').Snippet;
}
let {
twBgColor = 'bg-blue-200',
twTextColor = 'text-secondary',
tooltip = undefined,
children
}: Props = $props();
</script>
<span class="{twBgColor} {twTextColor} text-2xs rounded px-1 whitespace-nowrap">
<slot />
{@render children?.()}
{#if tooltip && tooltip != ''}
<Tooltip>{tooltip}</Tooltip>
{/if}
@@ -2,11 +2,17 @@
import { ExternalLink } from 'lucide-svelte'
import { twMerge } from 'tailwind-merge'
export let link: string | undefined = undefined
interface Props {
link?: string | undefined
class?: string
children?: import('svelte').Snippet
}
let { link = undefined, class: c = '', children }: Props = $props()
</script>
<div class={twMerge('text-xs text-primary font-normal', $$props.class)}>
<slot />
<div class={twMerge('text-xs text-primary font-normal', c)}>
{@render children?.()}
{#if link}
<a href={link} target="_blank" class="whitespace-nowrap"
>Learn more <ExternalLink size={12} class="inline-block" /></a
@@ -4,9 +4,13 @@
import { Hourglass } from 'lucide-svelte'
import WaitTimeWarning from './common/waitTimeWarning/WaitTimeWarning.svelte'
export let duration_ms: number
export let self_wait_time_ms: number | undefined = undefined
export let aggregate_wait_time_ms: number | undefined = undefined
interface Props {
duration_ms: number;
self_wait_time_ms?: number | undefined;
aggregate_wait_time_ms?: number | undefined;
}
let { duration_ms, self_wait_time_ms = undefined, aggregate_wait_time_ms = undefined }: Props = $props();
</script>
<div>
@@ -3,7 +3,11 @@
import FieldHeader from './FieldHeader.svelte'
export let schema: Schema | { [key: string]: unknown } | undefined
interface Props {
schema: Schema | { [key: string]: unknown } | undefined;
}
let { schema }: Props = $props();
</script>
<ul class="my-2">
@@ -8,7 +8,12 @@
let divEl: HTMLDivElement | null = null
let editor: meditor.IStandaloneCodeEditor
export let code: string = ''
interface Props {
code?: string
class?: string
}
let { code = '', class: c = '' }: Props = $props()
async function loadMonaco() {
editor = meditor.create(divEl as HTMLDivElement, {
@@ -43,4 +48,4 @@
})
</script>
<div bind:this={divEl} class="{$$props.class ?? ''} editor"></div>
<div bind:this={divEl} class="{c ?? ''} editor"></div>
@@ -100,6 +100,8 @@
}
const lang = $derived(highlightLanguage ?? getLang(language))
// Cast needed: svelte-highlight types not updated for Svelte 5 snippets
const HighlightWithSnippet = Highlight as any
</script>
<HighlightTheme />
@@ -134,9 +136,11 @@
{#if !lines}
<Highlight class="nowrap {className}" language={lang} {code} />
{:else}
<Highlight class="nowrap {className}" language={lang} {code} let:highlighted>
<LineNumbers {highlighted} />
</Highlight>
<HighlightWithSnippet class="nowrap {className}" language={lang} {code} >
{#snippet children({ highlighted })}
<LineNumbers {highlighted} />
{/snippet}
</HighlightWithSnippet>
{/if}
{:else}
<pre class="overflow-auto max-h-screen text-xs {className}"
@@ -7,8 +7,12 @@
import Cell from './table/Cell.svelte'
import Row from './table/Row.svelte'
export let inputTransforms: Record<string, InputTransform>
$: entries = Object.entries(inputTransforms)
interface Props {
inputTransforms: Record<string, InputTransform>;
}
let { inputTransforms }: Props = $props();
let entries = $derived(Object.entries(inputTransforms))
</script>
{#if entries.length}
@@ -5,9 +5,13 @@
import { displayDate } from '$lib/utils'
import { Hourglass } from 'lucide-svelte'
export let type: FlowStatusModule['type']
export let scheduled_for: Date | undefined
export let skipped: boolean = false
interface Props {
type: FlowStatusModule['type'];
scheduled_for: Date | undefined;
skipped?: boolean;
}
let { type, scheduled_for, skipped = false }: Props = $props();
</script>
{#if type == 'WaitingForEvents'}
+19 -7
View File
@@ -1,11 +1,23 @@
<script lang="ts">
import Tooltip from './Tooltip.svelte'
export let title: string
export let tooltip: string = ''
export let documentationLink: string | undefined = undefined
export let primary: boolean = true
export let childrenWrapperDivClasses: string = ''
interface Props {
title: string;
tooltip?: string;
documentationLink?: string | undefined;
primary?: boolean;
childrenWrapperDivClasses?: string;
children?: import('svelte').Snippet;
}
let {
title,
tooltip = '',
documentationLink = undefined,
primary = true,
childrenWrapperDivClasses = '',
children
}: Props = $props();
</script>
<div class="flex flex-row flex-wrap justify-between items-center pb-2 my-4 mr-2 min-h-16">
@@ -31,9 +43,9 @@
</span>
{/if}
{#if $$slots.default}
{#if children}
<div class="my-2 {childrenWrapperDivClasses}">
<slot />
{@render children?.()}
</div>
{/if}
</div>
@@ -79,12 +79,14 @@
<p class="text-primary text-sm">No permission changes recorded yet</p>
{:else}
<TableCustom>
<tr slot="header-row">
<th>Changed By</th>
<th>Change Type</th>
<th>Affected</th>
<th>Date</th>
</tr>
{#snippet header_row()}
<tr>
<th>Changed By</th>
<th>Change Type</th>
<th>Affected</th>
<th>Date</th>
</tr>
{/snippet}
{#snippet body()}
<tbody>
{#each history as change}
+3 -1
View File
@@ -1,3 +1,5 @@
const SIDE = ['auto', 'top', 'bottom', 'left', 'right'] as const
const ALIGN = ['start', 'end'] as const
export type PopoverPlacement = `${typeof SIDE[number]}` | `${typeof SIDE[number]}-${typeof ALIGN[number]}`
export type PopoverPlacement =
| `${(typeof SIDE)[number]}`
| `${(typeof SIDE)[number]}-${(typeof ALIGN)[number]}`
+9 -4
View File
@@ -1,12 +1,17 @@
<script lang="ts">
export let required: boolean
export let detail = ''
interface Props {
required: boolean
detail?: string
class?: string
}
let { required, detail = '', class: c = '' }: Props = $props()
</script>
{#if required}
<span class="text-red-500 dark:text-red-400 text-sm font-normal {$$props.class}">*</span>
<span class="text-red-500 dark:text-red-400 text-sm font-normal {c}">*</span>
{:else if detail || detail != ''}
<span class="text-sm text-primary ml-2 font-normal {$$props.class}"
<span class="text-sm text-primary ml-2 font-normal {c}"
>({detail != '' ? `${detail}` : ''})</span
>
{/if}
@@ -2,7 +2,11 @@
import type { SchemaProperty } from '$lib/common'
import Badge from './common/badge/Badge.svelte'
export let property: SchemaProperty
interface Props {
property: SchemaProperty;
}
let { property }: Props = $props();
</script>
<div class="flex flex-row flex-wrap gap-1">
+14 -9
View File
@@ -2,14 +2,19 @@
import { onMount, onDestroy } from 'svelte'
import { twMerge } from 'tailwind-merge'
let isAtBottom: boolean = false
let isScrollable = false
let isAtBottom: boolean = $state(false)
let isScrollable = $state(false)
export let id: string | null | undefined = undefined
export let scrollableClass: string = ''
export let shiftedShadow: boolean = false
interface Props {
id?: string | null | undefined
scrollableClass?: string
shiftedShadow?: boolean
children?: import('svelte').Snippet
}
let { id = undefined, scrollableClass = '', shiftedShadow = false, children }: Props = $props()
let mutationObserver: MutationObserver
let el: HTMLDivElement
let el: HTMLDivElement | undefined = $state()
function handleScroll(event) {
const scrollableElement = event.target
@@ -33,7 +38,7 @@
}
export function scrollIntoView(top: number) {
el.scrollTo({ top, behavior: 'smooth' })
el?.scrollTo({ top, behavior: 'smooth' })
}
onMount(() => {
observeScrollability(el)
@@ -45,8 +50,8 @@
</script>
<div {id} class={twMerge('relative pb-1', scrollableClass)}>
<div bind:this={el} on:scroll={handleScroll} class="w-full h-full overflow-y-auto">
<slot />
<div bind:this={el} onscroll={handleScroll} class="w-full h-full overflow-y-auto">
{@render children?.()}
</div>
{#if !isAtBottom && isScrollable}
<div
+6 -2
View File
@@ -2,8 +2,12 @@
import { Pen } from 'lucide-svelte'
import TextInput from './text_input/TextInput.svelte'
export let value: string
export let disabled = false
interface Props {
value: string;
disabled?: boolean;
}
let { value = $bindable(), disabled = false }: Props = $props();
function blur(e: KeyboardEvent) {
e.key === 'Enter' && (e?.target as any)?.blur()
+26 -12
View File
@@ -1,37 +1,51 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
interface Props {
paginated?: boolean
currentPage?: number
showNext?: boolean
class?: string
header_row?: import('svelte').Snippet
body?: import('svelte').Snippet
onNext?: () => void
onPrevious?: () => void
}
export let paginated = false
export let currentPage = 1
export let showNext = true
const dispatch = createEventDispatcher()
let {
paginated = false,
currentPage = 1,
showNext = true,
class: className = '',
header_row,
body,
onNext,
onPrevious
}: Props = $props()
</script>
<!-- A custom table
<!-- A custom table
- the first slot should be a <tr>, containing th elements
- the second slot should be a <tbody>, containing th elements
-->
<div class="flex flex-col {$$props.class} min-w-full">
<div class="flex flex-col {className} min-w-full">
<div class="inline-block min-w-full py-2 align-middle">
<table class="table-custom min-w-full table-auto divide-y">
<thead>
<slot name="header-row" />
{@render header_row?.()}
</thead>
<slot name="body" />
{@render body?.()}
</table>
</div>
{#if paginated}
<div class="sticky flex flex-row-reverse text-primary mb-6">
<button
class="ml-2 drop-shadow-md {showNext ? 'visible' : 'invisible'}"
on:click={() => dispatch('next')}
onclick={() => onNext?.()}
>
Next
</button>
<button
class="mx-2 drop-shadow-md {currentPage === 1 ? 'hidden' : ''}"
on:click={() => dispatch('previous')}
onclick={() => onPrevious?.()}
>
Previous
</button>
@@ -4,9 +4,19 @@
import { gfmPlugin } from 'svelte-exmarkdown/gfm'
import { twMerge } from 'tailwind-merge'
export let documentationLink: string | undefined = undefined
export let markdownTooltip: string | undefined = undefined
export let customBgClass: string | undefined = undefined
interface Props {
documentationLink?: string | undefined;
markdownTooltip?: string | undefined;
customBgClass?: string | undefined;
children?: import('svelte').Snippet;
}
let {
documentationLink = undefined,
markdownTooltip = undefined,
customBgClass = undefined,
children
}: Props = $props();
const plugins = [gfmPlugin()]
</script>
@@ -21,7 +31,7 @@
<Markdown md={markdownTooltip} {plugins} />
</div>
{:else}
<slot />
{@render children?.()}
{/if}
{#if documentationLink}
+1 -1
View File
@@ -3,7 +3,7 @@
import { isCloudHosted } from '$lib/cloud'
import Tooltip from './Tooltip.svelte'
let uptodate: string | undefined = undefined
let uptodate: string | undefined = $state(undefined)
async function loadVersion() {
try {
+6 -2
View File
@@ -1,8 +1,12 @@
<script lang="ts">
import { urlize } from '$lib/utils'
export let text: string
$: parsed = text ? urlize(text, 'html') : ''
interface Props {
text: string;
}
let { text }: Props = $props();
let parsed = $derived(text ? urlize(text, 'html') : '')
</script>
{@html parsed}
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { SettingsService } from '$lib/gen'
let version = ''
let version = $state('')
loadVersion()
@@ -2,7 +2,7 @@
import { WorkerService } from '$lib/gen'
import { Alert } from './common'
let ips: string[] | undefined = undefined
let ips: string[] | undefined = $state(undefined)
WorkerService.listWorkers({ pingSince: 300 }).then((workers) => {
ips = [
@@ -7,23 +7,15 @@
import TimelineBar from './TimelineBar.svelte'
import type { WorkflowStatus } from '$lib/gen'
export let flow_status: Record<string, WorkflowStatus>
export let flowDone = false
interface Props {
flow_status: Record<string, WorkflowStatus>;
flowDone?: boolean;
}
$: min = Object.values(flow_status).reduce(
(a, b) => Math.min(a, b.scheduled_for ? new Date(b.scheduled_for).getTime() : Infinity),
Infinity
)
$: max = flowDone
? Object.values(flow_status).reduce(
(a, b) =>
Math.max(a, b.started_at ? new Date(b.started_at).getTime() + (b.duration_ms ?? 0) : 0),
0
)
: undefined
$: total = flowDone && max ? max - min : now - min
let { flow_status, flowDone = false }: Props = $props();
let now = getDbClockNow().getTime()
let now = $state(getDbClockNow().getTime())
let interval = setInterval((x) => {
if (!max) {
@@ -37,6 +29,18 @@
onDestroy(() => {
interval && clearInterval(interval)
})
let min = $derived(Object.values(flow_status).reduce(
(a, b) => Math.min(a, b.scheduled_for ? new Date(b.scheduled_for).getTime() : Infinity),
Infinity
))
let max = $derived(flowDone
? Object.values(flow_status).reduce(
(a, b) =>
Math.max(a, b.started_at ? new Date(b.started_at).getTime() + (b.duration_ms ?? 0) : 0),
0
)
: undefined)
let total = $derived(flowDone && max ? max - min : now - min)
</script>
{#if flow_status}
@@ -1,3 +1,3 @@
export { default as AppButton } from './AppButton.svelte'
export { default as AppForm } from './AppForm.svelte'
export { default as AppFormButton } from './AppFormButton.svelte'
export { default as AppFormButton } from './AppFormButton.svelte'
@@ -12,7 +12,7 @@ export async function executeRunnable(
id: string,
requestBody: ExecuteComponentData['requestBody'],
inlineScriptOverride?: InlineScript,
queryParams?: Record<string, any>,
queryParams?: Record<string, any>
) {
let appPath = defaultIfEmptyString(path, `u/${username ?? 'unknown'}/newapp`)
if (isRunnableByName(runnable)) {
@@ -9,7 +9,7 @@
const { app } = getContext<AppViewerContext>('AppViewerContext')
let resourceOnly: boolean = true
let resourceOnly: boolean = $state(true)
</script>
<Alert type="info" title="Configurations">
@@ -1,7 +1,7 @@
import { base } from "$lib/base"
import { workspaceStore } from "$lib/stores"
import { get } from "svelte/store"
import { base } from '$lib/base'
import { workspaceStore } from '$lib/stores'
import { get } from 'svelte/store'
export function computeSecretUrl(secretUrl: string) {
return `${window.location.origin}${base}/public/${get(workspaceStore)}/${secretUrl}`
return `${window.location.origin}${base}/public/${get(workspaceStore)}/${secretUrl}`
}
@@ -1,37 +1,35 @@
import type { StaticAppInput } from "../inputType"
import type { StaticAppInput } from '../inputType'
import { Sha256 } from '@aws-crypto/sha256-js'
export function collectStaticFields(
fields: Record<string, StaticAppInput>
) {
return Object.fromEntries(
Object.entries(fields ?? {})
.filter(([k, v]) => v.type == 'static')
.map(([k, v]) => {
return [k, v['value']]
})
)
export function collectStaticFields(fields: Record<string, StaticAppInput>) {
return Object.fromEntries(
Object.entries(fields ?? {})
.filter(([k, v]) => v.type == 'static')
.map(([k, v]) => {
return [k, v['value']]
})
)
}
export type TriggerableV2 = {
static_inputs: Record<string, any>
one_of_inputs?: Record<string, any[] | undefined>
allow_user_resources?: string[]
static_inputs: Record<string, any>
one_of_inputs?: Record<string, any[] | undefined>
allow_user_resources?: string[]
}
export async function hash(message) {
try {
const msgUint8 = new TextEncoder().encode(message) // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8) // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)) // convert buffer to byte array
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string
return hashHex
} catch {
//subtle not available, trying pure js
const hash = new Sha256()
hash.update(message ?? '')
const result = Array.from(await hash.digest())
const hex = result.map((b) => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string
return hex
}
}
try {
const msgUint8 = new TextEncoder().encode(message) // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8) // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)) // convert buffer to byte array
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string
return hashHex
} catch {
//subtle not available, trying pure js
const hash = new Sha256()
hash.update(message ?? '')
const result = Array.from(await hash.digest())
const hex = result.map((b) => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string
return hex
}
}
@@ -24,7 +24,7 @@ export const DEFAULT_CODES: Partial<
| 'snowflake'
| 'mssql'
| 'bigquery'
| 'oracledb',
| 'oracledb',
string
>
>
@@ -1,7 +1,15 @@
import type { Schema } from '$lib/common'
import { ScriptService } from '$lib/gen'
import { sendUserToast } from '$lib/toast'
import { isRunnableByName, isRunnableByPath, type AppInputs, type InlineScript, type Runnable, type RunnableByName, type CtxInput } from '../../inputType'
import {
isRunnableByName,
isRunnableByPath,
type AppInputs,
type InlineScript,
type Runnable,
type RunnableByName,
type CtxInput
} from '../../inputType'
import type { GridItem, HiddenRunnable } from '../../types'
import { fieldTypeToTsType, schemaToInputsSpec } from '../../utils'
import type { AppComponent } from '../component'
@@ -14,7 +22,11 @@ export interface AppScriptsList {
// When the schema is loaded, we need to update the inputs spec
// in order to render the inputs the component panel
// Note: fields can include CtxInput for raw apps
export function computeFields(schema: Schema, defaultUserInput: boolean, fields: AppInputs | Record<string, CtxInput | AppInputs[string]>) {
export function computeFields(
schema: Schema,
defaultUserInput: boolean,
fields: AppInputs | Record<string, CtxInput | AppInputs[string]>
) {
let schemaCopy: Schema = JSON.parse(JSON.stringify(schema))
const result = {}
@@ -33,9 +33,13 @@ export function isFrontend(runnable: Runnable): boolean {
}
export function isTriggerable(componentType: string): boolean {
return ['buttoncomponent', 'formbuttoncomponent', 'formcomponent', 'steppercomponent', 'chatcomponent'].includes(
componentType
)
return [
'buttoncomponent',
'formbuttoncomponent',
'formcomponent',
'steppercomponent',
'chatcomponent'
].includes(componentType)
}
export function isTriggerOnAppLoad(appComponent: AppComponent): boolean {
@@ -10,7 +10,6 @@ const Breakpoints = {
export const moveMode = writable<'move' | 'insert'>('move')
const WIDE_GRID_COLUMNS = 12 as const
const NARROW_GRID_COLUMNS = 3 as const
@@ -1,5 +1,5 @@
import { getRowsCount } from "./other";
import { getRowsCount } from './other'
export function getContainerHeight(items, yPerPx, cols) {
return getRowsCount(items, cols) * yPerPx;
return getRowsCount(items, cols) * yPerPx
}
@@ -1,130 +1,130 @@
@import 'tailwindcss/base';
.svelte-select {
@apply border rounded box-border h-10 relative flex items-center px-4 py-0 bg-white m-0 w-full
@apply border rounded box-border h-10 relative flex items-center px-4 py-0 bg-white m-0 w-full
hover:border-gray-400;
}
.svelte-select input {
@apply cursor-default border-none text-tertiary h-10 leading-10 px-4 py-0 bg-transparent text-sm absolute left-0 m-0 w-full
@apply cursor-default border-none text-tertiary h-10 leading-10 px-4 py-0 bg-transparent text-sm absolute left-0 m-0 w-full
focus:outline-none hover:border-gray-400;
}
.svelte-select.focused {
@apply border-blue-600;
@apply border-blue-600;
}
.svelte-select.disabled {
@apply bg-gray-200 border-gray-200 text-tertiary;
@apply bg-gray-200 border-gray-200 text-tertiary;
}
.svelte-select.disabled input {
@apply placeholder:text-gray-400 placeholder:opacity-100;
@apply placeholder:text-gray-400 placeholder:opacity-100;
}
.svelte-select .selected-item {
@apply leading-10 h-10 overflow-x-hidden pr-5
@apply leading-10 h-10 overflow-x-hidden pr-5
focus:outline-none;
}
.svelte-select .icons {
@apply absolute flex items-center right-0 translate-y-0 text-gray-200 pointer-events-none top-0 bottom-0;
@apply absolute flex items-center right-0 translate-y-0 text-gray-200 pointer-events-none top-0 bottom-0;
}
.svelte-select .icons > * {
@apply transition-colors ease-in-out duration-200;
@apply transition-colors ease-in-out duration-200;
}
.svelte-select .clear-select {
@apply pointer-events-auto;
@apply pointer-events-auto;
}
.svelte-select.focused .icons,
.svelte-select .chevron:hover,
.svelte-select .clear-select:hover {
@apply text-tertiary;
@apply text-tertiary;
}
.svelte-select .clear-select {
@apply px-2 h-5 text-gray-300 flex-none w-9;
@apply px-2 h-5 text-gray-300 flex-none w-9;
}
.svelte-select .chevron {
@apply flex pt-0 pr-2 pl-2 border-l-2 w-9 h-5 text-gray-300;
@apply flex pt-0 pr-2 pl-2 border-l-2 w-9 h-5 text-gray-300;
}
.svelte-select.multi {
@apply pr-9 pl-4 h-auto flex-wrap items-stretch;
@apply pr-9 pl-4 h-auto flex-wrap items-stretch;
}
.svelte-select.multi input {
@apply p-0 relative m-0;
@apply p-0 relative m-0;
}
.svelte-select.error {
@apply border-red-500 bg-white;
@apply border-red-500 bg-white;
}
.a11y-text {
@apply sr-only;
@apply sr-only;
}
.list {
@apply shadow-md rounded-sm max-h-64 overflow-y-auto bg-white border-none absolute z-10 w-full left-0 right-0;
@apply shadow-md rounded-sm max-h-64 overflow-y-auto bg-white border-none absolute z-10 w-full left-0 right-0;
}
.list .list-group-title {
@apply text-gray-400 cursor-default text-sm font-medium h-10 leading-10 px-5 overflow-ellipsis whitespace-nowrap uppercase;
@apply text-gray-400 cursor-default text-sm font-medium h-10 leading-10 px-5 overflow-ellipsis whitespace-nowrap uppercase;
}
.list .empty {
@apply text-center py-5 text-tertiary;
@apply text-center py-5 text-tertiary;
}
.item {
@apply cursor-default h-10 leading-10 px-5 text-gray-800 overflow-ellipsis overflow-hidden whitespace-nowrap;
@apply cursor-default h-10 leading-10 px-5 text-gray-800 overflow-ellipsis overflow-hidden whitespace-nowrap;
}
.item.group-item {
@apply px-10;
@apply px-10;
}
.item:active {
@apply bg-blue-200;
@apply bg-blue-200;
}
.item.active {
@apply bg-blue-600 text-white;
@apply bg-blue-600 text-white;
}
.item.not-selectable {
@apply text-gray-300;
@apply text-gray-300;
}
.item.first {
@apply rounded-t-sm;
@apply rounded-t-sm;
}
.item.hover:not(.active) {
@apply bg-blue-100;
@apply bg-blue-100;
}
.multi input {
flex: 1 1 40px;
flex: 1 1 40px;
}
.multi-item {
@apply bg-gray-100 mt-1 border border-gray-200 rounded-sm h-8 leading-8 flex cursor-default pr-1 pl-1 max-w-full items-center mr-1 overflow-hidden overflow-ellipsis whitespace-nowrap;
@apply bg-gray-100 mt-1 border border-gray-200 rounded-sm h-8 leading-8 flex cursor-default pr-1 pl-1 max-w-full items-center mr-1 overflow-hidden overflow-ellipsis whitespace-nowrap;
}
.multi-item.disabled {
@apply hover:bg-gray-300 hover:text-tertiary;
@apply hover:bg-gray-300 hover:text-tertiary;
}
.multi-item-clear {
@apply flex items-center justify-center w-5;
@apply flex items-center justify-center w-5;
}
.list-item {
@apply list-none;
}
@apply list-none;
}
+10 -6
View File
@@ -2,7 +2,14 @@ import type { Schema } from '$lib/common'
import { twMerge } from 'tailwind-merge'
import { type AppComponent } from './editor/component'
import { isRunnableByName, isRunnableByPath, type AppInput, type InputType, type ResultAppInput, type StaticAppInput } from './inputType'
import {
isRunnableByName,
isRunnableByPath,
type AppInput,
type InputType,
type ResultAppInput,
type StaticAppInput
} from './inputType'
import type { Output } from './rx'
import type {
App,
@@ -133,7 +140,7 @@ export function isScriptByNameDefined(appInput: AppInput | undefined): boolean {
return false
}
if (appInput.type === 'runnable' && isRunnableByName(appInput.runnable)) {
if (appInput.type === 'runnable' && isRunnableByName(appInput.runnable)) {
return appInput.runnable?.name != undefined
}
@@ -402,10 +409,7 @@ export function getAllScriptNames(app: App): string[] {
const names = (allItems(app.grid, app?.subgrids) ?? []).reduce((acc, gridItem: GridItem) => {
const { componentInput } = gridItem.data
if (
componentInput?.type === 'runnable' &&
isRunnableByName(componentInput.runnable)
) {
if (componentInput?.type === 'runnable' && isRunnableByName(componentInput.runnable)) {
acc.push(componentInput.runnable.name)
}
@@ -90,6 +90,7 @@ export function formatAssetKind(asset: {
case 'datatable':
return 'Data table'
}
return 'Unknown'
}
export function formatAssetAccessType(accessType: AssetUsageAccessType | undefined) {
@@ -1,6 +1,10 @@
<script lang="ts">
export let notificationCount = 0
export let notificationLimit: number | undefined = undefined
interface Props {
notificationCount?: number;
notificationLimit?: number | undefined;
}
let { notificationCount = 0, notificationLimit = undefined }: Props = $props();
</script>
{#if notificationCount > 0}
@@ -1,12 +1,26 @@
<script lang="ts">
import AnimatedButtonInner from './AnimatedButtonInner.svelte'
export let marginWidth = '2px'
export let animationDuration = '2s'
export let baseRadius = '4px'
export let animate = true
export let wrapperClasses = ''
export let ringColor = 'transparent'
export let darkMode = false
interface Props {
marginWidth?: string;
animationDuration?: string;
baseRadius?: string;
animate?: boolean;
wrapperClasses?: string;
ringColor?: string;
darkMode?: boolean;
children?: import('svelte').Snippet;
}
let {
marginWidth = '2px',
animationDuration = '2s',
baseRadius = '4px',
animate = true,
wrapperClasses = '',
ringColor = 'transparent',
darkMode = false,
children
}: Props = $props();
</script>
{#if animate}
@@ -19,10 +33,10 @@
{ringColor}
{darkMode}
>
<slot />
{@render children?.()}
</AnimatedButtonInner>
{:else}
<div class={wrapperClasses}>
<slot />
{@render children?.()}
</div>
{/if}
@@ -1,27 +1,41 @@
<script lang="ts">
import { twMerge } from 'tailwind-merge'
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
export let marginWidth = '2px'
export let animationDuration = '2s'
export let baseRadius = '4px'
export let animate = true
export let wrapperClasses = ''
export let ringColor = 'transparent'
export let darkMode = false
interface Props {
marginWidth?: string;
animationDuration?: string;
baseRadius?: string;
animate?: boolean;
wrapperClasses?: string;
ringColor?: string;
darkMode?: boolean;
children?: import('svelte').Snippet;
}
let {
marginWidth = '2px',
animationDuration = '2s',
baseRadius = '4px',
animate = true,
wrapperClasses = '',
ringColor = 'transparent',
darkMode = $bindable(false),
children
}: Props = $props();
const gradientColors = {
light: ['#d6e5ff', '#0073ff', '#5aa2fa', '#0272fa', '#d6e5ff'],
dark: ['#0469db', '#15498a', '#031ea3', '#0073ff', '#0469db']
}
let clientWidth = 0
let clientHeight = 0
let clientWidth = $state(0)
let clientHeight = $state(0)
$: circleRadius = Math.ceil(
let circleRadius = $derived(Math.ceil(
Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight) / 2
)
))
$: gradientString = `from 0deg, ${gradientColors[darkMode ? 'dark' : 'light'].join(', ')}`
let gradientString = $derived(`from 0deg, ${gradientColors[darkMode ? 'dark' : 'light'].join(', ')}`)
</script>
<DarkModeObserver bind:darkMode />
@@ -33,7 +47,7 @@
bind:clientWidth
bind:clientHeight
>
<slot />
{@render children?.()}
</div>
<style>
@@ -1,10 +1,20 @@
<script>
export let pulseDuration = 2
export let numberOfPulses = 3
export let scale = 1.2
<script lang="ts">
interface Props {
pulseDuration?: number;
numberOfPulses?: number;
scale?: number;
children?: import('svelte').Snippet;
}
let isPulsing = false
let pulseCount = 1
let {
pulseDuration = 2,
numberOfPulses = 3,
scale = 1.2,
children
}: Props = $props();
let isPulsing = $state(false)
let pulseCount = $state(1)
export function triggerPulse(count) {
if (pulseCount < 1) return
@@ -20,7 +30,7 @@
<div class="relative">
<div>
<slot />
{@render children?.()}
</div>
{#each Array(numberOfPulses) as _, i}
<div
@@ -1,13 +1,18 @@
<script lang="ts">
export let color: 'light' | 'dark' | undefined = 'light'
export let border: boolean = false
interface Props {
color?: 'light' | 'dark' | undefined;
border?: boolean;
children?: import('svelte').Snippet;
}
$: colorClasses = {
let { color = 'light', border = false, children }: Props = $props();
let colorClasses = $derived({
light: 'text-primary bg-surface hover:bg-surface-hover text-primary',
dark: 'text-primary hover:bg-surface-hover-dark text-primary'
}[color ?? 'light']
}[color ?? 'light'])
</script>
<div class="rounded-full p-1 {colorClasses} {border ? 'border border-tertiary' : ''}">
<slot />
{@render children?.()}
</div>
@@ -1,8 +1,8 @@
import type { Value } from "$lib/utils"
import type { Value } from '$lib/utils'
export type GetInitialAndModifiedValues = (() => SavedAndModifiedValue) | undefined
export type SavedAndModifiedValue = {
savedValue: Value | undefined
modifiedValue: Value | undefined
}
savedValue: Value | undefined
modifiedValue: Value | undefined
}
@@ -42,4 +42,3 @@ export const CONTEXT_MENU_DIVIDER_CLASS = 'my-1 h-px bg-border-light'
*/
export const CONTEXT_MENU_ANIMATION_CLASSES =
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'
@@ -1,5 +1,4 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { FileUp, Trash } from 'lucide-svelte'
import Button from '../../common/button/Button.svelte'
import { twMerge } from 'tailwind-merge'
@@ -8,24 +7,47 @@
type ConvertedFile = string | ArrayBuffer | null
let c = ''
export { c as class }
export let style = ''
export let accept = '*'
export let multiple = false
export let convertTo: ReadFileAs | undefined = undefined
export let hideIcon = false
export let iconSize = 24
export let returnFileNames = false
export let submittedText: string | undefined = undefined
export let defaultFile: string | string[] | undefined = undefined
export let disabled: boolean | undefined = undefined
export let folderOnly = false
const dispatch = createEventDispatcher()
let input: HTMLInputElement
type FileWithPath = File & { path?: string }
export let files: FileWithPath[] | undefined = undefined
interface Props {
class?: string
style?: string
accept?: string
multiple?: boolean
convertTo?: ReadFileAs | undefined
hideIcon?: boolean
iconSize?: number
returnFileNames?: boolean
submittedText?: string | undefined
defaultFile?: string | string[] | undefined
disabled?: boolean | undefined
folderOnly?: boolean
files?: FileWithPath[] | undefined
selected_title?: import('svelte').Snippet
children?: import('svelte').Snippet
onChange?: (detail: any) => void
}
let {
class: c = '',
style = '',
accept = '*',
multiple = false,
convertTo = undefined,
hideIcon = false,
iconSize = 24,
returnFileNames = false,
submittedText = undefined,
defaultFile = undefined,
disabled = undefined,
folderOnly = false,
files = $bindable(undefined),
selected_title,
children,
onChange
}: Props = $props()
let input: HTMLInputElement
let pointerStartX = 0
let pointerStartY = 0
@@ -35,10 +57,10 @@
pointerStartY = e.clientY
}
async function onChange(fileList: FileWithPath[] | null) {
async function handleFileChange(fileList: FileWithPath[] | null) {
if (!fileList || !fileList.length) {
files = undefined
dispatch('change', files)
onChange?.(files)
return
}
@@ -98,14 +120,14 @@
dirEntry: FileSystemDirectoryEntry,
path: string
): Promise<FileWithPath[]> {
const files: FileWithPath[] = []
const filesArr: FileWithPath[] = []
const dirReader = dirEntry.createReader()
async function readEntries() {
return new Promise<FileWithPath[]>((resolve) => {
dirReader.readEntries(async (entries) => {
if (entries.length === 0) {
resolve(files)
resolve(filesArr)
return
}
@@ -113,7 +135,7 @@
return traverseFileTree(entry, path + dirEntry.name + '/')
})
const nestedFiles = await Promise.all(filePromises)
files.push(...nestedFiles.flat())
filesArr.push(...nestedFiles.flat())
// readEntries only return up to 100 files
// continue reading if more files exist
resolve(await readEntries())
@@ -141,8 +163,8 @@
if (folderOnly) {
const item = event.dataTransfer.items[0]?.webkitGetAsEntry()
if (item) {
const files = await traverseFileTree(item, '')
onChange(files)
const droppedFiles = await traverseFileTree(item, '')
handleFileChange(droppedFiles)
}
} else {
if (event.dataTransfer.files && event.dataTransfer.files.length) {
@@ -150,7 +172,7 @@
sendUserToast('Only one file can be uploaded at a time')
return
} else {
onChange(Array.from(event.dataTransfer.files))
handleFileChange(Array.from(event.dataTransfer.files))
}
}
}
@@ -173,9 +195,9 @@
if (returnFileNames) {
converted = converted.map((c, i) => ({ name: files![i].name, data: c }))
}
dispatch('change', converted)
onChange?.(converted)
} else {
dispatch('change', files)
onChange?.(files)
}
}
@@ -194,10 +216,10 @@
duration-200 px-1 py-8`,
c
)}
on:dragover={handleDragOver}
on:drop={handleDrop}
on:pointerdown={handlePointerDown}
on:click={(e) => {
ondragover={handleDragOver}
ondrop={handleDrop}
onpointerdown={handlePointerDown}
onclick={(e) => {
const deltaX = Math.abs(e.clientX - pointerStartX)
const deltaY = Math.abs(e.clientY - pointerStartY)
if (deltaX > 5 || deltaY > 5) {
@@ -214,11 +236,13 @@
{/if}
{#if files}
<div class="w-full max-h-full overflow-auto px-6">
<slot name="selected-title">
{#if selected_title}
{@render selected_title()}
{:else}
<div class="text-center mb-2 px-2">
{submittedText ? submittedText : `Selected file${files.length > 1 ? 's' : ''}`}:
</div>
</slot>
{/if}
<ul class="relative z-20 max-w-[500px] bg-surface rounded-lg overflow-hidden mx-auto">
{#each files as { name }, i}
<li
@@ -232,7 +256,7 @@
iconOnly
btnClasses="bg-transparent"
startIcon={{ icon: Trash }}
on:click={() => removeFile(i)}
onclick={() => removeFile(i)}
destructive
/>
</li>
@@ -240,9 +264,11 @@
</ul>
</div>
{:else}
<slot>
{#if children}
{@render children()}
{:else}
<span>Drag and drop {folderOnly ? 'a folder' : multiple ? 'files' : 'a file'}</span>
</slot>
{/if}
{/if}
<input
class="!absolute !inset-0 !z-10 !opacity-0 !cursor-pointer"
@@ -250,12 +276,11 @@
{...{ webkitdirectory: folderOnly }}
title={files ? `${files.length} file${files.length > 1 ? 's' : ''} chosen` : 'No file chosen'}
bind:this={input}
on:change={({ currentTarget }) => {
onChange(currentTarget.files ? Array.from(currentTarget.files) : null)
onchange={({ currentTarget }) => {
handleFileChange(currentTarget.files ? Array.from(currentTarget.files) : null)
}}
{accept}
{multiple}
{...$$restProps}
/>
{#if defaultFile && (!Array.isArray(defaultFile) || defaultFile.length > 0)}
<div class="w-full border-dashed border-t-2 text-2xs pt-1 text-primary mt-2">
@@ -1,3 +1,11 @@
<script lang="ts">
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
</script>
<div class="w-full flex-grow bg-blue-300">
<slot />
{@render children?.()}
</div>
@@ -1,10 +1,17 @@
<script lang="ts">
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
import { createEventDispatcher } from 'svelte'
interface Props {
open?: boolean;
onOpen?: () => void;
onClose?: () => void;
}
const dispatch = createEventDispatcher()
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
export let open: boolean = false
let { open = false, onOpen = undefined, onClose = undefined }: Props = $props();
$: dispatchIfMounted(open ? 'open' : 'close')
$effect(() => {
if (open) {
onOpen?.()
} else {
onClose?.()
}
});
</script>
@@ -7,13 +7,19 @@
import { getContext } from 'svelte'
import type { AppViewerContext } from '$lib/components/apps/types'
export let title: string
export let style: string = ''
export let css: any = {}
interface Props {
title: string
style?: string
css?: any
class?: string
children?: import('svelte').Snippet
}
let { title, style = '', css = {}, class: c = '', children }: Props = $props()
const { mode } = getContext<AppViewerContext>('AppViewerContext')
let isOpen = false
let isOpen = $state(false)
export function close() {
isOpen = false
@@ -41,16 +47,13 @@
css?.popup?.class,
'wm-modal-form-popup'
)}
use:clickOutside
on:click_outside={() => {
close()
}}
use:clickOutside={{ onClickOutside: () => close() }}
>
<div class="px-4 py-2 border-b flex justify-between items-center">
<div>{title}</div>
<div class="w-8">
<button
on:click={() => {
onclick={() => {
isOpen = false
}}
class="hover:bg-surface-hover bg-surface-secondary rounded-full w-8 h-8 flex items-center justify-center transition-all"
@@ -60,17 +63,17 @@
</div>
</div>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="relative bg-surface rounded-md" on:click|stopPropagation={() => {}}>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="relative bg-surface rounded-md" onclick={(e) => e.stopPropagation()}>
<div
class={twMerge(
'max-w-screen-lg max-h-screen-80 overflow-auto flex flex-col',
$$props.class
c
)}
{style}
>
<slot />
{@render children?.()}
</div>
</div>
</div>
@@ -69,7 +69,7 @@
}
]
}}
on:open={() => {
onOpen={() => {
menuOpen = true
}}
/>
@@ -18,33 +18,36 @@
Unplug
} from 'lucide-svelte'
export let kind:
| 'script'
| 'flow'
| 'app'
| 'raw_app'
| 'resource'
| 'variable'
| 'resource_type'
| 'folder'
| 'schedule'
| 'trigger'
| 'routes'
| 'schedules'
| 'websockets'
| 'postgres'
| 'kafka'
| 'nats'
| 'mqtt'
| 'sqs'
| 'gcp'
| 'emails'
interface Props {
kind:
| 'script'
| 'flow'
| 'app'
| 'raw_app'
| 'resource'
| 'variable'
| 'resource_type'
| 'folder'
| 'schedule'
| 'trigger'
| 'routes'
| 'schedules'
| 'websockets'
| 'postgres'
| 'kafka'
| 'nats'
| 'mqtt'
| 'sqs'
| 'gcp'
| 'emails'
/** For 'trigger' kind, specifies the specific trigger type (routes, schedules, etc.) */
triggerKind?: string | undefined
}
/** For 'trigger' kind, specifies the specific trigger type (routes, schedules, etc.) */
export let triggerKind: string | undefined = undefined
let { kind, triggerKind = undefined }: Props = $props()
// Use triggerKind if kind is 'trigger' and triggerKind is provided
$: effectiveKind = kind === 'trigger' && triggerKind ? triggerKind : kind
let effectiveKind = $derived(kind === 'trigger' && triggerKind ? triggerKind : kind)
</script>
<div class="flex justify-center items-center" title={effectiveKind}>
@@ -1,8 +1,16 @@
<script lang="ts">
interface Props {
children?: import('svelte').Snippet;
}
let { children }: Props = $props();
</script>
<div class="flex flex-col">
<div class="overflow-hidden rounded-md border">
<table class="min-w-full divide-y divide-gray-300">
<tbody class="divide-y bg-surface">
<slot />
{@render children?.()}
</tbody>
</table>
</div>
@@ -5,7 +5,11 @@
import CodeDisplay from './script/CodeDisplay.svelte'
import LinkRenderer from './LinkRenderer.svelte'
export let message: DisplayMessage
interface Props {
message: DisplayMessage;
}
let { message }: Props = $props();
</script>
<div
@@ -12,39 +12,43 @@
).length < 2}
class="max-w-full"
>
<svelte:fragment slot="trigger">
<div
class="text-primary text-xs flex flex-row items-center font-normal gap-0.5 border px-1 rounded-lg"
>
<span class={`truncate`}>
{aiChatManager.mode.charAt(0).toUpperCase() + aiChatManager.mode.slice(1)} mode
</span>
{#if Object.keys(aiChatManager.allowedModes).filter((k) => aiChatManager.allowedModes[k]).length > 1}
<div class="shrink-0">
<ChevronDown size={16} />
</div>
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="content" let:close>
<div class="flex flex-col gap-1 p-1 min-w-24">
{#each Object.values(AIMode) as possibleMode}
{#if aiChatManager.allowedModes[possibleMode]}
<button
class={twMerge(
'text-left text-xs hover:bg-surface-hover rounded-md p-1 font-normal',
aiChatManager.mode === possibleMode && 'bg-surface-hover'
)}
onclick={() => {
aiChatManager.changeMode(possibleMode)
close()
}}
>
{possibleMode.charAt(0).toUpperCase() + possibleMode.slice(1)} mode
</button>
{#snippet trigger()}
<div
class="text-primary text-xs flex flex-row items-center font-normal gap-0.5 border px-1 rounded-lg"
>
<span class={`truncate`}>
{aiChatManager.mode.charAt(0).toUpperCase() + aiChatManager.mode.slice(1)} mode
</span>
{#if Object.keys(aiChatManager.allowedModes).filter((k) => aiChatManager.allowedModes[k]).length > 1}
<div class="shrink-0">
<ChevronDown size={16} />
</div>
{/if}
{/each}
</div>
</svelte:fragment>
</div>
{/snippet}
{#snippet content({ close })}
<div class="flex flex-col gap-1 p-1 min-w-24">
{#each Object.values(AIMode) as possibleMode}
{#if aiChatManager.allowedModes[possibleMode]}
<button
class={twMerge(
'text-left text-xs hover:bg-surface-hover rounded-md p-1 font-normal',
aiChatManager.mode === possibleMode && 'bg-surface-hover'
)}
onclick={() => {
aiChatManager.changeMode(possibleMode)
close()
}}
>
{possibleMode.charAt(0).toUpperCase() + possibleMode.slice(1)} mode
</button>
{/if}
{/each}
</div>
{/snippet}
</Popover>
</div>
@@ -30,7 +30,10 @@ export function createAppEvalHelpers(
let frontend: Record<string, string> = { ...initialFrontend }
let backend: Record<string, BackendRunnable> = { ...initialBackend }
let snapshotId = 0
const snapshots: Map<number, { frontend: Record<string, string>; backend: Record<string, BackendRunnable> }> = new Map()
const snapshots: Map<
number,
{ frontend: Record<string, string>; backend: Record<string, BackendRunnable> }
> = new Map()
const helpers: AppAIChatHelpers = {
// Frontend file operations
@@ -126,11 +129,7 @@ export function createAppEvalHelpers(
return { success: true, result: [] }
},
addTableToWhitelist: (
_datatableName: string,
_schemaName: string,
_tableName: string
) => {
addTableToWhitelist: (_datatableName: string, _schemaName: string, _tableName: string) => {
// No-op for eval testing - tables are not tracked in test context
}
}
@@ -156,9 +156,7 @@ export async function loadAppFixture(fixturePath: string): Promise<AppFiles> {
* Loads an app fixture and returns the separate frontend and backend objects.
* Convenience function for use with runAppEval options.
*/
export async function loadAppFixtureForEval(
fixturePath: string
): Promise<{
export async function loadAppFixtureForEval(fixturePath: string): Promise<{
initialFrontend: Record<string, string>
initialBackend: Record<string, BackendRunnable>
}> {
@@ -11,11 +11,46 @@ interface FileItem {
const mockFiles: FileItem[] = [
{ id: 'f1', name: 'Documents', type: 'folder', modifiedAt: '2024-01-15', parentId: null },
{ id: 'f2', name: 'Images', type: 'folder', modifiedAt: '2024-01-10', parentId: null },
{ id: 'f3', name: 'readme.txt', type: 'file', size: 1024, modifiedAt: '2024-01-20', parentId: null },
{ id: 'f4', name: 'report.pdf', type: 'file', size: 52400, modifiedAt: '2024-01-18', parentId: 'f1' },
{ id: 'f5', name: 'notes.txt', type: 'file', size: 256, modifiedAt: '2024-01-12', parentId: 'f1' },
{ id: 'f6', name: 'photo1.jpg', type: 'file', size: 2048000, modifiedAt: '2024-01-08', parentId: 'f2' },
{ id: 'f7', name: 'photo2.jpg', type: 'file', size: 1536000, modifiedAt: '2024-01-09', parentId: 'f2' },
{
id: 'f3',
name: 'readme.txt',
type: 'file',
size: 1024,
modifiedAt: '2024-01-20',
parentId: null
},
{
id: 'f4',
name: 'report.pdf',
type: 'file',
size: 52400,
modifiedAt: '2024-01-18',
parentId: 'f1'
},
{
id: 'f5',
name: 'notes.txt',
type: 'file',
size: 256,
modifiedAt: '2024-01-12',
parentId: 'f1'
},
{
id: 'f6',
name: 'photo1.jpg',
type: 'file',
size: 2048000,
modifiedAt: '2024-01-08',
parentId: 'f2'
},
{
id: 'f7',
name: 'photo2.jpg',
type: 'file',
size: 1536000,
modifiedAt: '2024-01-09',
parentId: 'f2'
},
{ id: 'f8', name: 'Projects', type: 'folder', modifiedAt: '2024-01-05', parentId: 'f1' }
]
@@ -46,9 +46,7 @@ export const FileItem: React.FC<FileItemProps> = ({ item, onDelete, onRename, on
autoFocus
/>
) : (
<span
className={item.type === 'folder' ? 'cursor-pointer hover:text-blue-600' : ''}
>
<span className={item.type === 'folder' ? 'cursor-pointer hover:text-blue-600' : ''}>
{item.name}
</span>
)}
@@ -67,10 +65,7 @@ export const FileItem: React.FC<FileItemProps> = ({ item, onDelete, onRename, on
>
Rename
</button>
<button
onClick={() => onDelete(item)}
className="text-red-500 hover:text-red-700 text-sm"
>
<button onClick={() => onDelete(item)} className="text-red-500 hover:text-red-700 text-sm">
Delete
</button>
</div>
@@ -11,9 +11,7 @@ interface FileListProps {
export const FileList: React.FC<FileListProps> = ({ files, onDelete, onRename, onFolderOpen }) => {
if (files.length === 0) {
return (
<div className="text-center text-gray-500 py-8">This folder is empty</div>
)
return <div className="text-center text-gray-500 py-8">This folder is empty</div>
}
// Sort: folders first, then files
@@ -1,134 +1,134 @@
{
"summary": "",
"value": {
"modules": [
{
"id": "fetch_users",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n return [\n { id: 1, name: \"Alice\", role: \"admin\", active: true },\n { id: 2, name: \"Bob\", role: \"user\", active: false },\n { id: 4, name: \"Dana\", role: \"moderator\", active: true },\n { id: 3, name: \"Charlie\", role: \"user\", active: true },\n ];\n}",
"input_transforms": {}
}
},
{
"id": "filter_active_users",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(users) {\n return users.filter(user => user.active);\n}",
"input_transforms": {
"users": {
"type": "javascript",
"expr": "results.fetch_users"
}
}
}
},
{
"id": "loop_users",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.filter_active_users"
},
"skip_failures": false,
"modules": [
{
"id": "branch_user_role",
"value": {
"type": "branchone",
"branches": [
{
"summary": "Admin Action",
"expr": "flow_input.iter.value.role === 'admin'",
"modules": [
{
"id": "admin_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) {\n return `Admin action taken for ${user.name}`;\n}",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "User Action",
"expr": "flow_input.iter.value.role === 'user'",
"modules": [
{
"id": "user_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) {\n return `User action taken for ${user.name}`;\n}",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Moderator Action",
"expr": "flow_input.iter.value.role === 'moderator'",
"modules": [
{
"id": "moderator_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) { return `Moderator action taken for ${user.name}`; }",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
],
"default": []
}
}
],
"parallel": false,
"squash": false
}
},
{
"id": "return_actions",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(actions) {\n return actions;\n}",
"input_transforms": {
"actions": {
"type": "javascript",
"expr": "results.loop_users"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
"summary": "",
"value": {
"modules": [
{
"id": "fetch_users",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n return [\n { id: 1, name: \"Alice\", role: \"admin\", active: true },\n { id: 2, name: \"Bob\", role: \"user\", active: false },\n { id: 4, name: \"Dana\", role: \"moderator\", active: true },\n { id: 3, name: \"Charlie\", role: \"user\", active: true },\n ];\n}",
"input_transforms": {}
}
},
{
"id": "filter_active_users",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(users) {\n return users.filter(user => user.active);\n}",
"input_transforms": {
"users": {
"type": "javascript",
"expr": "results.fetch_users"
}
}
}
},
{
"id": "loop_users",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.filter_active_users"
},
"skip_failures": false,
"modules": [
{
"id": "branch_user_role",
"value": {
"type": "branchone",
"branches": [
{
"summary": "Admin Action",
"expr": "flow_input.iter.value.role === 'admin'",
"modules": [
{
"id": "admin_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) {\n return `Admin action taken for ${user.name}`;\n}",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "User Action",
"expr": "flow_input.iter.value.role === 'user'",
"modules": [
{
"id": "user_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) {\n return `User action taken for ${user.name}`;\n}",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Moderator Action",
"expr": "flow_input.iter.value.role === 'moderator'",
"modules": [
{
"id": "moderator_action",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(user) { return `Moderator action taken for ${user.name}`; }",
"input_transforms": {
"user": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
],
"default": []
}
}
],
"parallel": false,
"squash": false
}
},
{
"id": "return_actions",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(actions) {\n return actions;\n}",
"input_transforms": {
"actions": {
"type": "javascript",
"expr": "results.loop_users"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
@@ -1,183 +1,183 @@
{
"summary": "E-commerce Order Processing Pipeline",
"value": {
"modules": [
{
"id": "validate_order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(items: { name: string; price: number; quantity: number }[]) {\n const invalid = items.filter(item => item.price <= 0 || item.quantity <= 0);\n return { valid: invalid.length === 0, invalidItems: invalid };\n}",
"input_transforms": {
"items": {
"type": "javascript",
"expr": "flow_input.items"
}
}
}
},
{
"id": "calculate_total",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(items: { name: string; price: number; quantity: number }[]) {\n const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n const tax = subtotal * 0.08;\n return { subtotal, tax, total: subtotal + tax };\n}",
"input_transforms": {
"items": {
"type": "javascript",
"expr": "flow_input.items"
}
}
}
},
{
"id": "check_inventory",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "flow_input.items"
},
"skip_failures": false,
"parallel": true,
"modules": [
{
"id": "check_item_stock",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: { name: string; quantity: number }) {\n // Mock inventory check - items with even quantity are in stock\n const inStock = item.quantity % 2 === 0 || item.quantity < 5;\n return { name: item.name, requested: item.quantity, inStock, available: inStock ? item.quantity : 0 };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
},
{
"id": "process_inventory_result",
"value": {
"type": "branchone",
"branches": [
{
"summary": "All items in stock - create shipment",
"expr": "results.check_inventory.every(item => item.inStock)",
"modules": [
{
"id": "create_shipment",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(shipping_address: string, total: number) {\n return {\n shipment_id: `SHIP-${Date.now()}`,\n status: 'created',\n address: shipping_address,\n total,\n estimated_delivery: '3-5 business days'\n };\n}",
"input_transforms": {
"shipping_address": {
"type": "javascript",
"expr": "flow_input.shipping_address"
},
"total": {
"type": "javascript",
"expr": "results.calculate_total.total"
}
}
}
}
]
}
],
"default": [
{
"id": "create_backorder",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(inventory_results: { name: string; inStock: boolean }[]) {\n const outOfStock = inventory_results.filter(item => !item.inStock);\n return {\n backorder_id: `BO-${Date.now()}`,\n status: 'backorder',\n unavailable_items: outOfStock.map(item => item.name),\n message: 'Some items are out of stock. We will notify you when available.'\n };\n}",
"input_transforms": {
"inventory_results": {
"type": "javascript",
"expr": "results.check_inventory"
}
}
}
}
]
}
},
{
"id": "send_confirmation",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_email: string, order_result: any) {\n // Mock email sending\n return {\n email_sent: true,\n to: customer_email,\n subject: order_result.shipment_id ? 'Order Confirmed' : 'Order Update - Backorder',\n sent_at: new Date().toISOString()\n };\n}",
"input_transforms": {
"customer_email": {
"type": "javascript",
"expr": "flow_input.customer_email"
},
"order_result": {
"type": "javascript",
"expr": "results.process_inventory_result"
}
}
}
},
{
"id": "return_summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(validation: any, totals: any, order_result: any, confirmation: any) {\n return {\n order_valid: validation.valid,\n totals,\n order_status: order_result.shipment_id ? 'shipped' : 'backorder',\n order_id: order_result.shipment_id || order_result.backorder_id,\n confirmation_sent: confirmation.email_sent\n };\n}",
"input_transforms": {
"validation": {
"type": "javascript",
"expr": "results.validate_order"
},
"totals": {
"type": "javascript",
"expr": "results.calculate_total"
},
"order_result": {
"type": "javascript",
"expr": "results.process_inventory_result"
},
"confirmation": {
"type": "javascript",
"expr": "results.send_confirmation"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"items": {
"type": "array",
"description": "Array of order items",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "number" },
"quantity": { "type": "integer" }
},
"required": ["name", "price", "quantity"]
}
},
"customer_email": {
"type": "string",
"description": "Customer email address"
},
"shipping_address": {
"type": "string",
"description": "Shipping address"
}
},
"required": ["items", "customer_email", "shipping_address"]
}
"summary": "E-commerce Order Processing Pipeline",
"value": {
"modules": [
{
"id": "validate_order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(items: { name: string; price: number; quantity: number }[]) {\n const invalid = items.filter(item => item.price <= 0 || item.quantity <= 0);\n return { valid: invalid.length === 0, invalidItems: invalid };\n}",
"input_transforms": {
"items": {
"type": "javascript",
"expr": "flow_input.items"
}
}
}
},
{
"id": "calculate_total",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(items: { name: string; price: number; quantity: number }[]) {\n const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n const tax = subtotal * 0.08;\n return { subtotal, tax, total: subtotal + tax };\n}",
"input_transforms": {
"items": {
"type": "javascript",
"expr": "flow_input.items"
}
}
}
},
{
"id": "check_inventory",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "flow_input.items"
},
"skip_failures": false,
"parallel": true,
"modules": [
{
"id": "check_item_stock",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: { name: string; quantity: number }) {\n // Mock inventory check - items with even quantity are in stock\n const inStock = item.quantity % 2 === 0 || item.quantity < 5;\n return { name: item.name, requested: item.quantity, inStock, available: inStock ? item.quantity : 0 };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
},
{
"id": "process_inventory_result",
"value": {
"type": "branchone",
"branches": [
{
"summary": "All items in stock - create shipment",
"expr": "results.check_inventory.every(item => item.inStock)",
"modules": [
{
"id": "create_shipment",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(shipping_address: string, total: number) {\n return {\n shipment_id: `SHIP-${Date.now()}`,\n status: 'created',\n address: shipping_address,\n total,\n estimated_delivery: '3-5 business days'\n };\n}",
"input_transforms": {
"shipping_address": {
"type": "javascript",
"expr": "flow_input.shipping_address"
},
"total": {
"type": "javascript",
"expr": "results.calculate_total.total"
}
}
}
}
]
}
],
"default": [
{
"id": "create_backorder",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(inventory_results: { name: string; inStock: boolean }[]) {\n const outOfStock = inventory_results.filter(item => !item.inStock);\n return {\n backorder_id: `BO-${Date.now()}`,\n status: 'backorder',\n unavailable_items: outOfStock.map(item => item.name),\n message: 'Some items are out of stock. We will notify you when available.'\n };\n}",
"input_transforms": {
"inventory_results": {
"type": "javascript",
"expr": "results.check_inventory"
}
}
}
}
]
}
},
{
"id": "send_confirmation",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_email: string, order_result: any) {\n // Mock email sending\n return {\n email_sent: true,\n to: customer_email,\n subject: order_result.shipment_id ? 'Order Confirmed' : 'Order Update - Backorder',\n sent_at: new Date().toISOString()\n };\n}",
"input_transforms": {
"customer_email": {
"type": "javascript",
"expr": "flow_input.customer_email"
},
"order_result": {
"type": "javascript",
"expr": "results.process_inventory_result"
}
}
}
},
{
"id": "return_summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(validation: any, totals: any, order_result: any, confirmation: any) {\n return {\n order_valid: validation.valid,\n totals,\n order_status: order_result.shipment_id ? 'shipped' : 'backorder',\n order_id: order_result.shipment_id || order_result.backorder_id,\n confirmation_sent: confirmation.email_sent\n };\n}",
"input_transforms": {
"validation": {
"type": "javascript",
"expr": "results.validate_order"
},
"totals": {
"type": "javascript",
"expr": "results.calculate_total"
},
"order_result": {
"type": "javascript",
"expr": "results.process_inventory_result"
},
"confirmation": {
"type": "javascript",
"expr": "results.send_confirmation"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"items": {
"type": "array",
"description": "Array of order items",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "number" },
"quantity": { "type": "integer" }
},
"required": ["name", "price", "quantity"]
}
},
"customer_email": {
"type": "string",
"description": "Customer email address"
},
"shipping_address": {
"type": "string",
"description": "Shipping address"
}
},
"required": ["items", "customer_email", "shipping_address"]
}
}
@@ -1,204 +1,204 @@
{
"summary": "Data Pipeline with Quality-Based Routing",
"value": {
"modules": [
{
"id": "fetch_data_sources",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n return [\n { id: 'source_1', url: 'https://api.example.com/data1' },\n { id: 'source_2', url: 'https://api.example.com/data2' },\n { id: 'source_3', url: 'https://api.example.com/data3' }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_sources",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.fetch_data_sources"
},
"skip_failures": false,
"parallel": true,
"modules": [
{
"id": "fetch_raw_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(source: { id: string; url: string }) {\n // Mock fetch returning sample records\n return {\n source_id: source.id,\n records: [\n { id: 1, value: 'data_a', valid: true },\n { id: 2, value: '', valid: false },\n { id: 3, value: 'data_c', valid: true },\n { id: 4, value: 'data_d', valid: true }\n ]\n };\n}",
"input_transforms": {
"source": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
},
{
"id": "transform_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(raw_data: { source_id: string; records: any[] }) {\n // Filter out invalid entries\n const cleaned = raw_data.records.filter(r => r.valid && r.value);\n return {\n source_id: raw_data.source_id,\n original_count: raw_data.records.length,\n cleaned_count: cleaned.length,\n records: cleaned\n };\n}",
"input_transforms": {
"raw_data": {
"type": "javascript",
"expr": "results.fetch_raw_data"
}
}
}
},
{
"id": "validate_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(transformed: { source_id: string; original_count: number; cleaned_count: number; records: any[] }) {\n // Calculate validation score based on data quality\n const score = Math.round((transformed.cleaned_count / transformed.original_count) * 100);\n return {\n source_id: transformed.source_id,\n records: transformed.records,\n validation_score: score,\n record_count: transformed.cleaned_count\n };\n}",
"input_transforms": {
"transformed": {
"type": "javascript",
"expr": "results.transform_data"
}
}
}
}
]
}
},
{
"id": "aggregate_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(processed_sources: any[]) {\n const all_records = processed_sources.flatMap(s => s.records);\n const total_count = all_records.length;\n return {\n combined_records: all_records,\n total_record_count: total_count,\n sources_processed: processed_sources.length\n };\n}",
"input_transforms": {
"processed_sources": {
"type": "javascript",
"expr": "results.process_sources"
}
}
}
},
{
"id": "calculate_quality_score",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(processed_sources: any[]) {\n const scores = processed_sources.map(s => s.validation_score);\n const average = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);\n return { quality_score: average, individual_scores: scores };\n}",
"input_transforms": {
"processed_sources": {
"type": "javascript",
"expr": "results.process_sources"
}
}
}
},
{
"id": "route_by_quality",
"value": {
"type": "branchone",
"branches": [
{
"summary": "High quality - store in primary database",
"expr": "results.calculate_quality_score.quality_score >= 90",
"modules": [
{
"id": "store_primary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'primary_database',\n status: 'success',\n records_stored: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
},
{
"summary": "Medium quality - store in secondary with warning",
"expr": "results.calculate_quality_score.quality_score >= 70",
"modules": [
{
"id": "store_secondary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'secondary_database',\n status: 'warning',\n warning_message: 'Data quality below optimal threshold',\n records_stored: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
}
],
"default": [
{
"id": "store_quarantine",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'quarantine',\n status: 'alert',\n alert_message: 'Data quality critically low - requires review',\n records_quarantined: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
}
},
{
"id": "generate_report",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(aggregated: any, quality: any, storage_result: any) {\n return {\n report: {\n total_records: aggregated.total_record_count,\n sources_processed: aggregated.sources_processed,\n quality_score: quality.quality_score,\n individual_scores: quality.individual_scores,\n destination: storage_result.destination,\n status: storage_result.status,\n processed_at: new Date().toISOString()\n }\n };\n}",
"input_transforms": {
"aggregated": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"quality": {
"type": "javascript",
"expr": "results.calculate_quality_score"
},
"storage_result": {
"type": "javascript",
"expr": "results.route_by_quality"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"required": []
}
"summary": "Data Pipeline with Quality-Based Routing",
"value": {
"modules": [
{
"id": "fetch_data_sources",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n return [\n { id: 'source_1', url: 'https://api.example.com/data1' },\n { id: 'source_2', url: 'https://api.example.com/data2' },\n { id: 'source_3', url: 'https://api.example.com/data3' }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_sources",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.fetch_data_sources"
},
"skip_failures": false,
"parallel": true,
"modules": [
{
"id": "fetch_raw_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(source: { id: string; url: string }) {\n // Mock fetch returning sample records\n return {\n source_id: source.id,\n records: [\n { id: 1, value: 'data_a', valid: true },\n { id: 2, value: '', valid: false },\n { id: 3, value: 'data_c', valid: true },\n { id: 4, value: 'data_d', valid: true }\n ]\n };\n}",
"input_transforms": {
"source": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
},
{
"id": "transform_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(raw_data: { source_id: string; records: any[] }) {\n // Filter out invalid entries\n const cleaned = raw_data.records.filter(r => r.valid && r.value);\n return {\n source_id: raw_data.source_id,\n original_count: raw_data.records.length,\n cleaned_count: cleaned.length,\n records: cleaned\n };\n}",
"input_transforms": {
"raw_data": {
"type": "javascript",
"expr": "results.fetch_raw_data"
}
}
}
},
{
"id": "validate_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(transformed: { source_id: string; original_count: number; cleaned_count: number; records: any[] }) {\n // Calculate validation score based on data quality\n const score = Math.round((transformed.cleaned_count / transformed.original_count) * 100);\n return {\n source_id: transformed.source_id,\n records: transformed.records,\n validation_score: score,\n record_count: transformed.cleaned_count\n };\n}",
"input_transforms": {
"transformed": {
"type": "javascript",
"expr": "results.transform_data"
}
}
}
}
]
}
},
{
"id": "aggregate_data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(processed_sources: any[]) {\n const all_records = processed_sources.flatMap(s => s.records);\n const total_count = all_records.length;\n return {\n combined_records: all_records,\n total_record_count: total_count,\n sources_processed: processed_sources.length\n };\n}",
"input_transforms": {
"processed_sources": {
"type": "javascript",
"expr": "results.process_sources"
}
}
}
},
{
"id": "calculate_quality_score",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(processed_sources: any[]) {\n const scores = processed_sources.map(s => s.validation_score);\n const average = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);\n return { quality_score: average, individual_scores: scores };\n}",
"input_transforms": {
"processed_sources": {
"type": "javascript",
"expr": "results.process_sources"
}
}
}
},
{
"id": "route_by_quality",
"value": {
"type": "branchone",
"branches": [
{
"summary": "High quality - store in primary database",
"expr": "results.calculate_quality_score.quality_score >= 90",
"modules": [
{
"id": "store_primary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'primary_database',\n status: 'success',\n records_stored: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
},
{
"summary": "Medium quality - store in secondary with warning",
"expr": "results.calculate_quality_score.quality_score >= 70",
"modules": [
{
"id": "store_secondary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'secondary_database',\n status: 'warning',\n warning_message: 'Data quality below optimal threshold',\n records_stored: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
}
],
"default": [
{
"id": "store_quarantine",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any, score: number) {\n return {\n destination: 'quarantine',\n status: 'alert',\n alert_message: 'Data quality critically low - requires review',\n records_quarantined: data.total_record_count,\n quality_score: score\n };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"score": {
"type": "javascript",
"expr": "results.calculate_quality_score.quality_score"
}
}
}
}
]
}
},
{
"id": "generate_report",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(aggregated: any, quality: any, storage_result: any) {\n return {\n report: {\n total_records: aggregated.total_record_count,\n sources_processed: aggregated.sources_processed,\n quality_score: quality.quality_score,\n individual_scores: quality.individual_scores,\n destination: storage_result.destination,\n status: storage_result.status,\n processed_at: new Date().toISOString()\n }\n };\n}",
"input_transforms": {
"aggregated": {
"type": "javascript",
"expr": "results.aggregate_data"
},
"quality": {
"type": "javascript",
"expr": "results.calculate_quality_score"
},
"storage_result": {
"type": "javascript",
"expr": "results.route_by_quality"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"required": []
}
}
@@ -1,175 +1,175 @@
{
"summary": "AI-Powered Customer Support with Tools",
"value": {
"modules": [
{
"id": "fetch_customer_profile",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_id: string) {\n // Mock customer profile and order history\n return {\n customer_id,\n name: 'John Doe',\n email: 'john.doe@example.com',\n membership_tier: 'gold',\n recent_orders: [\n { order_id: 'ORD-001', date: '2024-01-15', total: 149.99, status: 'delivered' },\n { order_id: 'ORD-002', date: '2024-02-20', total: 89.50, status: 'shipped' }\n ],\n support_history: [\n { ticket_id: 'TKT-100', issue: 'Delivery delay', resolved: true }\n ]\n };\n}",
"input_transforms": {
"customer_id": {
"type": "javascript",
"expr": "flow_input.customer_id"
}
}
}
},
{
"id": "support_agent",
"value": {
"type": "aiagent",
"input_transforms": {
"provider": {
"type": "static",
"value": "$res:f/ai_providers/openai"
},
"output_type": {
"type": "static",
"value": "text"
},
"user_message": {
"type": "javascript",
"expr": "`Customer Profile:\nName: ${results.fetch_customer_profile.name}\nMembership: ${results.fetch_customer_profile.membership_tier}\nRecent Orders: ${JSON.stringify(results.fetch_customer_profile.recent_orders)}\n\nCustomer Query: ${flow_input.query_text}`"
},
"system_prompt": {
"type": "static",
"value": "You are a helpful customer support agent. Use the available tools to look up order information, check refund eligibility, create support tickets, or search FAQs to help resolve customer queries. Be professional and empathetic."
}
},
"tools": [
{
"id": "lookup_order",
"summary": "Look up order details by order ID. Returns order status, items, and shipping information.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(order_id: string) {\n // Mock order lookup\n return {\n order_id,\n status: 'shipped',\n items: [\n { name: 'Wireless Headphones', quantity: 1, price: 79.99 },\n { name: 'Phone Case', quantity: 2, price: 19.99 }\n ],\n shipping: {\n carrier: 'FedEx',\n tracking_number: 'FX123456789',\n estimated_delivery: '2024-03-01'\n },\n total: 119.97\n };\n}",
"input_transforms": {
"order_id": {
"type": "static",
"value": null
}
}
}
},
{
"id": "check_refund_eligibility",
"summary": "Check if an order is eligible for refund. Returns eligibility status and reason.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(order_id: string) {\n // Mock refund eligibility check\n return {\n order_id,\n eligible: true,\n reason: 'Within 30-day return window',\n refund_amount: 119.97,\n refund_method: 'original_payment_method',\n processing_time: '5-7 business days'\n };\n}",
"input_transforms": {
"order_id": {
"type": "static",
"value": null
}
}
}
},
{
"id": "create_support_ticket",
"summary": "Create a support ticket with specified description and priority (low, medium, high).",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(description: string, priority: 'low' | 'medium' | 'high') {\n // Mock ticket creation\n return {\n ticket_id: `TKT-${Date.now()}`,\n description,\n priority,\n status: 'open',\n created_at: new Date().toISOString(),\n estimated_response: priority === 'high' ? '2 hours' : priority === 'medium' ? '24 hours' : '48 hours'\n };\n}",
"input_transforms": {
"description": {
"type": "static",
"value": null
},
"priority": {
"type": "static",
"value": null
}
}
}
},
{
"id": "search_faq",
"summary": "Search the FAQ database for relevant answers to common questions.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(search_query: string) {\n // Mock FAQ search\n return {\n query: search_query,\n results: [\n {\n question: 'How do I track my order?',\n answer: 'You can track your order by logging into your account and clicking on \"Order History\". Each order has a tracking link.',\n relevance: 0.95\n },\n {\n question: 'What is the return policy?',\n answer: 'We offer a 30-day return policy for all unused items in original packaging. Refunds are processed within 5-7 business days.',\n relevance: 0.85\n }\n ]\n };\n}",
"input_transforms": {
"search_query": {
"type": "static",
"value": null
}
}
}
}
],
"parallel": true
}
},
{
"id": "log_interaction",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_id: string, query: string, agent_response: string) {\n // Mock audit logging\n return {\n logged: true,\n log_id: `LOG-${Date.now()}`,\n timestamp: new Date().toISOString(),\n customer_id,\n query_summary: query.substring(0, 100),\n response_length: agent_response.length\n };\n}",
"input_transforms": {
"customer_id": {
"type": "javascript",
"expr": "flow_input.customer_id"
},
"query": {
"type": "javascript",
"expr": "flow_input.query_text"
},
"agent_response": {
"type": "javascript",
"expr": "results.support_agent"
}
}
}
},
{
"id": "return_response",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(agent_response: string, log_info: any, customer_profile: any) {\n return {\n response: agent_response,\n customer_name: customer_profile.name,\n interaction_logged: log_info.logged,\n log_id: log_info.log_id\n };\n}",
"input_transforms": {
"agent_response": {
"type": "javascript",
"expr": "results.support_agent"
},
"log_info": {
"type": "javascript",
"expr": "results.log_interaction"
},
"customer_profile": {
"type": "javascript",
"expr": "results.fetch_customer_profile"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "The unique identifier of the customer"
},
"query_text": {
"type": "string",
"description": "The customer's support query text"
}
},
"required": ["customer_id", "query_text"]
}
"summary": "AI-Powered Customer Support with Tools",
"value": {
"modules": [
{
"id": "fetch_customer_profile",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_id: string) {\n // Mock customer profile and order history\n return {\n customer_id,\n name: 'John Doe',\n email: 'john.doe@example.com',\n membership_tier: 'gold',\n recent_orders: [\n { order_id: 'ORD-001', date: '2024-01-15', total: 149.99, status: 'delivered' },\n { order_id: 'ORD-002', date: '2024-02-20', total: 89.50, status: 'shipped' }\n ],\n support_history: [\n { ticket_id: 'TKT-100', issue: 'Delivery delay', resolved: true }\n ]\n };\n}",
"input_transforms": {
"customer_id": {
"type": "javascript",
"expr": "flow_input.customer_id"
}
}
}
},
{
"id": "support_agent",
"value": {
"type": "aiagent",
"input_transforms": {
"provider": {
"type": "static",
"value": "$res:f/ai_providers/openai"
},
"output_type": {
"type": "static",
"value": "text"
},
"user_message": {
"type": "javascript",
"expr": "`Customer Profile:\nName: ${results.fetch_customer_profile.name}\nMembership: ${results.fetch_customer_profile.membership_tier}\nRecent Orders: ${JSON.stringify(results.fetch_customer_profile.recent_orders)}\n\nCustomer Query: ${flow_input.query_text}`"
},
"system_prompt": {
"type": "static",
"value": "You are a helpful customer support agent. Use the available tools to look up order information, check refund eligibility, create support tickets, or search FAQs to help resolve customer queries. Be professional and empathetic."
}
},
"tools": [
{
"id": "lookup_order",
"summary": "Look up order details by order ID. Returns order status, items, and shipping information.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(order_id: string) {\n // Mock order lookup\n return {\n order_id,\n status: 'shipped',\n items: [\n { name: 'Wireless Headphones', quantity: 1, price: 79.99 },\n { name: 'Phone Case', quantity: 2, price: 19.99 }\n ],\n shipping: {\n carrier: 'FedEx',\n tracking_number: 'FX123456789',\n estimated_delivery: '2024-03-01'\n },\n total: 119.97\n };\n}",
"input_transforms": {
"order_id": {
"type": "static",
"value": null
}
}
}
},
{
"id": "check_refund_eligibility",
"summary": "Check if an order is eligible for refund. Returns eligibility status and reason.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(order_id: string) {\n // Mock refund eligibility check\n return {\n order_id,\n eligible: true,\n reason: 'Within 30-day return window',\n refund_amount: 119.97,\n refund_method: 'original_payment_method',\n processing_time: '5-7 business days'\n };\n}",
"input_transforms": {
"order_id": {
"type": "static",
"value": null
}
}
}
},
{
"id": "create_support_ticket",
"summary": "Create a support ticket with specified description and priority (low, medium, high).",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(description: string, priority: 'low' | 'medium' | 'high') {\n // Mock ticket creation\n return {\n ticket_id: `TKT-${Date.now()}`,\n description,\n priority,\n status: 'open',\n created_at: new Date().toISOString(),\n estimated_response: priority === 'high' ? '2 hours' : priority === 'medium' ? '24 hours' : '48 hours'\n };\n}",
"input_transforms": {
"description": {
"type": "static",
"value": null
},
"priority": {
"type": "static",
"value": null
}
}
}
},
{
"id": "search_faq",
"summary": "Search the FAQ database for relevant answers to common questions.",
"value": {
"tool_type": "flowmodule",
"type": "rawscript",
"language": "bun",
"content": "export async function main(search_query: string) {\n // Mock FAQ search\n return {\n query: search_query,\n results: [\n {\n question: 'How do I track my order?',\n answer: 'You can track your order by logging into your account and clicking on \"Order History\". Each order has a tracking link.',\n relevance: 0.95\n },\n {\n question: 'What is the return policy?',\n answer: 'We offer a 30-day return policy for all unused items in original packaging. Refunds are processed within 5-7 business days.',\n relevance: 0.85\n }\n ]\n };\n}",
"input_transforms": {
"search_query": {
"type": "static",
"value": null
}
}
}
}
],
"parallel": true
}
},
{
"id": "log_interaction",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(customer_id: string, query: string, agent_response: string) {\n // Mock audit logging\n return {\n logged: true,\n log_id: `LOG-${Date.now()}`,\n timestamp: new Date().toISOString(),\n customer_id,\n query_summary: query.substring(0, 100),\n response_length: agent_response.length\n };\n}",
"input_transforms": {
"customer_id": {
"type": "javascript",
"expr": "flow_input.customer_id"
},
"query": {
"type": "javascript",
"expr": "flow_input.query_text"
},
"agent_response": {
"type": "javascript",
"expr": "results.support_agent"
}
}
}
},
{
"id": "return_response",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(agent_response: string, log_info: any, customer_profile: any) {\n return {\n response: agent_response,\n customer_name: customer_profile.name,\n interaction_logged: log_info.logged,\n log_id: log_info.log_id\n };\n}",
"input_transforms": {
"agent_response": {
"type": "javascript",
"expr": "results.support_agent"
},
"log_info": {
"type": "javascript",
"expr": "results.log_interaction"
},
"customer_profile": {
"type": "javascript",
"expr": "results.fetch_customer_profile"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "The unique identifier of the customer"
},
"query_text": {
"type": "string",
"description": "The customer's support query text"
}
},
"required": ["customer_id", "query_text"]
}
}
@@ -1,68 +1,68 @@
{
"summary": "Simple data pipeline with validation",
"value": {
"modules": [
{
"id": "fetch_data",
"summary": "Fetch data from API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock API call\n return [\n { id: 1, name: \"Item 1\", value: 100 },\n { id: 2, name: \"Item 2\", value: 200 },\n { id: 3, name: \"Item 3\", value: 300 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_data",
"summary": "Process the fetched data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Apply some transformation\n return data.map(item => ({\n ...item,\n value: item.value * 1.1,\n processed: true\n }));\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.fetch_data"
}
}
}
},
{
"id": "validate_data",
"summary": "Validate processed data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n if (!data || data.length === 0) {\n return { error: true, message: \"No data to save\" };\n }\n return { error: false, data: data };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.process_data"
}
}
}
},
{
"id": "save_results",
"summary": "Save results to database",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(validated: any) {\n if (validated.error) {\n return { saved: 0, status: \"skipped\", reason: validated.message };\n }\n // Mock database save\n console.log(`Saving ${validated.data.length} items to database`);\n return { saved: validated.data.length, status: \"success\" };\n}",
"input_transforms": {
"validated": {
"type": "javascript",
"expr": "results.validate_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
"summary": "Simple data pipeline with validation",
"value": {
"modules": [
{
"id": "fetch_data",
"summary": "Fetch data from API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock API call\n return [\n { id: 1, name: \"Item 1\", value: 100 },\n { id: 2, name: \"Item 2\", value: 200 },\n { id: 3, name: \"Item 3\", value: 300 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_data",
"summary": "Process the fetched data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Apply some transformation\n return data.map(item => ({\n ...item,\n value: item.value * 1.1,\n processed: true\n }));\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.fetch_data"
}
}
}
},
{
"id": "validate_data",
"summary": "Validate processed data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n if (!data || data.length === 0) {\n return { error: true, message: \"No data to save\" };\n }\n return { error: false, data: data };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.process_data"
}
}
}
},
{
"id": "save_results",
"summary": "Save results to database",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(validated: any) {\n if (validated.error) {\n return { saved: 0, status: \"skipped\", reason: validated.message };\n }\n // Mock database save\n console.log(`Saving ${validated.data.length} items to database`);\n return { saved: validated.data.length, status: \"success\" };\n}",
"input_transforms": {
"validated": {
"type": "javascript",
"expr": "results.validate_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
@@ -1,142 +1,142 @@
{
"summary": "Order processing flow with type-based branching",
"value": {
"modules": [
{
"id": "get_orders",
"summary": "Fetch list of orders",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock orders from database\n return [\n { id: \"ORD-001\", type: \"express\", items: 3, total: 150 },\n { id: \"ORD-002\", type: \"standard\", items: 5, total: 280 },\n { id: \"ORD-003\", type: \"pickup\", items: 2, total: 75 },\n { id: \"ORD-004\", type: \"express\", items: 1, total: 50 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "loop_orders",
"summary": "Process each order",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.get_orders"
},
"skip_failures": false,
"parallel": false,
"modules": [
{
"id": "branch_order_type",
"summary": "Branch based on order type",
"value": {
"type": "branchone",
"branches": [
{
"summary": "Express Order",
"expr": "flow_input.iter.value.type === 'express'",
"modules": [
{
"id": "handle_express",
"summary": "Handle express order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Mark as priority and calculate express shipping\n const expressShippingCost = 15.99;\n return {\n orderId: order.id,\n priority: true,\n shippingCost: expressShippingCost,\n shippingType: \"express\",\n estimatedDays: 1\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Standard Order",
"expr": "flow_input.iter.value.type === 'standard'",
"modules": [
{
"id": "handle_standard",
"summary": "Handle standard order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Calculate standard shipping cost\n const standardShippingCost = 5.99;\n return {\n orderId: order.id,\n priority: false,\n shippingCost: standardShippingCost,\n shippingType: \"standard\",\n estimatedDays: 5\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Pickup Order",
"expr": "flow_input.iter.value.type === 'pickup'",
"modules": [
{
"id": "handle_pickup",
"summary": "Handle pickup order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Mark as no shipping required\n return {\n orderId: order.id,\n priority: false,\n shippingCost: 0,\n shippingType: \"pickup\",\n estimatedDays: 0,\n pickupLocation: \"Store #1\"\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
],
"default": [
{
"id": "process_order",
"summary": "Process unknown order type",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n console.log(`Processing order ${order.id} with unknown type`);\n return {\n orderId: order.id,\n processed: true,\n shippingType: \"unknown\",\n timestamp: new Date().toISOString()\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
}
]
}
},
{
"id": "summarize",
"summary": "Return processing summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(results: any[]) {\n return {\n totalProcessed: results.length,\n processedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"results": {
"type": "javascript",
"expr": "results.loop_orders"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
"summary": "Order processing flow with type-based branching",
"value": {
"modules": [
{
"id": "get_orders",
"summary": "Fetch list of orders",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock orders from database\n return [\n { id: \"ORD-001\", type: \"express\", items: 3, total: 150 },\n { id: \"ORD-002\", type: \"standard\", items: 5, total: 280 },\n { id: \"ORD-003\", type: \"pickup\", items: 2, total: 75 },\n { id: \"ORD-004\", type: \"express\", items: 1, total: 50 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "loop_orders",
"summary": "Process each order",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.get_orders"
},
"skip_failures": false,
"parallel": false,
"modules": [
{
"id": "branch_order_type",
"summary": "Branch based on order type",
"value": {
"type": "branchone",
"branches": [
{
"summary": "Express Order",
"expr": "flow_input.iter.value.type === 'express'",
"modules": [
{
"id": "handle_express",
"summary": "Handle express order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Mark as priority and calculate express shipping\n const expressShippingCost = 15.99;\n return {\n orderId: order.id,\n priority: true,\n shippingCost: expressShippingCost,\n shippingType: \"express\",\n estimatedDays: 1\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Standard Order",
"expr": "flow_input.iter.value.type === 'standard'",
"modules": [
{
"id": "handle_standard",
"summary": "Handle standard order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Calculate standard shipping cost\n const standardShippingCost = 5.99;\n return {\n orderId: order.id,\n priority: false,\n shippingCost: standardShippingCost,\n shippingType: \"standard\",\n estimatedDays: 5\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
},
{
"summary": "Pickup Order",
"expr": "flow_input.iter.value.type === 'pickup'",
"modules": [
{
"id": "handle_pickup",
"summary": "Handle pickup order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n // Mark as no shipping required\n return {\n orderId: order.id,\n priority: false,\n shippingCost: 0,\n shippingType: \"pickup\",\n estimatedDays: 0,\n pickupLocation: \"Store #1\"\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
],
"default": [
{
"id": "process_order",
"summary": "Process unknown order type",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n console.log(`Processing order ${order.id} with unknown type`);\n return {\n orderId: order.id,\n processed: true,\n shippingType: \"unknown\",\n timestamp: new Date().toISOString()\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
}
]
}
},
{
"id": "summarize",
"summary": "Return processing summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(results: any[]) {\n return {\n totalProcessed: results.length,\n processedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"results": {
"type": "javascript",
"expr": "results.loop_orders"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
@@ -1,136 +1,136 @@
{
"summary": "Data enrichment flow with parallel processing",
"value": {
"modules": [
{
"id": "get_item",
"summary": "Get item from input",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item_id: string) {\n // Mock item lookup\n return {\n id: item_id,\n name: \"Product \" + item_id,\n sku: \"SKU-\" + item_id\n };\n}",
"input_transforms": {
"item_id": {
"type": "javascript",
"expr": "flow_input.item_id"
}
}
}
},
{
"id": "parallel_enrichment",
"summary": "Enrich data in parallel",
"value": {
"type": "branchall",
"branches": [
{
"summary": "Price enrichment",
"modules": [
{
"id": "enrich_price",
"summary": "Call pricing API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock pricing API call with timeout handling\n try {\n return {\n itemId: item.id,\n price: 99.99,\n currency: \"USD\",\n discount: 10\n };\n } catch (e) {\n return { itemId: item.id, price: 0, currency: \"USD\", fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
},
{
"summary": "Inventory enrichment",
"modules": [
{
"id": "enrich_inventory",
"summary": "Call inventory API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock inventory API call with timeout handling\n try {\n return {\n itemId: item.id,\n inStock: true,\n quantity: 150,\n warehouse: \"WH-001\"\n };\n } catch (e) {\n return { itemId: item.id, inStock: false, quantity: 0, fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
},
{
"summary": "Reviews enrichment",
"modules": [
{
"id": "enrich_reviews",
"summary": "Call reviews API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock reviews API call with timeout handling\n try {\n return {\n itemId: item.id,\n averageRating: 4.5,\n reviewCount: 127,\n topReview: \"Great product!\"\n };\n } catch (e) {\n return { itemId: item.id, averageRating: 0, reviewCount: 0, fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
}
]
}
},
{
"id": "combine_data",
"summary": "Combine all enrichment data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any, parallel_results: any) {\n // Extract results from parallel branches\n const [priceResult, inventoryResult, reviewsResult] = parallel_results;\n return {\n ...item,\n pricing: priceResult,\n inventory: inventoryResult,\n reviews: reviewsResult,\n hasFallbacks: priceResult?.fallback || inventoryResult?.fallback || reviewsResult?.fallback\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
},
"parallel_results": {
"type": "javascript",
"expr": "results.parallel_enrichment"
}
}
}
},
{
"id": "return_result",
"summary": "Return final result",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(enriched_item: any) {\n return {\n success: true,\n data: enriched_item,\n enrichedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"enriched_item": {
"type": "javascript",
"expr": "results.combine_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"item_id": {
"type": "string",
"description": "The ID of the item to enrich"
}
},
"required": ["item_id"],
"type": "object"
}
"summary": "Data enrichment flow with parallel processing",
"value": {
"modules": [
{
"id": "get_item",
"summary": "Get item from input",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item_id: string) {\n // Mock item lookup\n return {\n id: item_id,\n name: \"Product \" + item_id,\n sku: \"SKU-\" + item_id\n };\n}",
"input_transforms": {
"item_id": {
"type": "javascript",
"expr": "flow_input.item_id"
}
}
}
},
{
"id": "parallel_enrichment",
"summary": "Enrich data in parallel",
"value": {
"type": "branchall",
"branches": [
{
"summary": "Price enrichment",
"modules": [
{
"id": "enrich_price",
"summary": "Call pricing API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock pricing API call with timeout handling\n try {\n return {\n itemId: item.id,\n price: 99.99,\n currency: \"USD\",\n discount: 10\n };\n } catch (e) {\n return { itemId: item.id, price: 0, currency: \"USD\", fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
},
{
"summary": "Inventory enrichment",
"modules": [
{
"id": "enrich_inventory",
"summary": "Call inventory API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock inventory API call with timeout handling\n try {\n return {\n itemId: item.id,\n inStock: true,\n quantity: 150,\n warehouse: \"WH-001\"\n };\n } catch (e) {\n return { itemId: item.id, inStock: false, quantity: 0, fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
},
{
"summary": "Reviews enrichment",
"modules": [
{
"id": "enrich_reviews",
"summary": "Call reviews API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock reviews API call with timeout handling\n try {\n return {\n itemId: item.id,\n averageRating: 4.5,\n reviewCount: 127,\n topReview: \"Great product!\"\n };\n } catch (e) {\n return { itemId: item.id, averageRating: 0, reviewCount: 0, fallback: true };\n }\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
}
]
}
]
}
},
{
"id": "combine_data",
"summary": "Combine all enrichment data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any, parallel_results: any) {\n // Extract results from parallel branches\n const [priceResult, inventoryResult, reviewsResult] = parallel_results;\n return {\n ...item,\n pricing: priceResult,\n inventory: inventoryResult,\n reviews: reviewsResult,\n hasFallbacks: priceResult?.fallback || inventoryResult?.fallback || reviewsResult?.fallback\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
},
"parallel_results": {
"type": "javascript",
"expr": "results.parallel_enrichment"
}
}
}
},
{
"id": "return_result",
"summary": "Return final result",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(enriched_item: any) {\n return {\n success: true,\n data: enriched_item,\n enrichedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"enriched_item": {
"type": "javascript",
"expr": "results.combine_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"item_id": {
"type": "string",
"description": "The ID of the item to enrich"
}
},
"required": ["item_id"],
"type": "object"
}
}
@@ -1,5 +1,9 @@
import { describe, it, expect } from 'vitest'
import { runVariantComparison, writeFlowComparisonResults, type ExpectedFlow } from './flowEvalRunner'
import {
runVariantComparison,
writeFlowComparisonResults,
type ExpectedFlow
} from './flowEvalRunner'
import { BASELINE_VARIANT, MINIMAL_SINGLE_TOOL_VARIANT } from './variants'
// @ts-ignore - JSON import
import expectedTest1 from './expected/test1.json'
@@ -1,6 +1,11 @@
import type { FlowModule } from '$lib/gen'
import type { ExtendedOpenFlow } from '$lib/components/flows/types'
import { flowTools, prepareFlowSystemMessage, prepareFlowUserMessage, type FlowAIChatHelpers } from '../../flow/core'
import {
flowTools,
prepareFlowSystemMessage,
prepareFlowUserMessage,
type FlowAIChatHelpers
} from '../../flow/core'
import { createFlowEvalHelpers } from './flowEvalHelpers'
import { evaluateFlowComparison, type ExpectedFlow } from './flowEvalComparison'
import {
@@ -64,7 +69,11 @@ export async function runFlowEval(
// Resolve variant configuration
const variantName = options?.variant?.name ?? 'baseline'
const systemMessage = resolveSystemPrompt(options?.variant, flowDefaults, options?.customSystemPrompt)
const systemMessage = resolveSystemPrompt(
options?.variant,
flowDefaults,
options?.customSystemPrompt
)
const { toolDefs, tools } = resolveTools(options?.variant, flowDefaults)
const model = resolveModel(options?.variant, options?.model)
@@ -1,53 +1,53 @@
{
"summary": "Simple data pipeline",
"value": {
"modules": [
{
"id": "fetch_data",
"summary": "Fetch data from API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock API call\n return [\n { id: 1, name: \"Item 1\", value: 100 },\n { id: 2, name: \"Item 2\", value: 200 },\n { id: 3, name: \"Item 3\", value: 300 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_data",
"summary": "Process the fetched data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Apply some transformation\n return data.map(item => ({\n ...item,\n value: item.value * 1.1,\n processed: true\n }));\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.fetch_data"
}
}
}
},
{
"id": "save_results",
"summary": "Save results to database",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Mock database save\n console.log(`Saving ${data.length} items to database`);\n return { saved: data.length, status: \"success\" };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.process_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
"summary": "Simple data pipeline",
"value": {
"modules": [
{
"id": "fetch_data",
"summary": "Fetch data from API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock API call\n return [\n { id: 1, name: \"Item 1\", value: 100 },\n { id: 2, name: \"Item 2\", value: 200 },\n { id: 3, name: \"Item 3\", value: 300 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "process_data",
"summary": "Process the fetched data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Apply some transformation\n return data.map(item => ({\n ...item,\n value: item.value * 1.1,\n processed: true\n }));\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.fetch_data"
}
}
}
},
{
"id": "save_results",
"summary": "Save results to database",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(data: any[]) {\n // Mock database save\n console.log(`Saving ${data.length} items to database`);\n return { saved: data.length, status: \"success\" };\n}",
"input_transforms": {
"data": {
"type": "javascript",
"expr": "results.process_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
@@ -1,68 +1,68 @@
{
"summary": "Order processing flow",
"value": {
"modules": [
{
"id": "get_orders",
"summary": "Fetch list of orders",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock orders from database\n return [\n { id: \"ORD-001\", type: \"express\", items: 3, total: 150 },\n { id: \"ORD-002\", type: \"standard\", items: 5, total: 280 },\n { id: \"ORD-003\", type: \"pickup\", items: 2, total: 75 },\n { id: \"ORD-004\", type: \"express\", items: 1, total: 50 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "loop_orders",
"summary": "Process each order",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.get_orders"
},
"skip_failures": false,
"parallel": false,
"modules": [
{
"id": "process_order",
"summary": "Process individual order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n console.log(`Processing order ${order.id}`);\n return {\n orderId: order.id,\n processed: true,\n timestamp: new Date().toISOString()\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
},
{
"id": "summarize",
"summary": "Return processing summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(results: any[]) {\n return {\n totalProcessed: results.length,\n processedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"results": {
"type": "javascript",
"expr": "results.loop_orders"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
"summary": "Order processing flow",
"value": {
"modules": [
{
"id": "get_orders",
"summary": "Fetch list of orders",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main() {\n // Mock orders from database\n return [\n { id: \"ORD-001\", type: \"express\", items: 3, total: 150 },\n { id: \"ORD-002\", type: \"standard\", items: 5, total: 280 },\n { id: \"ORD-003\", type: \"pickup\", items: 2, total: 75 },\n { id: \"ORD-004\", type: \"express\", items: 1, total: 50 }\n ];\n}",
"input_transforms": {}
}
},
{
"id": "loop_orders",
"summary": "Process each order",
"value": {
"type": "forloopflow",
"iterator": {
"type": "javascript",
"expr": "results.get_orders"
},
"skip_failures": false,
"parallel": false,
"modules": [
{
"id": "process_order",
"summary": "Process individual order",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(order: any) {\n console.log(`Processing order ${order.id}`);\n return {\n orderId: order.id,\n processed: true,\n timestamp: new Date().toISOString()\n };\n}",
"input_transforms": {
"order": {
"type": "javascript",
"expr": "flow_input.iter.value"
}
}
}
}
]
}
},
{
"id": "summarize",
"summary": "Return processing summary",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(results: any[]) {\n return {\n totalProcessed: results.length,\n processedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"results": {
"type": "javascript",
"expr": "results.loop_orders"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"required": [],
"type": "object"
}
}
@@ -1,120 +1,120 @@
{
"summary": "Data enrichment flow",
"value": {
"modules": [
{
"id": "get_item",
"summary": "Get item from input",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item_id: string) {\n // Mock item lookup\n return {\n id: item_id,\n name: \"Product \" + item_id,\n sku: \"SKU-\" + item_id\n };\n}",
"input_transforms": {
"item_id": {
"type": "javascript",
"expr": "flow_input.item_id"
}
}
}
},
{
"id": "enrich_price",
"summary": "Call pricing API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock pricing API call\n return {\n itemId: item.id,\n price: 99.99,\n currency: \"USD\",\n discount: 10\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "enrich_inventory",
"summary": "Call inventory API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock inventory API call\n return {\n itemId: item.id,\n inStock: true,\n quantity: 150,\n warehouse: \"WH-001\"\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "enrich_reviews",
"summary": "Call reviews API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock reviews API call\n return {\n itemId: item.id,\n averageRating: 4.5,\n reviewCount: 127,\n topReview: \"Great product!\"\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "combine_data",
"summary": "Combine all enrichment data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any, price: any, inventory: any, reviews: any) {\n return {\n ...item,\n pricing: price,\n inventory: inventory,\n reviews: reviews\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
},
"price": {
"type": "javascript",
"expr": "results.enrich_price"
},
"inventory": {
"type": "javascript",
"expr": "results.enrich_inventory"
},
"reviews": {
"type": "javascript",
"expr": "results.enrich_reviews"
}
}
}
},
{
"id": "return_result",
"summary": "Return final result",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(enriched_item: any) {\n return {\n success: true,\n data: enriched_item,\n enrichedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"enriched_item": {
"type": "javascript",
"expr": "results.combine_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"item_id": {
"type": "string",
"description": "The ID of the item to enrich"
}
},
"required": ["item_id"],
"type": "object"
}
"summary": "Data enrichment flow",
"value": {
"modules": [
{
"id": "get_item",
"summary": "Get item from input",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item_id: string) {\n // Mock item lookup\n return {\n id: item_id,\n name: \"Product \" + item_id,\n sku: \"SKU-\" + item_id\n };\n}",
"input_transforms": {
"item_id": {
"type": "javascript",
"expr": "flow_input.item_id"
}
}
}
},
{
"id": "enrich_price",
"summary": "Call pricing API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock pricing API call\n return {\n itemId: item.id,\n price: 99.99,\n currency: \"USD\",\n discount: 10\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "enrich_inventory",
"summary": "Call inventory API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock inventory API call\n return {\n itemId: item.id,\n inStock: true,\n quantity: 150,\n warehouse: \"WH-001\"\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "enrich_reviews",
"summary": "Call reviews API",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any) {\n // Mock reviews API call\n return {\n itemId: item.id,\n averageRating: 4.5,\n reviewCount: 127,\n topReview: \"Great product!\"\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
}
}
}
},
{
"id": "combine_data",
"summary": "Combine all enrichment data",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(item: any, price: any, inventory: any, reviews: any) {\n return {\n ...item,\n pricing: price,\n inventory: inventory,\n reviews: reviews\n };\n}",
"input_transforms": {
"item": {
"type": "javascript",
"expr": "results.get_item"
},
"price": {
"type": "javascript",
"expr": "results.enrich_price"
},
"inventory": {
"type": "javascript",
"expr": "results.enrich_inventory"
},
"reviews": {
"type": "javascript",
"expr": "results.enrich_reviews"
}
}
}
},
{
"id": "return_result",
"summary": "Return final result",
"value": {
"type": "rawscript",
"language": "bun",
"content": "export async function main(enriched_item: any) {\n return {\n success: true,\n data: enriched_item,\n enrichedAt: new Date().toISOString()\n };\n}",
"input_transforms": {
"enriched_item": {
"type": "javascript",
"expr": "results.combine_data"
}
}
}
}
]
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"item_id": {
"type": "string",
"description": "The ID of the item to enrich"
}
},
"required": ["item_id"],
"type": "object"
}
}
@@ -1,5 +1,8 @@
import OpenAI, { APIError } from 'openai'
import type { ChatCompletionMessageParam, ChatCompletionSystemMessageParam } from 'openai/resources/chat/completions.mjs'
import type {
ChatCompletionMessageParam,
ChatCompletionSystemMessageParam
} from 'openai/resources/chat/completions.mjs'
import type { ChatCompletionTool } from 'openai/resources/chat/completions.mjs'
import type { TokenUsage, ToolCallDetail, EvalRunnerOptions } from './types'
import type { Tool } from './baseVariants'
@@ -52,16 +55,8 @@ export interface RunEvalParams<THelpers, TOutput> {
export async function runEval<THelpers, TOutput>(
params: RunEvalParams<THelpers, TOutput>
): Promise<RawEvalResult<TOutput>> {
const {
systemMessage,
userMessage,
toolDefs,
tools,
helpers,
apiKey,
getOutput,
options
} = params
const { systemMessage, userMessage, toolDefs, tools, helpers, apiKey, getOutput, options } =
params
const client = new OpenAI({ baseURL: 'https://openrouter.ai/api/v1', apiKey })
const model = options?.model ?? 'gpt-4o'
@@ -8,7 +8,7 @@ import { getDocumentationTool } from '../navigator/core'
import { userStore } from '$lib/stores'
import { get } from 'svelte/store'
export const CHAT_SYSTEM_PROMPT = (username: string) =>`
export const CHAT_SYSTEM_PROMPT = (username: string) => `
You are Windmill's intelligent assistant, designed to interact with the platform via API endpoints and answer questions about its functionality. Your purpose is to help the user directly query and manipulate Windmill resources through API calls.
Windmill is an open-source developer platform for building internal tools, API integrations, background jobs, workflows, and user interfaces. It offers a unified system where scripts are automatically turned into sharable UIs and can be composed into flows or embedded in custom applications.
@@ -76,4 +76,4 @@ export function prepareApiUserMessage(instructions: string): ChatCompletionUserM
role: 'user',
content: instructions
}
}
}
@@ -18,20 +18,22 @@
</script>
<Modal bind:open title="AI Changes Will Be Lost">
<div slot="title" class="flex items-center gap-2">
<AlertTriangleIcon size={20} class="text-orange-500" />
AI Changes Will Be Lost
</div>
<div class="py-4">
<div class="flex items-center gap-2 mb-3">
<AlertTriangleIcon size={20} class="text-orange-500" />
<span class="font-medium">Warning</span>
</div>
<p class="text-gray-700 dark:text-gray-300">
You have pending AI changes that will be rejected when saving. Do you want to continue?
</p>
</div>
<div slot="actions">
<div class="flex gap-2 justify-end">
<Button variant="contained" color="red" on:click={handleConfirm}>
Reject changes and save
</Button>
{#snippet actions()}
<div>
<div class="flex gap-2 justify-end">
<Button variant="contained" color="red" onclick={handleConfirm}>
Reject changes and save
</Button>
</div>
</div>
</div>
{/snippet}
</Modal>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,65 +1,86 @@
export const EDIT_PROMPT = {
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You modify code as instructed by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nPut explanations directly in the code as comments.\nReturn the complete modified code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```",
"prompts": {
"python3": {
"prompt": "Here's my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result.\nThe \"main\" function cannot be async. If you need to use async code, you can use the asyncio library.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nMy instructions: {description}"
},
"deno": {
"prompt": "Here's my TypeScript code in a deno running environment:\n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\". \nThe resource type name has to be exactly as specified.\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}"
},
"rust": {
"prompt": "Here's my Rust code:\n```rust\n{code}\n```\n<contextual_information>\nPlease define a `main` function in Rust with this signature:\n```rust\npub fn main(...) -> Result<ReturnType, Box<dyn std::error::Error>>\n```\nbut do not call it. Favor idiomatic Rust patterns, ensuring safe handling of ownership and borrowing, robust error handling with `Result`, and concurrency if needed (`async`/`tokio` or std threading). \n\nFollow these guidelines:\n- Include necessary imports and modules only when needed.\n- Add comments explaining important operations and any unsafe usage (if absolutely required).\n- The generated code should be easily executable and testable in an integrated terminal.\n</contextual_information>\nMy instructions: {description}"
},
"go": {
"prompt": "Here's my go code: \n```go\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function. Import the packages you need. The return type of the function has to be ({return_type}, error). The file package has to be \"inner\"\n</contextual_information>\nMy instructions: {description}"
},
"bash": {
"prompt": "Here's my bash code: \n```shell\n{code}\n```\n<contextual_information>\nDo 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.\n</contextual_information>\nMy instructions: {description}"
},
"postgresql": {
"prompt": "Here's my PostgreSQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with `$1::{type}`, `$2::{type}`, etc... Name the parameters (without specifying the type) by adding comments before the statement like that: `-- $1 name1` or `-- $2 name = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"mysql": {
"prompt": "Here's my MySQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"bigquery": {
"prompt": "Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\n</contextual_information>\nMy instructions: {description}"
},
"snowflake": {
"prompt": "Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"mssql": {
"prompt": "Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @P1, @P2, etc.. Name the parameters by adding comments before the statement like that: `-- @P1 name1 ({type})` or `-- @P2 name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
"graphql": {
"prompt": "Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nMy instructions: {description}"
},
"powershell": {
"prompt": "Here's my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = \"default value\", [{type}]$ParamName3, ...)`\n</contextual_information>\nMy instructions: {description}"
},
"nativets": {
"prompt": "Here's my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}"
},
"bun": {
"prompt": "Here's my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\". \nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}"
},
"php": {
"prompt": "Here's my php code: \n```php\n{code}\n```\n<contextual_information>\nYou have to write a function in php called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result. The script must start with `<?php`.\nYou can take as parameters resources which are classes containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nBefore defining each type, check if the class already exists using class_exists.\nThe resource type name has to be exactly as specified.\nIf you need to import libraries, you need to specify them as comments in the following manner before the main function:\n```\n// require:\n// mylibrary/mylibrary\n// myotherlibrary/myotherlibrary@optionalversion\n```\nNo need to require autoload, it is already done.\n<contextual_information>\nMy instructions: {description}"
},
"csharp": {
"prompt": "Here's my C# code:\n```csharp\n{code}\n```\n<contextual_information>\nYou have to write C# code with a public static Main method inside a class. The class name is irrelevant. Specify the parameter types. Do not call the main function. You should generally return the result.\nNuGet packages can be added using the format: #r \"nuget: PackageName, Version\" at the top of the script.\nThe Main method signature should be: public static ReturnType Main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nMy instructions: {description}"
},
"java": {
"prompt": "Here's my Java code:\n```java\n{code}\n```\n<contextual_information>\nYou have to write Java code with a Main public class and a public static main() method. The return type can be Object or void. Do not call the main function. You should generally return the result.\nDependencies can be added using the format: //requirements://groupId:artifactId:version at the top of the script.\nThe method signature should be: public static Object main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nMy instructions: {description}"
},
"frontend": {
"prompt": "Here's my client-side javascript code: \n```javascript\n{code}\n```\n<contextual_information>\nYou can access the context object with the ctx global variable. \nThe app state is a store that can be used to store data. You can access and update the state object with the state global variable like this: state.foo = 'bar'\nYou can use the goto function to navigate to a specific URL: goto(path: string, newTab?: boolean)\nYou can use the openModal and closeModal functions to open and close a modal: openModal(id: string), closeModal(id: string)\nYou can use the setTab function to manually set the tab of a Tab component: setTab(id: string, index: string)\nYou can use the recompute function to recompute a component: recompute(id: string)\nYou can use the getAgGrid function to get the ag-grid instance of a table: getAgGrid(id: string)\nYou can use the setSelectedIndex function to select a row in a table or an AG Grid table: setSelectedIndex(id: string, index: number)\nThe setValue function is meant to set or force the value of a component: setValue(id: string, value: any)\nYou can validate a specific field of a form: validate(id: string, key: string)\nYou can validate all fields of a form: validateAll(id: string, key: string)\nYou can invalidate a specific field of a form: invalidate(id: string, key: string, error: string)\n</contextual_information>\nMy instructions: {description}"
},
"transformer": {
"prompt": "Here's my client-side javascript code: \n```javascript\n{code}\n```\n\n<contextual_information>\nThe code should process the variable `result` according to my instructions.\nThe variable `result` is available globally.\nAt the end of the code, the processed result has to be returned.\n\n<helpers>\nYou can access the context object with the ctx global variable. \nThe app state is a store that can be used to store data. You can access and update the state object with the state global variable like this: state.foo = 'bar'\nYou can use the goto function to navigate to a specific URL: goto(path: string, newTab?: boolean)\nYou can use the openModal and closeModal functions to open and close a modal: openModal(id: string), closeModal(id: string)\nYou can use the setTab function to manually set the tab of a Tab component: setTab(id: string, index: string)\nYou can use the recompute function to recompute a component: recompute(id: string)\nYou can use the getAgGrid function to get the ag-grid instance of a table: getAgGrid(id: string)\nYou can use the setSelectedIndex function to select a row in a table or an AG Grid table: setSelectedIndex(id: string, index: number)\nThe setValue function is meant to set or force the value of a component: setValue(id: string, value: any)\nYou can validate a specific field of a form: validate(id: string, key: string)\nYou can validate all fields of a form: validateAll(id: string, key: string)\nYou can invalidate a specific field of a form: invalidate(id: string, key: string, error: string)\n</helpers>\n</contextual_information>\nMy instructions: {description}"
},
"duckdb": {
"prompt": "Here's my DuckDB code:\n```sql\n{code}\n```\n<contextual_information>\nArguments are defined with comments like `-- $age (text) = 20` or `-- $name (text)` (one per row) and used in the statement with $age, $name, etc. To use Ducklake, attach it with `ATTACH 'ducklake' AS dl;` (for main ducklake) or `ATTACH 'ducklake://name' AS dl;` for named ducklakes. To connect to external databases, use `ATTACH '$res:path/to/resource' AS db (TYPE postgres);`. To read S3 files, use `SELECT * FROM read_csv('s3:///path/to/file.csv');` for default storage or `SELECT * FROM read_csv('s3://secondary_storage_name/path/to/file.csv');` for named storage.\n</contextual_information>\nMy instructions: {description}"
}
}
};
system:
"You are a helpful coding assistant for Windmill, a developer platform for running scripts. You modify code as instructed by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nPut explanations directly in the code as comments.\nReturn the complete modified code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```",
prompts: {
python3: {
prompt:
'Here\'s my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called "main". Specify the parameter types. Do not call the main function. You should generally return the result.\nThe "main" function cannot be async. If you need to use async code, you can use the asyncio library.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nMy instructions: {description}'
},
deno: {
prompt:
'Here\'s my TypeScript code in a deno running environment:\n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource". \nThe resource type name has to be exactly as specified.\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}'
},
rust: {
prompt:
"Here's my Rust code:\n```rust\n{code}\n```\n<contextual_information>\nPlease define a `main` function in Rust with this signature:\n```rust\npub fn main(...) -> Result<ReturnType, Box<dyn std::error::Error>>\n```\nbut do not call it. Favor idiomatic Rust patterns, ensuring safe handling of ownership and borrowing, robust error handling with `Result`, and concurrency if needed (`async`/`tokio` or std threading). \n\nFollow these guidelines:\n- Include necessary imports and modules only when needed.\n- Add comments explaining important operations and any unsafe usage (if absolutely required).\n- The generated code should be easily executable and testable in an integrated terminal.\n</contextual_information>\nMy instructions: {description}"
},
go: {
prompt:
'Here\'s my go code: \n```go\n{code}\n```\n<contextual_information>\nWe have to export a "main" function. Import the packages you need. The return type of the function has to be ({return_type}, error). The file package has to be "inner"\n</contextual_information>\nMy instructions: {description}'
},
bash: {
prompt:
'Here\'s my bash code: \n```shell\n{code}\n```\n<contextual_information>\nDo 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.\n</contextual_information>\nMy instructions: {description}'
},
postgresql: {
prompt:
"Here's my PostgreSQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with `$1::{type}`, `$2::{type}`, etc... Name the parameters (without specifying the type) by adding comments before the statement like that: `-- $1 name1` or `-- $2 name = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
mysql: {
prompt:
"Here's my MySQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
bigquery: {
prompt:
"Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\n</contextual_information>\nMy instructions: {description}"
},
snowflake: {
prompt:
"Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
mssql: {
prompt:
"Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @P1, @P2, etc.. Name the parameters by adding comments before the statement like that: `-- @P1 name1 ({type})` or `-- @P2 name2 ({type}) = default` (one per row)\n</contextual_information>\nMy instructions: {description}"
},
graphql: {
prompt:
"Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nMy instructions: {description}"
},
powershell: {
prompt:
'Here\'s my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = "default value", [{type}]$ParamName3, ...)`\n</contextual_information>\nMy instructions: {description}'
},
nativets: {
prompt:
'Here\'s my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}'
},
bun: {
prompt:
'Here\'s my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource". \nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nMy instructions: {description}'
},
php: {
prompt:
'Here\'s my php code: \n```php\n{code}\n```\n<contextual_information>\nYou have to write a function in php called "main". Specify the parameter types. Do not call the main function. You should generally return the result. The script must start with `<?php`.\nYou can take as parameters resources which are classes containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nBefore defining each type, check if the class already exists using class_exists.\nThe resource type name has to be exactly as specified.\nIf you need to import libraries, you need to specify them as comments in the following manner before the main function:\n```\n// require:\n// mylibrary/mylibrary\n// myotherlibrary/myotherlibrary@optionalversion\n```\nNo need to require autoload, it is already done.\n<contextual_information>\nMy instructions: {description}'
},
csharp: {
prompt:
'Here\'s my C# code:\n```csharp\n{code}\n```\n<contextual_information>\nYou have to write C# code with a public static Main method inside a class. The class name is irrelevant. Specify the parameter types. Do not call the main function. You should generally return the result.\nNuGet packages can be added using the format: #r "nuget: PackageName, Version" at the top of the script.\nThe Main method signature should be: public static ReturnType Main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nMy instructions: {description}'
},
java: {
prompt:
"Here's my Java code:\n```java\n{code}\n```\n<contextual_information>\nYou have to write Java code with a Main public class and a public static main() method. The return type can be Object or void. Do not call the main function. You should generally return the result.\nDependencies can be added using the format: //requirements://groupId:artifactId:version at the top of the script.\nThe method signature should be: public static Object main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nMy instructions: {description}"
},
frontend: {
prompt:
"Here's my client-side javascript code: \n```javascript\n{code}\n```\n<contextual_information>\nYou can access the context object with the ctx global variable. \nThe app state is a store that can be used to store data. You can access and update the state object with the state global variable like this: state.foo = 'bar'\nYou can use the goto function to navigate to a specific URL: goto(path: string, newTab?: boolean)\nYou can use the openModal and closeModal functions to open and close a modal: openModal(id: string), closeModal(id: string)\nYou can use the setTab function to manually set the tab of a Tab component: setTab(id: string, index: string)\nYou can use the recompute function to recompute a component: recompute(id: string)\nYou can use the getAgGrid function to get the ag-grid instance of a table: getAgGrid(id: string)\nYou can use the setSelectedIndex function to select a row in a table or an AG Grid table: setSelectedIndex(id: string, index: number)\nThe setValue function is meant to set or force the value of a component: setValue(id: string, value: any)\nYou can validate a specific field of a form: validate(id: string, key: string)\nYou can validate all fields of a form: validateAll(id: string, key: string)\nYou can invalidate a specific field of a form: invalidate(id: string, key: string, error: string)\n</contextual_information>\nMy instructions: {description}"
},
transformer: {
prompt:
"Here's my client-side javascript code: \n```javascript\n{code}\n```\n\n<contextual_information>\nThe code should process the variable `result` according to my instructions.\nThe variable `result` is available globally.\nAt the end of the code, the processed result has to be returned.\n\n<helpers>\nYou can access the context object with the ctx global variable. \nThe app state is a store that can be used to store data. You can access and update the state object with the state global variable like this: state.foo = 'bar'\nYou can use the goto function to navigate to a specific URL: goto(path: string, newTab?: boolean)\nYou can use the openModal and closeModal functions to open and close a modal: openModal(id: string), closeModal(id: string)\nYou can use the setTab function to manually set the tab of a Tab component: setTab(id: string, index: string)\nYou can use the recompute function to recompute a component: recompute(id: string)\nYou can use the getAgGrid function to get the ag-grid instance of a table: getAgGrid(id: string)\nYou can use the setSelectedIndex function to select a row in a table or an AG Grid table: setSelectedIndex(id: string, index: number)\nThe setValue function is meant to set or force the value of a component: setValue(id: string, value: any)\nYou can validate a specific field of a form: validate(id: string, key: string)\nYou can validate all fields of a form: validateAll(id: string, key: string)\nYou can invalidate a specific field of a form: invalidate(id: string, key: string, error: string)\n</helpers>\n</contextual_information>\nMy instructions: {description}"
},
duckdb: {
prompt:
"Here's my DuckDB code:\n```sql\n{code}\n```\n<contextual_information>\nArguments are defined with comments like `-- $age (text) = 20` or `-- $name (text)` (one per row) and used in the statement with $age, $name, etc. To use Ducklake, attach it with `ATTACH 'ducklake' AS dl;` (for main ducklake) or `ATTACH 'ducklake://name' AS dl;` for named ducklakes. To connect to external databases, use `ATTACH '$res:path/to/resource' AS db (TYPE postgres);`. To read S3 files, use `SELECT * FROM read_csv('s3:///path/to/file.csv');` for default storage or `SELECT * FROM read_csv('s3://secondary_storage_name/path/to/file.csv');` for named storage.\n</contextual_information>\nMy instructions: {description}"
}
}
}
@@ -1,59 +1,78 @@
export const FIX_PROMPT = {
"system": "You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nExplain the error and the fix after generating the code inside an <explanation> tag.\nAlso put explanations directly in the code as comments.\nReturn the complete fixed code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```\n<explanation>{explanation}</explanation>",
"prompts": {
"python3": {
"prompt": "Here's my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result.\nThe \"main\" function cannot be async. If you need to use async code, you can use the asyncio library.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nI get the following error: {error}\nFix my code."
},
"deno": {
"prompt": "Here's my TypeScript code in a deno running environment:\n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"rust": {
"prompt": "Here's my Rust code:\n```rust\n{code}\n```\n<contextual_information>\nPlease define a `main` function in Rust with this signature:\n```rust\npub fn main(...) -> Result<ReturnType, Box<dyn std::error::Error>>\n```\nbut do not call it. Favor idiomatic Rust patterns, ensuring safe handling of ownership and borrowing, robust error handling with `Result`, and concurrency if needed (`async`/`tokio` or std threading). \n\n- Include necessary imports and modules only when needed.\n- Add comments explaining important operations and any unsafe usage (if absolutely required).\n- The generated code should be easily executable and testable in an integrated terminal.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"go": {
"prompt": "Here's my go code: \n```go\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function. Import the packages you need. The return type of the function has to be ({return_type}, error). The file package has to be \"inner\"\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"bash": {
"prompt": "Here's my bash code: \n```shell\n{code}\n```\n<contextual_information>\nDo 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.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"postgresql": {
"prompt": "Here's my PostgreSQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with `$1::{type}`, `$2::{type}`, etc... Name the parameters (without specifying the type) by adding comments before the statement like that: `-- $1 name1` or `-- $2 name = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"mysql": {
"prompt": "Here's my MySQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"bigquery": {
"prompt": "Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"snowflake": {
"prompt": "Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"mssql": {
"prompt": "Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @P1, @P2, etc.. Name the parameters by adding comments before the statement like that: `-- @P1 name1 ({type})` or `-- @P2 name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"graphql": {
"prompt": "Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"powershell": {
"prompt": "Here's my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = \"default value\", [{type}]$ParamName3, ...)`\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"nativets": {
"prompt": "Here's my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\". \nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"bun": {
"prompt": "Here's my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a \"main\" function like this: \"export async function main(...)\" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: \"{resource_type}Resource\".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user's naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"php": {
"prompt": "Here's my php code: \n```php\n{code}\n```\n<contextual_information>\nYou have to write a function in php called \"main\". Specify the parameter types. Do not call the main function. You should generally return the result. The script must start with `<?php`.\nYou can take as parameters resources which are classes containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nBefore defining each type, check if the class already exists using class_exists.\nThe resource type name has to be exactly as specified.\nIf you need to import libraries, you need to specify them as comments in the following manner before the main function:\n```\n// require:\n// mylibrary/mylibrary\n// myotherlibrary/myotherlibrary@optionalversion\n```\nNo need to require autoload, it is already done.\n<contextual_information>\nI get the following error: {error}\nFix my code."
},
"csharp": {
"prompt": "Here's my C# code:\n```csharp\n{code}\n```\n<contextual_information>\nYou have to write C# code with a public static Main method inside a class. The class name is irrelevant. Specify the parameter types. Do not call the main function. You should generally return the result.\nNuGet packages can be added using the format: #r \"nuget: PackageName, Version\" at the top of the script.\nThe Main method signature should be: public static ReturnType Main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"java": {
"prompt": "Here's my Java code:\n```java\n{code}\n```\n<contextual_information>\nYou have to write Java code with a Main public class and a public static main() method. The return type can be Object or void. Do not call the main function. You should generally return the result.\nDependencies can be added using the format: //requirements://groupId:artifactId:version at the top of the script.\nThe method signature should be: public static Object main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
"duckdb": {
"prompt": "Here's my DuckDB code:\n```sql\n{code}\n```\n<contextual_information>\nArguments are defined with comments like `-- $age (text) = 20` or `-- $name (text)` (one per row) and used in the statement with $age, $name, etc. To use Ducklake, attach it with `ATTACH 'ducklake' AS dl;` (for main ducklake) or `ATTACH 'ducklake://name' AS dl;` for named ducklakes. To connect to external databases, use `ATTACH '$res:path/to/resource' AS db (TYPE postgres);`. To read S3 files, use `SELECT * FROM read_csv('s3:///path/to/file.csv');` for default storage or `SELECT * FROM read_csv('s3://secondary_storage_name/path/to/file.csv');` for named storage.\n</contextual_information>\nI get the following error: {error}\nFix my code."
}
}
};
system:
"You are a helpful coding assistant for Windmill, a developer platform for running scripts. You fix the code shared by the user. Each user message includes some contextual information which should guide your answer.\nOnly output code. Wrap the code in a code block. \nExplain the error and the fix after generating the code inside an <explanation> tag.\nAlso put explanations directly in the code as comments.\nReturn the complete fixed code.\n\nHere's how interactions have to look like:\nuser: {sample_question}\nassistant: ```language\n{code}\n```\n<explanation>{explanation}</explanation>",
prompts: {
python3: {
prompt:
'Here\'s my python3 code: \n```python\n{code}\n```\n<contextual_information>\nYou have to write a function in python called "main". Specify the parameter types. Do not call the main function. You should generally return the result.\nThe "main" function cannot be async. If you need to use async code, you can use the asyncio library.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nThe resource type name has to be exactly as specified (has to be IN LOEWRCASE). If the type name conflicts with any imported methods, you have to rename the imported method with the conflicting name.\n<contextual_information>\nI get the following error: {error}\nFix my code.'
},
deno: {
prompt:
'Here\'s my TypeScript code in a deno running environment:\n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
rust: {
prompt:
"Here's my Rust code:\n```rust\n{code}\n```\n<contextual_information>\nPlease define a `main` function in Rust with this signature:\n```rust\npub fn main(...) -> Result<ReturnType, Box<dyn std::error::Error>>\n```\nbut do not call it. Favor idiomatic Rust patterns, ensuring safe handling of ownership and borrowing, robust error handling with `Result`, and concurrency if needed (`async`/`tokio` or std threading). \n\n- Include necessary imports and modules only when needed.\n- Add comments explaining important operations and any unsafe usage (if absolutely required).\n- The generated code should be easily executable and testable in an integrated terminal.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
go: {
prompt:
'Here\'s my go code: \n```go\n{code}\n```\n<contextual_information>\nWe have to export a "main" function. Import the packages you need. The return type of the function has to be ({return_type}, error). The file package has to be "inner"\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
bash: {
prompt:
'Here\'s my bash code: \n```shell\n{code}\n```\n<contextual_information>\nDo 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.\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
postgresql: {
prompt:
"Here's my PostgreSQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with `$1::{type}`, `$2::{type}`, etc... Name the parameters (without specifying the type) by adding comments before the statement like that: `-- $1 name1` or `-- $2 name = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
mysql: {
prompt:
"Here's my MySQL code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
bigquery: {
prompt:
"Here's my BigQuery code: \n```sql\n{code}\n```\n<contextual_information>\nYou can define arguments by adding comments before the statement like that: `-- @name1 ({type})` or `-- @name2 ({type}) = default` (one per row). They can then be obtained directly in the statement with `@name1`, `@name2`, etc....\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
snowflake: {
prompt:
"Here's my snowflake code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with ?. Name the parameters by adding comments before the statement like that: `-- ? name1 ({type})` or `-- ? name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
mssql: {
prompt:
"Here's my Microsoft SQL Server code: \n```sql\n{code}\n```\n<contextual_information>\nArguments can be obtained directly in the statement with @P1, @P2, etc.. Name the parameters by adding comments before the statement like that: `-- @P1 name1 ({type})` or `-- @P2 name2 ({type}) = default` (one per row)\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
graphql: {
prompt:
"Here's my graphql code: \n```graphql\n{code}\n```\n<contextual_information>\nAdd the needed arguments as query parameters.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
powershell: {
prompt:
'Here\'s my powershell code: \n```powershell\n{code}\n```\n<contextual_information>\nArguments can be obtained by calling the param function on the first line like that: `param($ParamName1, $ParamName2 = "default value", [{type}]$ParamName3, ...)`\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
nativets: {
prompt:
'Here\'s my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource". \nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
bun: {
prompt:
'Here\'s my TypeScript code: \n```typescript\n{code}\n```\n<contextual_information>\nWe have to export a "main" function like this: "export async function main(...)" and specify the parameter types but do not call it. You should generally return the result.\nIf needed, the standard fetch method is available globally, do not import it.\nYou can take as parameters resources which are dictionaries containing credentials or configuration information. Name the resource parameters like this: "{resource_type}Resource".\nThe following resource types are available:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nOnly define the type for resources that are actually needed to achieve the function purpose. The resource type name has to be exactly as specified. If the type name conflicts with the imported object, rename the imported object NOT THE TYPE.\nNaming conventions are guidelines, but follow the user\'s naming choices in the existing code.\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
php: {
prompt:
'Here\'s my php code: \n```php\n{code}\n```\n<contextual_information>\nYou have to write a function in php called "main". Specify the parameter types. Do not call the main function. You should generally return the result. The script must start with `<?php`.\nYou can take as parameters resources which are classes containing credentials or configuration information. For Windmill to correctly detect the resources to be passed, the resource type name has to be exactly as specified in the following list:\n<resourceTypes>\n{resourceTypes}\n</resourceTypes>\nYou need to define the type of the resources that are needed before the main function, but only include them if they are actually needed to achieve the function purpose.\nBefore defining each type, check if the class already exists using class_exists.\nThe resource type name has to be exactly as specified.\nIf you need to import libraries, you need to specify them as comments in the following manner before the main function:\n```\n// require:\n// mylibrary/mylibrary\n// myotherlibrary/myotherlibrary@optionalversion\n```\nNo need to require autoload, it is already done.\n<contextual_information>\nI get the following error: {error}\nFix my code.'
},
csharp: {
prompt:
'Here\'s my C# code:\n```csharp\n{code}\n```\n<contextual_information>\nYou have to write C# code with a public static Main method inside a class. The class name is irrelevant. Specify the parameter types. Do not call the main function. You should generally return the result.\nNuGet packages can be added using the format: #r "nuget: PackageName, Version" at the top of the script.\nThe Main method signature should be: public static ReturnType Main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nI get the following error: {error}\nFix my code.'
},
java: {
prompt:
"Here's my Java code:\n```java\n{code}\n```\n<contextual_information>\nYou have to write Java code with a Main public class and a public static main() method. The return type can be Object or void. Do not call the main function. You should generally return the result.\nDependencies can be added using the format: //requirements://groupId:artifactId:version at the top of the script.\nThe method signature should be: public static Object main(parameter types...)\nArguments are used to generate the input specification and create the frontend UI for the script.\n</contextual_information>\nI get the following error: {error}\nFix my code."
},
duckdb: {
prompt:
"Here's my DuckDB code:\n```sql\n{code}\n```\n<contextual_information>\nArguments are defined with comments like `-- $age (text) = 20` or `-- $name (text)` (one per row) and used in the statement with $age, $name, etc. To use Ducklake, attach it with `ATTACH 'ducklake' AS dl;` (for main ducklake) or `ATTACH 'ducklake://name' AS dl;` for named ducklakes. To connect to external databases, use `ATTACH '$res:path/to/resource' AS db (TYPE postgres);`. To read S3 files, use `SELECT * FROM read_csv('s3:///path/to/file.csv');` for default storage or `SELECT * FROM read_csv('s3://secondary_storage_name/path/to/file.csv');` for named storage.\n</contextual_information>\nI get the following error: {error}\nFix my code."
}
}
}

Some files were not shown because too many files have changed in this diff Show More