mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 16:09:39 +00:00
Merge branch 'main' into free-token-limit
Resolve conflicts: ee-repo-ref (EE branch merged with EE main), NoItemFound (keep branch's no-welcome layout), AIChatManager (keep both refreshFreeTierUsage + QueuedEntry), ItemsList (integrate FilterSearchbar UX onto main's #10297 merged-runnables data layer). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,19 @@
|
||||
---
|
||||
name: pr
|
||||
user_invocable: true
|
||||
description: Open a draft pull request on GitHub. MUST use when you want to create/open a PR.
|
||||
description: Open a draft pull request on GitHub and drive CI review rounds until it is ready. MUST use when you want to create/open a PR.
|
||||
---
|
||||
|
||||
# Pull Request Skill
|
||||
|
||||
Create a draft pull request with a clear title and explicit description of changes.
|
||||
Create a draft pull request with a clear title and explicit description of changes, then drive it through CI review rounds to ready.
|
||||
|
||||
## Instructions
|
||||
|
||||
1. **Analyze branch changes**: Understand all commits since diverging from main
|
||||
2. **Push to remote**: Ensure all commits are pushed
|
||||
3. **Create draft PR**: Always open as draft for review before merging
|
||||
4. **Drive review rounds**: trigger CI reviews on the draft and only flip to ready once every verdict is a go (see "Review rounds" below)
|
||||
|
||||
## PR Title Format
|
||||
|
||||
@@ -124,6 +125,42 @@ and continue once they confirm it's done.
|
||||
)"
|
||||
```
|
||||
9. Return the PR URL to the user
|
||||
10. Drive the PR through CI review rounds to ready (see "Review rounds" below)
|
||||
|
||||
## Review rounds (draft → ready)
|
||||
|
||||
A PR leaves draft **only after a clean CI review round**. Never run `gh pr ready` before that.
|
||||
|
||||
1. **Trigger a round and wait for it**: launch the waiter as a background Bash task (a round takes 10–30 min; you are woken when it exits — do not stop the session or poll in the foreground while it runs):
|
||||
|
||||
```bash
|
||||
bash .agents/skills/pr/review-round.sh <PR_NUMBER>
|
||||
```
|
||||
|
||||
It comments `/review` on the PR — which runs the Codex, Claude and Pi CI reviewers even on a draft — waits for the spawned `PR Review Commands` workflow run(s) to complete, then prints one verdict line per reviewer and saves the full review comments to files.
|
||||
|
||||
`/review` (and `/codex`) are **idempotent per head SHA**: if a running or successful review already covers the current head, they skip that agent and post nothing new — the waiter reads the existing verdict for that head, so a skipped agent is *not* a missing one. A cancelled/failed head run is re-run in place; a fresh run is launched only when nothing covers the head. So an unchanged-head re-review is a near no-op, not a new round — push a commit to get genuinely fresh reviews.
|
||||
|
||||
2. **Judge the round.** Codex is mandatory; Claude, Pi and cubic count whenever they posted. Every review starts with one of the three `REVIEW.md` verdicts:
|
||||
- Codex verdict missing → the round is void: the waiter warns only when the head has no green Codex run (cancelled/failed/absent — not merely skipped-because-already-reviewed). Comment `/codex` on the PR, which re-runs the interrupted run in place (or launches one if none exists), wait the same way, and judge again.
|
||||
- Any **"Should address issues before merging"** → fix the P0/P1 findings (and the nits while you're there), commit, push, and start a new round (step 1).
|
||||
- Only **"Mergeable, but should ideally address nits"** and/or **"Good to merge"** → fix the nits too; a nit that is wrong or genuinely not worth fixing may instead be dismissed by replying to the review comment with your reasoning. Push nit-only fixes without starting another full round.
|
||||
|
||||
3. **Flip to ready with the marker comment.** The review workflows skip the redundant `ready_for_review`-triggered round when the PR author has posted a marker naming the current head SHA **and** the PR's latest Codex review *posted before the marker* has a non-blocking verdict (reviewer evidence — a bare marker with no round behind it, or one whose last pre-marker Codex verdict is "Should address issues", skips nothing). Keep the prefix exact and use the full 40-char SHA of the head you are flipping:
|
||||
- every verdict was "Good to merge" (head unchanged since the round):
|
||||
|
||||
`✅ Review round clean @ <head-sha>`
|
||||
|
||||
- nit-only round, nits fixed or dismissed afterwards (head may have moved past the reviewed SHA — say so):
|
||||
|
||||
`✅ Review round clean @ <head-sha> — nit-only verdicts at <round-sha>; nits addressed in <commit sha(s)> / dismissed in review replies`
|
||||
|
||||
```bash
|
||||
gh pr comment <PR_NUMBER> --body "✅ Review round clean @ $(git rev-parse HEAD)"
|
||||
gh pr ready <PR_NUMBER>
|
||||
```
|
||||
|
||||
If any P0/P1 finding is unaddressed or the head moved for reasons other than nit fixes, do **not** post the marker or flip — run another round instead.
|
||||
|
||||
## EE Companion PR (when `*_ee.rs` files were modified)
|
||||
|
||||
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
# Trigger a CI review round on a PR and wait for it to finish.
|
||||
#
|
||||
# Usage: bash .agents/skills/pr/review-round.sh [PR_NUMBER]
|
||||
# PR_NUMBER defaults to the current branch's PR.
|
||||
#
|
||||
# Comments `/review` on the PR (works on drafts), waits for the spawned
|
||||
# "PR Review Commands" workflow run(s) to complete, then prints one verdict
|
||||
# line per reviewer and saves each full review comment to a file. A round
|
||||
# takes 10-30 minutes: run this in the background and act on its output when
|
||||
# it exits, per the pr skill ("Review rounds").
|
||||
set -euo pipefail
|
||||
|
||||
REPO=${REPO:-$(gh repo view --json nameWithOwner --jq .nameWithOwner)}
|
||||
PR=${1:-$(gh pr view --json number --jq .number)}
|
||||
|
||||
# Timestamp of the trigger comment, straight from GitHub, so local clock skew
|
||||
# can't make the run/comment filters below miss part of the round.
|
||||
TRIGGER_TIME=$(gh api "repos/$REPO/issues/$PR/comments" -f body='/review' --jq .created_at)
|
||||
echo "Review round triggered on $REPO#$PR at $TRIGGER_TIME"
|
||||
|
||||
# Retry wrapper for one-off gh/API hiccups: a 45-minute wait must not die on
|
||||
# a single transient failure.
|
||||
retry() {
|
||||
local attempt
|
||||
for attempt in 1 2 3; do
|
||||
if "$@"; then return 0; fi
|
||||
sleep 10
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# The /review comment spawns one "PR Review Commands" run holding the
|
||||
# claude/codex/pi jobs. Runs aren't linked to a PR, so wait on every run of
|
||||
# that workflow created after the trigger: a concurrent round on another PR
|
||||
# can only delay the answer, never truncate it. Every issue comment on any PR
|
||||
# spawns a fast-completing parse run of the same workflow, so the round's own
|
||||
# run may briefly lag the listing while unrelated runs already show completed:
|
||||
# require the all-completed state to hold past a floor and across two
|
||||
# consecutive polls before trusting it.
|
||||
DEADLINE=$(( $(date +%s) + 45 * 60 ))
|
||||
NO_RUN_DEADLINE=$(( $(date +%s) + 5 * 60 ))
|
||||
MIN_WAIT_UNTIL=$(( $(date +%s) + 3 * 60 ))
|
||||
STABLE=0
|
||||
FAILURES=0
|
||||
while :; do
|
||||
if RUNS=$(gh run list --repo "$REPO" --workflow=pr-review-commands.yml \
|
||||
--created ">=$TRIGGER_TIME" --limit 100 --json status); then
|
||||
FAILURES=0
|
||||
else
|
||||
FAILURES=$(( FAILURES + 1 ))
|
||||
if [ "$FAILURES" -ge 5 ]; then
|
||||
echo "ERROR: listing workflow runs failed $FAILURES times in a row; aborting the wait." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "WARNING: listing workflow runs failed (attempt $FAILURES/5); retrying in 60s." >&2
|
||||
sleep 60
|
||||
continue
|
||||
fi
|
||||
TOTAL=$(jq length <<<"$RUNS")
|
||||
PENDING=$(jq '[.[] | select(.status != "completed")] | length' <<<"$RUNS")
|
||||
NOW=$(date +%s)
|
||||
if [ "$TOTAL" -gt 0 ] && [ "$PENDING" -eq 0 ] && [ "$NOW" -gt "$MIN_WAIT_UNTIL" ]; then
|
||||
STABLE=$(( STABLE + 1 ))
|
||||
if [ "$STABLE" -ge 2 ]; then
|
||||
break
|
||||
fi
|
||||
else
|
||||
STABLE=0
|
||||
fi
|
||||
if [ "$TOTAL" -eq 0 ] && [ "$NOW" -gt "$NO_RUN_DEADLINE" ]; then
|
||||
echo "ERROR: no 'PR Review Commands' run appeared within 5 minutes of the /review comment; check that the comment author has write access and the workflow is enabled." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$NOW" -gt "$DEADLINE" ]; then
|
||||
echo "WARNING: review round still pending after 45 minutes; reporting whatever has been posted so far." >&2
|
||||
break
|
||||
fi
|
||||
sleep 60
|
||||
done
|
||||
|
||||
# Head SHA at trigger time. `/review` is idempotent per head: it skips an agent a
|
||||
# running/successful review already covers, re-runs a cancelled/failed one in place on a
|
||||
# separate head-tied run, and launches fresh only when nothing covers the head. Verdict
|
||||
# reading below therefore keys off the head, not just the trigger timestamp.
|
||||
HEAD_SHA=$(retry gh api "repos/$REPO/pulls/$PR" --jq .head.sha)
|
||||
echo "Reviewing head $HEAD_SHA"
|
||||
|
||||
# Newest non-skipped run of <workflow> tied to the head ("status conclusion"), or empty
|
||||
# when none exists. A re-run-in-place or an already-covering review resolves on such a
|
||||
# head-tied run — separate from the pr-review-commands run waited on above (a fresh
|
||||
# launch instead runs inside it, and posts after the trigger). A `skipped` run is the
|
||||
# draft/fork gate and produced no review, so it is ignored.
|
||||
head_run_state() {
|
||||
gh run list --repo "$REPO" --workflow "$1" --commit "$HEAD_SHA" --limit 20 \
|
||||
--json databaseId,status,conclusion \
|
||||
--jq '[.[] | select(.conclusion != "skipped")] | sort_by(.databaseId) | last | if . then "\(.status) \(.conclusion // "-")" else empty end' 2>/dev/null || true
|
||||
}
|
||||
|
||||
# A re-run-in-place review lands on a head-tied run that finishes after the fast
|
||||
# pr-review-commands run, so let those settle before reading verdicts.
|
||||
for wf in codex-pr-review.yml pi-pr-review.yml pr-ready-review.yml; do
|
||||
while :; do
|
||||
case "$(head_run_state "$wf")" in
|
||||
""|"completed "*) break ;;
|
||||
*) if [ "$(date +%s)" -gt "$DEADLINE" ]; then break; fi; sleep 30 ;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
OUT_DIR=$(mktemp -d -t review-round-XXXXXX)
|
||||
COMMENTS_RAW=$(retry gh api "repos/$REPO/issues/$PR/comments?per_page=100" --paginate)
|
||||
# Two views: comments from THIS round (after the trigger) and the full history. A fresh
|
||||
# launch posts after the trigger; an idempotent skip leaves the covering verdict in the
|
||||
# earlier run's comment, so fall back to history when that agent's head run is green.
|
||||
jq -s --arg t "$TRIGGER_TIME" '[.[][] | select(.created_at > $t)]' \
|
||||
<<<"$COMMENTS_RAW" > "$OUT_DIR/comments.json"
|
||||
jq -s '[.[][]]' <<<"$COMMENTS_RAW" > "$OUT_DIR/comments-all.json"
|
||||
# cubic posts through the PR reviews API, not issue comments.
|
||||
REVIEWS_RAW=$(retry gh api "repos/$REPO/pulls/$PR/reviews?per_page=100" --paginate)
|
||||
jq -s --arg t "$TRIGGER_TIME" '[.[][] | select((.submitted_at // "") > $t)]' \
|
||||
<<<"$REVIEWS_RAW" > "$OUT_DIR/pr-reviews.json"
|
||||
|
||||
VERDICT_RE='(Good to merge|Mergeable, but should ideally address nits|Should address issues before merging)'
|
||||
|
||||
body_by_header() { # <file> <header-substring>
|
||||
jq -r --arg h "$2" '[.[] | select(.body // "" | contains($h))] | last | .body // empty' "$1"
|
||||
}
|
||||
body_by_login() { # <file> <login>
|
||||
jq -r --arg l "$2" '[.[] | select(.user.login == $l)] | last | .body // empty' "$1"
|
||||
}
|
||||
head_ok() { [ "$(head_run_state "$1")" = "completed success" ]; }
|
||||
# Latest verdict for a reviewer: prefer this round's comment; if none and the reviewer's
|
||||
# head run succeeded (an idempotent /review skipped re-reviewing an already-green head),
|
||||
# fall back to the covering comment from the full history.
|
||||
verdict_body() { # <header|login> <value> <workflow>
|
||||
local body
|
||||
body=$("body_by_$1" "$OUT_DIR/comments.json" "$2")
|
||||
if [ -z "$body" ] && head_ok "$3"; then
|
||||
body=$("body_by_$1" "$OUT_DIR/comments-all.json" "$2")
|
||||
fi
|
||||
printf '%s' "$body"
|
||||
}
|
||||
report() { # <reviewer-name> <comment-body>
|
||||
local name=$1 body=$2 verdict
|
||||
if [ -z "$body" ]; then
|
||||
echo "$name: (no review posted for this head)"
|
||||
return
|
||||
fi
|
||||
printf '%s\n' "$body" > "$OUT_DIR/$name.md"
|
||||
verdict=$(printf '%s\n' "$body" | grep -m1 -oE "${VERDICT_RE}.*" | sed 's/\*\*//g' || true)
|
||||
echo "$name: ${verdict:-(review posted but no verdict line; read $OUT_DIR/$name.md)}"
|
||||
}
|
||||
|
||||
echo
|
||||
echo "=== Review round verdicts for $REPO#$PR (head $HEAD_SHA) ==="
|
||||
CODEX_BODY=$(verdict_body header '## Codex Review' codex-pr-review.yml)
|
||||
report codex "$CODEX_BODY"
|
||||
report claude "$(verdict_body login 'claude[bot]' pr-ready-review.yml)"
|
||||
report pi "$(verdict_body header '## Pi Review' pi-pr-review.yml)"
|
||||
CUBIC_BODY=$(jq -r '[.[] | select(.user.login | test("^cubic(-dev-ai)?(\\[bot\\])?$"; "i"))] | last | .body // empty' \
|
||||
"$OUT_DIR/pr-reviews.json")
|
||||
if [ -z "$CUBIC_BODY" ]; then
|
||||
CUBIC_BODY=$(jq -r '[.[] | select(.user.login | test("^cubic(-dev-ai)?(\\[bot\\])?$"; "i"))] | last | .body // empty' \
|
||||
"$OUT_DIR/comments.json")
|
||||
fi
|
||||
report cubic "$CUBIC_BODY"
|
||||
echo
|
||||
echo "Full round output: $OUT_DIR (comments.json, pr-reviews.json, one .md per reviewer)"
|
||||
if [ -z "$CODEX_BODY" ]; then
|
||||
echo "WARNING: no Codex verdict for $HEAD_SHA - its head run is not green (cancelled/failed/absent, not merely skipped-because-already-reviewed). Re-trigger with a '/codex' PR comment (re-runs the interrupted run in place, or launches one) and wait again." >&2
|
||||
fi
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
# PreToolUse guard for `rm`: auto-allow ONLY a single, plain, single-line `rm` whose every
|
||||
# operand is a whitelisted target — under /tmp, or inside a git working tree located in $HOME
|
||||
# (a version-controlled project dir). Anything else makes no decision (exit 0) and falls back
|
||||
# to the normal permission flow, where the `Bash(rm:*)` ask rule prompts (classifier as a
|
||||
# backstop).
|
||||
#
|
||||
# The git-tree allowance trades on "this is a project under version control" being lower-stakes
|
||||
# than a delete elsewhere — NOT on full recoverability: committed content is restorable via git,
|
||||
# but untracked / .gitignore'd / uncommitted content, and an independent nested repo's history
|
||||
# under a recursively-deleted parent, are NOT. Accepted as a deliberate convenience tradeoff.
|
||||
#
|
||||
# Deny-by-default: every token must consist only of a safe character set (alphanumerics,
|
||||
# `. _ / -` and glob chars `* ? [ ]`). That set contains none of the characters bash uses for
|
||||
# quoting, expansion, or command separation ($ ` ~ { } ( ) ' " \ ; & | < >), so those forms
|
||||
# fail by construction rather than needing to be enumerated. `realpath -m` then resolves `..`
|
||||
# and existing symlinks (so a symlink out of the allowed roots is caught), and a wildcard in a
|
||||
# non-final path segment is refused because it can expand through a symlink realpath can't see.
|
||||
#
|
||||
# The git-repo allowance covers targets inside a git working tree under $HOME, and the tree's
|
||||
# own root folder only when it is a linked worktree (`.git` is a pointer file, so history in
|
||||
# the main repo survives); a primary checkout's root (`.git` is a history dir) and any `.git`
|
||||
# path are never auto-allowed. Globs auto-allow only under /tmp — elsewhere their expansion
|
||||
# could reach `.git` or a dotfile the literal checks never see. Relative operands resolve
|
||||
# against the command's cwd (from the hook input). A PreToolUse `allow` overrides the ask rule.
|
||||
#
|
||||
# Assumes GNU `realpath` (-m) and `jq`, both present in this repo's Linux dev env.
|
||||
set -uo pipefail
|
||||
|
||||
input=$(cat)
|
||||
command -v jq >/dev/null 2>&1 || exit 0
|
||||
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
||||
[ -z "$cmd" ] && exit 0
|
||||
cwd=$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)
|
||||
|
||||
# A newline separates commands, and the tokenizer below only reads the first line — defer.
|
||||
case "$cmd" in *$'\n'*) exit 0 ;; esac
|
||||
|
||||
read -r -a toks <<< "$cmd"
|
||||
# Bare leading `rm` only; wrappers (`timeout rm`), env prefixes, and `/bin/rm` defer.
|
||||
[ "${toks[0]:-}" = "rm" ] || exit 0
|
||||
|
||||
# 0 (allow) iff the canonical path is an auto-allowable rm target: under /tmp, or strictly
|
||||
# inside a git working tree located under $HOME. The walk stops at $HOME, so a dotfiles repo at
|
||||
# ~ can't make all of $HOME deletable, and top-level ~ files stay protected.
|
||||
allowed_target() {
|
||||
local canon="$1" d root=""
|
||||
case "$canon" in /tmp/?*) return 0 ;; esac
|
||||
[ -n "${HOME:-}" ] || return 1
|
||||
case "$canon" in "$HOME"/?*) ;; *) return 1 ;; esac
|
||||
case "$canon" in *"/.git" | *"/.git/"*) return 1 ;; esac # protect history, not recoverable
|
||||
d="$canon"
|
||||
while [ "$d" != "/" ] && [ "$d" != "$HOME" ]; do
|
||||
[ -e "$d/.git" ] && { root="$d"; break; }
|
||||
d=$(dirname "$d")
|
||||
done
|
||||
[ -n "$root" ] || return 1 # not inside a git working tree under $HOME
|
||||
if [ "$canon" = "$root" ]; then
|
||||
# Deleting the repo root folder itself: allow only for a linked worktree, whose `.git` is
|
||||
# a file/pointer so the history lives in the main repo and survives. A primary checkout's
|
||||
# `.git` is a directory holding the history, so deleting it is unrecoverable — defer.
|
||||
[ -f "$root/.git" ] && return 0
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
had_operand=0
|
||||
end_opts=0
|
||||
i=1
|
||||
while [ "$i" -lt "${#toks[@]}" ]; do
|
||||
t="${toks[$i]}"
|
||||
i=$((i + 1))
|
||||
# Whitelist every token (flags included, so an operator hidden in a flag like `-rf;rm`
|
||||
# can't slip past): any character outside the safe set makes it unsafe to reason about.
|
||||
[ -n "$(printf '%s' "$t" | tr -d 'A-Za-z0-9._/*?[]-')" ] && exit 0
|
||||
# A glob in an option-looking token (`-[-]`) can expand to `--` and turn a later `-name`
|
||||
# into an operand — never a real option, so defer.
|
||||
case "$t" in -*[*?[]*) exit 0 ;; esac
|
||||
if [ "$end_opts" = 0 ]; then
|
||||
[ "$t" = "--" ] && { end_opts=1; continue; }
|
||||
# Skip real options only before the first operand. A bare `-` is a filename, and under
|
||||
# POSIXLY_CORRECT GNU rm stops option parsing at the first operand, so a later `-name`
|
||||
# is a filename too — validate it rather than skipping it.
|
||||
if [ "$had_operand" = 0 ]; then
|
||||
case "$t" in -?*) continue ;; esac
|
||||
fi
|
||||
fi
|
||||
had_operand=1
|
||||
# No wildcard in a non-final path segment (`a/*/b`): it can expand through a symlink
|
||||
# realpath can't see. A slashless glob (`*.rs`) is a final-segment match — fine.
|
||||
case "$t" in */*) case "${t%/*}" in *[*?[]*) exit 0 ;; esac ;; esac
|
||||
case "$t" in
|
||||
/*) canon=$(realpath -m -- "$t" 2>/dev/null) ;;
|
||||
*) canon=$(realpath -m -- "${cwd:-$PWD}/$t" 2>/dev/null) ;;
|
||||
esac
|
||||
[ -n "$canon" ] || exit 0
|
||||
# A glob may auto-allow only under /tmp, where everything is deletable. Elsewhere its
|
||||
# expansion could match `.git`, a dotfile like `.*`, or a nested checkout root that the
|
||||
# literal-path checks never see — so require literal operands in git repos.
|
||||
case "$t" in *[*?[]*) case "$canon" in /tmp/?*) ;; *) exit 0 ;; esac ;; esac
|
||||
allowed_target "$canon" || exit 0
|
||||
done
|
||||
|
||||
[ "$had_operand" = 1 ] || exit 0
|
||||
jq -nc '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"rm operands are under /tmp or inside a git checkout in $HOME"}}'
|
||||
+20
-5
@@ -48,9 +48,6 @@
|
||||
"Read(/tmp/**)",
|
||||
"Write(/tmp/**)",
|
||||
"Edit(/tmp/**)",
|
||||
"Bash(rm:/tmp/*)",
|
||||
"Bash(rm:/tmp/**)",
|
||||
"Bash(rmdir:/tmp/*)",
|
||||
"Bash(mkdir:/tmp/*)",
|
||||
"Bash(mkdir:/tmp/**)",
|
||||
"Bash(cp:/tmp/*)",
|
||||
@@ -62,7 +59,12 @@
|
||||
"Bash(chmod:/tmp/*)",
|
||||
"Bash(chmod:/tmp/**)",
|
||||
"Bash(tar * /tmp/*)",
|
||||
"Bash(unzip * /tmp/*)"
|
||||
"Bash(unzip * /tmp/*)",
|
||||
"mcp__claude_ai_Gmail__search_threads",
|
||||
"mcp__claude_ai_Gmail__get_thread",
|
||||
"mcp__claude_ai_Gmail__get_message",
|
||||
"mcp__claude_ai_Gmail__list_labels",
|
||||
"mcp__claude_ai_Gmail__list_drafts"
|
||||
],
|
||||
"deny": [
|
||||
"Read(.env)",
|
||||
@@ -92,7 +94,15 @@
|
||||
"Bash(shred:*)",
|
||||
"Bash(unlink:*)",
|
||||
"mcp__claude_ai_Stripe",
|
||||
"mcp__claude_ai_Gmail",
|
||||
"mcp__claude_ai_Gmail__create_draft",
|
||||
"mcp__claude_ai_Gmail__update_draft",
|
||||
"mcp__claude_ai_Gmail__create_label",
|
||||
"mcp__claude_ai_Gmail__label_message",
|
||||
"mcp__claude_ai_Gmail__label_thread",
|
||||
"mcp__claude_ai_Gmail__unlabel_message",
|
||||
"mcp__claude_ai_Gmail__unlabel_thread",
|
||||
"mcp__claude_ai_Gmail__apply_sensitive_message_label",
|
||||
"mcp__claude_ai_Gmail__apply_sensitive_thread_label",
|
||||
"mcp__claude_ai_Google_Calendar",
|
||||
"mcp__claude_ai_Google_Drive",
|
||||
"mcp__claude_ai_Slack",
|
||||
@@ -109,6 +119,11 @@
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-main-branch.sh",
|
||||
"timeout": 5
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-rm-outside-tmp.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ jobs:
|
||||
with:
|
||||
cache-workspaces: backend
|
||||
toolchain: 1.97.0
|
||||
# This action defaults RUSTFLAGS to "-D warnings"; unset it so the test
|
||||
# run is not failed by cross-platform dead-code (cfg(unix)-only helpers
|
||||
# are unused on Windows). Warning hygiene is enforced on the Linux CI
|
||||
# and the build_windows_worker_ release build, not this test job.
|
||||
rustflags: ""
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
@@ -203,9 +208,15 @@ jobs:
|
||||
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
|
||||
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
|
||||
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
|
||||
# Windows ships a worker-only binary, so test the crates a worker runs
|
||||
# (windmill-worker/-common/-queue) via -p, not `--all`: this skips the
|
||||
# disk-heavy windmill-api test binaries (LNK1180) and the server-only
|
||||
# windmill-trigger-* crates (amqp does not build on Windows). Linux CI runs the rest.
|
||||
run: >
|
||||
cargo test
|
||||
--no-fail-fast
|
||||
--features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,csharp,php,quickjs,mcp,run_inline
|
||||
--all
|
||||
-p windmill-worker
|
||||
-p windmill-common
|
||||
-p windmill-queue
|
||||
--features private,enterprise,deno_core,duckdb,python,rust,csharp,php,quickjs,parquet,mcp,scoped_cache,windmill-git-sync/private,windmill-object-store/private,windmill-object-store/enterprise
|
||||
-- --nocapture --test-threads=10
|
||||
|
||||
@@ -10,6 +10,14 @@ on:
|
||||
- main
|
||||
paths:
|
||||
- docker/DockerfileCaddyL4
|
||||
- docker/entrypoint-caddy.sh
|
||||
- docker/caddy-compat-normalize.awk
|
||||
- docker/caddy-l4.version
|
||||
- docker/test-caddy-compat.sh
|
||||
- Caddyfile
|
||||
# The version check below reads the pin out of docker-compose.yml, so a
|
||||
# compose-only bump has to trigger this workflow or the check never runs.
|
||||
- docker-compose.yml
|
||||
- .github/workflows/build-caddy-l4-image.yml
|
||||
|
||||
permissions: write-all
|
||||
@@ -20,6 +28,35 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: depot/setup-action@v1
|
||||
|
||||
# docker-compose.yml pins an exact tag, and the Caddyfile it must agree
|
||||
# with lives in the same checkout. Fail the build rather than publish a
|
||||
# version nothing references, which is how :latest drifted from the
|
||||
# Caddyfile in the first place.
|
||||
- name: Resolve and check image version
|
||||
id: version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$(tr -d '[:space:]' < docker/caddy-l4.version)"
|
||||
pinned="$(grep -oE 'caddy-l4:[^[:space:]"]+' docker-compose.yml | head -1 | cut -d: -f2-)"
|
||||
caddy="$(grep -m1 -oE '^FROM caddy:[0-9]+\.[0-9]+\.[0-9]+' docker/DockerfileCaddyL4 | cut -d: -f2)"
|
||||
if [ "$version" != "$pinned" ]; then
|
||||
echo "docker/caddy-l4.version is '$version' but docker-compose.yml pins '$pinned'" >&2
|
||||
echo "Bump both together." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Otherwise a caddy bump that forgets the version file publishes a tag
|
||||
# that names the wrong caddy.
|
||||
case "$version" in
|
||||
"$caddy"-*) ;;
|
||||
*)
|
||||
echo "docker/caddy-l4.version is '$version' but the Dockerfile pins caddy '$caddy'" >&2
|
||||
echo "The version must be <caddy-version>-<revision>." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Docker meta
|
||||
id: meta-ee-public
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -29,8 +66,22 @@ jobs:
|
||||
tags: |
|
||||
type=sha
|
||||
type=ref,event=branch
|
||||
# Not gated on the default branch: docker-compose.yml pins this tag,
|
||||
# so it has to be publishable from a branch (workflow_dispatch)
|
||||
# before the pin merges, or main would reference a tag that does not
|
||||
# exist yet. The version is immutable, so republishing from main is
|
||||
# a no-op. Only branch pushes to main and manual dispatch run this
|
||||
# workflow, so a branch cannot claim the tag by accident.
|
||||
type=raw,value=${{ steps.version.outputs.version }}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
# The shim rewrites config a self-hoster never sees, so a silent
|
||||
# regression here strands them on a restart loop or a dead :80.
|
||||
- name: Test the legacy-Caddyfile compatibility shim
|
||||
run: |
|
||||
docker build -f docker/DockerfileCaddyL4 -t caddy-l4:ci ./docker
|
||||
docker/test-caddy-compat.sh caddy-l4:ci
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
||||
@@ -63,6 +63,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ee_rhel
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
|
||||
@@ -65,6 +65,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ee_rhel
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -82,6 +83,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ee_rhel
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
|
||||
@@ -45,8 +45,12 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: "-D warnings"
|
||||
run: |
|
||||
mkdir frontend/build && cd backend
|
||||
cd backend
|
||||
# Stub the openapi specs to empty: they are compiled in via an ungated
|
||||
# include_str! but a worker binary never serves them, so this avoids
|
||||
# embedding ~2.5MB of spec.
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.json" -ItemType "File" -Force
|
||||
cargo check --features=ee_windows
|
||||
|
||||
- name: Cargo build dynamic libraries windows
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
allowed_bots: 'windmill-internal-app[bot]'
|
||||
trigger_phrase: '/plan'
|
||||
claude_args: |
|
||||
--model claude-opus-4-8
|
||||
--model claude-opus-5
|
||||
--system-prompt "# Claude Planning Mode
|
||||
|
||||
You are operating in PLANNING MODE ONLY. Your role is to create detailed, structured plans without making any code changes.
|
||||
|
||||
@@ -93,4 +93,4 @@ jobs:
|
||||
}
|
||||
claude_args: |
|
||||
--allowedTools "Bash,WebFetch,WebSearch"
|
||||
--model claude-opus-4-8
|
||||
--model claude-opus-5
|
||||
|
||||
@@ -82,6 +82,7 @@ jobs:
|
||||
EVENT_BODY: ${{ github.event.pull_request.body }}
|
||||
EVENT_FORK: ${{ github.event.pull_request.head.repo.fork }}
|
||||
EVENT_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
EVENT_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
if [ -n "$INPUT_PR_NUMBER" ]; then
|
||||
PR_JSON=$(gh pr view "$INPUT_PR_NUMBER" --repo "${{ github.repository }}" \
|
||||
@@ -113,6 +114,38 @@ jobs:
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
# An agent-driven PR flips to ready only after a clean /review round on
|
||||
# a draft, marked by an author comment naming the head SHA (pr skill,
|
||||
# "Review rounds"). Re-reviewing that same head on ready_for_review is
|
||||
# redundant. The marker alone is author attestation, so also require
|
||||
# reviewer evidence: a Codex review (posted by github-actions[bot], not
|
||||
# forgeable by the author) that predates the marker and carries a
|
||||
# non-blocking verdict. Comment-triggered and synchronize runs never
|
||||
# skip. Keep the three copies of this check in sync (pr-ready-review /
|
||||
# codex-pr-review / pi-pr-review); a shared local action would need the
|
||||
# repo checked out before the check, which the fork paths here
|
||||
# deliberately avoid.
|
||||
if [ "$EVENT_ACTION" = "ready_for_review" ] && [ -z "$INPUT_PR_NUMBER" ]; then
|
||||
# Fetch failures fail open (no skip): an API hiccup must run the
|
||||
# review, never skip it or fail the job.
|
||||
COMMENTS=$(gh api "repos/${{ github.repository }}/issues/$PR_NUMBER/comments?per_page=100" --paginate | jq -s '[.[][]]') || COMMENTS='[]'
|
||||
MARKER_TIME=$(jq -r --arg author "$PR_AUTHOR" --arg marker "✅ Review round clean @ $HEAD_SHA" \
|
||||
'[.[] | select(.user.login == $author) | select(.body | contains($marker)) | .created_at] | min // empty' <<<"$COMMENTS")
|
||||
CODEX_VERDICT=''
|
||||
if [ -n "$MARKER_TIME" ]; then
|
||||
# Only Codex evidence that predates the marker counts: the ready-
|
||||
# triggered Codex run itself posts after the flip and must not
|
||||
# vouch for a sibling reviewer's skip.
|
||||
CODEX_VERDICT=$(jq -r --arg mt "$MARKER_TIME" \
|
||||
'[.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("## Codex Review")) | select(.created_at < $mt)] | last | .body // ""' <<<"$COMMENTS" \
|
||||
| grep -m1 -oE '(Good to merge|Mergeable, but should ideally address nits|Should address issues before merging)' || true)
|
||||
fi
|
||||
if [ -n "$MARKER_TIME" ] && [ -n "$CODEX_VERDICT" ] && [ "$CODEX_VERDICT" != "Should address issues before merging" ]; then
|
||||
echo "Clean review round marker found for $HEAD_SHA with pre-marker non-blocking Codex verdict; skipping redundant review."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
# PR title/body are attacker-controlled free text. Use an unguessable
|
||||
# per-run delimiter so a fork can't embed a fixed heredoc terminator to
|
||||
# inject extra outputs — e.g. is_fork=false (last-write-wins), which
|
||||
|
||||
@@ -68,6 +68,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ce_rpi
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
|
||||
@@ -93,6 +93,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ce
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
@@ -155,6 +156,7 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
features=ee
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
@@ -254,6 +256,7 @@ jobs:
|
||||
target: debuginfo
|
||||
build-args: |
|
||||
features=ee
|
||||
WM_BUILD_VERSION=${{ github.sha }}
|
||||
outputs: type=local,dest=./debuginfo
|
||||
|
||||
- name: Rename debug file with corresponding architecture
|
||||
|
||||
@@ -9,6 +9,10 @@ on:
|
||||
- "backend/windmill-api-integration-tests/tests/git_sync*"
|
||||
- "backend/ee-repo-ref.txt"
|
||||
- "backend/windmill-common/src/workspaces.rs"
|
||||
- "backend/windmill-worker/src/result_processor.rs"
|
||||
- "backend/windmill-api-workspaces/**"
|
||||
- "cli/src/commands/sync/**"
|
||||
- "cli/src/utils/git.ts"
|
||||
- "integration_tests/test/git_sync_test.py"
|
||||
- ".github/workflows/git-sync-test.yml"
|
||||
pull_request:
|
||||
@@ -18,6 +22,10 @@ on:
|
||||
- "backend/windmill-api-integration-tests/tests/git_sync*"
|
||||
- "backend/ee-repo-ref.txt"
|
||||
- "backend/windmill-common/src/workspaces.rs"
|
||||
- "backend/windmill-worker/src/result_processor.rs"
|
||||
- "backend/windmill-api-workspaces/**"
|
||||
- "cli/src/commands/sync/**"
|
||||
- "cli/src/utils/git.ts"
|
||||
- "integration_tests/test/git_sync_test.py"
|
||||
- ".github/workflows/git-sync-test.yml"
|
||||
|
||||
@@ -50,8 +58,8 @@ jobs:
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED_FILES"
|
||||
|
||||
# Direct git sync file changes — always relevant
|
||||
if echo "$CHANGED_FILES" | grep -qE '^(backend/windmill-git-sync/|backend/windmill-api-integration-tests/tests/git_sync|backend/windmill-common/src/workspaces\.rs|integration_tests/test/git_sync|\.github/workflows/git-sync-test\.yml)'; then
|
||||
# Direct git sync file changes — always relevant.
|
||||
if echo "$CHANGED_FILES" | grep -qE '^(backend/windmill-git-sync/|backend/windmill-worker/src/result_processor\.rs|backend/windmill-api-workspaces/|backend/windmill-api-integration-tests/tests/git_sync|backend/windmill-common/src/workspaces\.rs|cli/src/commands/sync/|cli/src/utils/git\.ts|integration_tests/test/git_sync|\.github/workflows/git-sync-test\.yml)'; then
|
||||
echo "should_run=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Relevant: direct git sync file changes"
|
||||
exit 0
|
||||
@@ -180,6 +188,9 @@ jobs:
|
||||
DENO_PATH: deno
|
||||
BUN_PATH: bun
|
||||
NODE_BIN_PATH: node
|
||||
# The auto-pull poller's SSRF guard rejects localhost git remotes;
|
||||
# the tests' Gitea runs on localhost.
|
||||
ALLOW_LOCAL_GIT_REMOTES: "true"
|
||||
run: |
|
||||
./target/debug/windmill &
|
||||
echo "Waiting for Windmill to be ready..."
|
||||
|
||||
@@ -75,6 +75,7 @@ jobs:
|
||||
EVENT_BODY: ${{ github.event.pull_request.body }}
|
||||
EVENT_FORK: ${{ github.event.pull_request.head.repo.fork }}
|
||||
EVENT_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
EVENT_ACTION: ${{ github.event.action }}
|
||||
run: |
|
||||
if [ -n "$INPUT_PR_NUMBER" ]; then
|
||||
PR_JSON=$(gh pr view "$INPUT_PR_NUMBER" --repo "${{ github.repository }}" \
|
||||
@@ -106,6 +107,38 @@ jobs:
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
# An agent-driven PR flips to ready only after a clean /review round on
|
||||
# a draft, marked by an author comment naming the head SHA (pr skill,
|
||||
# "Review rounds"). Re-reviewing that same head on ready_for_review is
|
||||
# redundant. The marker alone is author attestation, so also require
|
||||
# reviewer evidence: a Codex review (posted by github-actions[bot], not
|
||||
# forgeable by the author) that predates the marker and carries a
|
||||
# non-blocking verdict. Comment-triggered and synchronize runs never
|
||||
# skip. Keep the three copies of this check in sync (pr-ready-review /
|
||||
# codex-pr-review / pi-pr-review); a shared local action would need the
|
||||
# repo checked out before the check, which the fork paths here
|
||||
# deliberately avoid.
|
||||
if [ "$EVENT_ACTION" = "ready_for_review" ] && [ -z "$INPUT_PR_NUMBER" ]; then
|
||||
# Fetch failures fail open (no skip): an API hiccup must run the
|
||||
# review, never skip it or fail the job.
|
||||
COMMENTS=$(gh api "repos/${{ github.repository }}/issues/$PR_NUMBER/comments?per_page=100" --paginate | jq -s '[.[][]]') || COMMENTS='[]'
|
||||
MARKER_TIME=$(jq -r --arg author "$PR_AUTHOR" --arg marker "✅ Review round clean @ $HEAD_SHA" \
|
||||
'[.[] | select(.user.login == $author) | select(.body | contains($marker)) | .created_at] | min // empty' <<<"$COMMENTS")
|
||||
CODEX_VERDICT=''
|
||||
if [ -n "$MARKER_TIME" ]; then
|
||||
# Only Codex evidence that predates the marker counts: the ready-
|
||||
# triggered Codex run itself posts after the flip and must not
|
||||
# vouch for a sibling reviewer's skip.
|
||||
CODEX_VERDICT=$(jq -r --arg mt "$MARKER_TIME" \
|
||||
'[.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("## Codex Review")) | select(.created_at < $mt)] | last | .body // ""' <<<"$COMMENTS" \
|
||||
| grep -m1 -oE '(Good to merge|Mergeable, but should ideally address nits|Should address issues before merging)' || true)
|
||||
fi
|
||||
if [ -n "$MARKER_TIME" ] && [ -n "$CODEX_VERDICT" ] && [ "$CODEX_VERDICT" != "Should address issues before merging" ]; then
|
||||
echo "Clean review round marker found for $HEAD_SHA with pre-marker non-blocking Codex verdict; skipping redundant review."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
# PR title/body are attacker-controlled free text. Use an unguessable
|
||||
# per-run delimiter so a fork can't embed a fixed heredoc terminator to
|
||||
# inject extra outputs — e.g. is_fork=false (last-write-wins), which
|
||||
|
||||
@@ -48,12 +48,55 @@ jobs:
|
||||
pull-requests: read
|
||||
id-token: write
|
||||
steps:
|
||||
# An agent-driven PR flips to ready only after a clean /review round on a
|
||||
# draft, marked by an author comment naming the head SHA (pr skill, "Review
|
||||
# rounds"). Re-reviewing that same head on ready_for_review is redundant.
|
||||
# The marker alone is author attestation, so also require reviewer evidence:
|
||||
# a Codex review (posted by github-actions[bot], not forgeable by the author)
|
||||
# that predates the marker and carries a non-blocking verdict. Comment-
|
||||
# triggered (workflow_call) and opened runs never skip. Keep the three
|
||||
# copies of this check in sync (pr-ready-review / codex-pr-review /
|
||||
# pi-pr-review); a shared local action would need the repo checked out
|
||||
# before the check, which the codex/pi fork paths deliberately avoid.
|
||||
- name: Check clean-round marker
|
||||
id: marker
|
||||
if: github.event_name == 'pull_request' && github.event.action == 'ready_for_review'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
run: |
|
||||
# Fetch failures fail open (skip=false): an API hiccup must run the
|
||||
# review, never skip it or fail the job.
|
||||
COMMENTS=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments?per_page=100" --paginate | jq -s '[.[][]]') || COMMENTS='[]'
|
||||
MARKER_TIME=$(jq -r --arg author "$PR_AUTHOR" --arg marker "✅ Review round clean @ $HEAD_SHA" \
|
||||
'[.[] | select(.user.login == $author) | select(.body | contains($marker)) | .created_at] | min // empty' <<<"$COMMENTS")
|
||||
CODEX_VERDICT=''
|
||||
if [ -n "$MARKER_TIME" ]; then
|
||||
# Only Codex evidence that predates the marker counts: the ready-
|
||||
# triggered Codex run itself posts after the flip and must not vouch
|
||||
# for a sibling reviewer's skip.
|
||||
CODEX_VERDICT=$(jq -r --arg mt "$MARKER_TIME" \
|
||||
'[.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("## Codex Review")) | select(.created_at < $mt)] | last | .body // ""' <<<"$COMMENTS" \
|
||||
| grep -m1 -oE '(Good to merge|Mergeable, but should ideally address nits|Should address issues before merging)' || true)
|
||||
fi
|
||||
if [ -n "$MARKER_TIME" ] && [ -n "$CODEX_VERDICT" ] && [ "$CODEX_VERDICT" != "Should address issues before merging" ]; then
|
||||
echo "Clean review round marker found for $HEAD_SHA with pre-marker non-blocking Codex verdict; skipping redundant review."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check EE access
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
id: ee
|
||||
env:
|
||||
EE_TOKEN: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
@@ -80,6 +123,7 @@ jobs:
|
||||
run: ./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- name: Resolve PR number
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
id: resolve
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -99,6 +143,7 @@ jobs:
|
||||
echo "pr_author=$PR_AUTHOR" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Fetch prior PR discussion
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
id: prior
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -117,6 +162,7 @@ jobs:
|
||||
' prior-comments.json > prior-comments.md
|
||||
|
||||
- name: Read review prompt
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
id: review-prompt
|
||||
env:
|
||||
EXTRA_PROMPT: ${{ inputs.extra_prompt }}
|
||||
@@ -140,6 +186,7 @@ jobs:
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Automatic PR Review
|
||||
if: steps.marker.outputs.skip != 'true'
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
@@ -152,4 +199,4 @@ jobs:
|
||||
${{ env.REVIEW_PROMPT }}
|
||||
claude_args: |
|
||||
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
|
||||
--model claude-opus-4-8
|
||||
--model claude-opus-5
|
||||
|
||||
@@ -75,14 +75,153 @@ jobs:
|
||||
"/repos/$REPO/issues/comments/$COMMENT_ID/reactions" \
|
||||
-f content=eyes >/dev/null
|
||||
|
||||
claude:
|
||||
# Decide, per agent, whether to launch a fresh run, re-run in place, or skip. A push
|
||||
# already auto-triggers codex/pi (and claude on open) against the PR head. Relaunching
|
||||
# via this issue_comment path both cancels those in-flight auto runs (shared concurrency
|
||||
# group) AND lands the new run's status on main — issue_comment runs never attach a
|
||||
# check to the PR head — leaving the PR showing only a cancelled review. So for every
|
||||
# command, launch an agent only when nothing covers the head commit; if the head's run
|
||||
# was cancelled/failed, re-run it in place (a re-run keeps the original pull_request
|
||||
# event, so its checks re-attach to the PR head); skip when a running or successful run
|
||||
# already covers it. `/review` applies this to all three agents; `/codex`, `/pi`,
|
||||
# `/claude` apply the same decision to just their own agent.
|
||||
plan:
|
||||
needs: [parse, check-access]
|
||||
if: |
|
||||
needs.parse.outputs.command != '' &&
|
||||
(
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) ||
|
||||
needs.check-access.outputs.authorized == 'true'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
pull-requests: read
|
||||
statuses: write
|
||||
outputs:
|
||||
head_sha: ${{ steps.plan.outputs.head_sha }}
|
||||
launch_codex: ${{ steps.plan.outputs.launch_codex }}
|
||||
launch_pi: ${{ steps.plan.outputs.launch_pi }}
|
||||
launch_claude: ${{ steps.plan.outputs.launch_claude }}
|
||||
steps:
|
||||
- name: Decide per-agent launch vs re-run for the head commit
|
||||
id: plan
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.issue.number }}
|
||||
COMMAND: ${{ needs.parse.outputs.command }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
HEAD_SHA=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json headRefOid --jq '.headRefOid')
|
||||
echo "PR #$PR_NUMBER head: $HEAD_SHA"
|
||||
echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
RUN_URL="$GITHUB_SERVER_URL/$REPO/actions/runs/$GITHUB_RUN_ID"
|
||||
|
||||
# A fresh launch runs from this issue_comment workflow (associated with main),
|
||||
# so it never appears in the PR-head run query below and its own check lands on
|
||||
# main, not the head. To keep fresh launches idempotent per head, mark the head
|
||||
# SHA with a `review-launch/<agent>` commit status at launch; the `finalize` job
|
||||
# resolves it to success/failure. A prior launch's status covering the head lets
|
||||
# a second comment skip instead of relaunching (which would cancel the first via
|
||||
# the reviewer's shared concurrency group). All status calls are best-effort — a
|
||||
# GitHub API hiccup must degrade to a relaunch, never abort the decision.
|
||||
mark_launch() {
|
||||
agent="$1"
|
||||
gh api -X POST "repos/$REPO/statuses/$HEAD_SHA" \
|
||||
-f state=pending -f "context=review-launch/$agent" -f "target_url=$RUN_URL" \
|
||||
-f "description=Review launched via /$COMMAND" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
# Returns "covered" if a prior fresh launch (this or an earlier comment run)
|
||||
# already covers the head: a success status, or a pending status whose launching
|
||||
# run is still alive. A pending whose run has completed is stale (that run
|
||||
# crashed before finalize) and does not count.
|
||||
launch_coverage() {
|
||||
agent="$1"
|
||||
st_json=$(gh api "repos/$REPO/commits/$HEAD_SHA/statuses" \
|
||||
--jq "[.[] | select(.context == \"review-launch/$agent\")] | first // empty" 2>/dev/null || true)
|
||||
[ -n "$st_json" ] || return 0
|
||||
state=$(jq -r '.state // empty' <<<"$st_json" 2>/dev/null || true)
|
||||
[ "$state" = success ] && { echo covered; return 0; }
|
||||
[ "$state" = pending ] || return 0
|
||||
target=$(jq -r '.target_url // empty' <<<"$st_json" 2>/dev/null || true)
|
||||
run_id=$(printf '%s' "$target" | grep -oE '[0-9]+$' || true)
|
||||
if [ -n "$run_id" ]; then
|
||||
run_state=$(gh run view "$run_id" --repo "$REPO" --json status --jq '.status' 2>/dev/null || true)
|
||||
[ "$run_state" = completed ] && return 0 # stale pending -> not covered
|
||||
fi
|
||||
echo covered
|
||||
}
|
||||
|
||||
decide() {
|
||||
wf="$1"; key="$2"; agent="$3"
|
||||
if [ "$(launch_coverage "$agent")" = covered ]; then
|
||||
echo "$key: a prior launch already covers $HEAD_SHA (review-launch/$agent) -> skip"
|
||||
echo "$key=false" >> "$GITHUB_OUTPUT"
|
||||
return
|
||||
fi
|
||||
# `--commit` matches runs whose head SHA is the PR head. Auto reviews run on
|
||||
# `pull_request` against that SHA; `/review` (issue_comment) runs execute on
|
||||
# main, so they never match and are not counted as covering the head commit.
|
||||
runs=$(gh run list --repo "$REPO" --workflow "$wf" --commit "$HEAD_SHA" --limit 40 \
|
||||
--json databaseId,status,conclusion)
|
||||
# Healthy = still running, or completed successfully: a review already
|
||||
# covers this commit, so skip.
|
||||
healthy=$(jq -r '[.[] | select(.status != "completed" or .conclusion == "success")] | length' <<<"$runs")
|
||||
if [ "$healthy" -gt 0 ]; then
|
||||
echo "$key: a running or successful review already covers $HEAD_SHA -> skip"
|
||||
echo "$key=false" >> "$GITHUB_OUTPUT"
|
||||
return
|
||||
fi
|
||||
# Re-run only genuinely interrupted runs (cancelled/failed/timed out) in
|
||||
# place, so their checks re-attach to the PR head instead of posting on
|
||||
# main. A `skipped` run produced no review and would just skip again (it is
|
||||
# the draft/fork gate), so it does not count — fall through to a fresh launch.
|
||||
retry_id=$(jq -r '[.[] | select(.status == "completed" and (.conclusion == "cancelled" or .conclusion == "failure" or .conclusion == "timed_out"))] | sort_by(.databaseId) | last | .databaseId // empty' <<<"$runs")
|
||||
if [ -n "$retry_id" ]; then
|
||||
if gh run rerun "$retry_id" --repo "$REPO" >/dev/null 2>&1; then
|
||||
echo "$key: re-ran interrupted run $retry_id (re-attaches to PR head)"
|
||||
echo "$key=false" >> "$GITHUB_OUTPUT"
|
||||
return
|
||||
fi
|
||||
echo "$key: re-run of $retry_id failed -> fresh launch"
|
||||
mark_launch "$agent"
|
||||
echo "$key=true" >> "$GITHUB_OUTPUT"
|
||||
return
|
||||
fi
|
||||
echo "$key: no usable review for $HEAD_SHA -> launch"
|
||||
mark_launch "$agent"
|
||||
echo "$key=true" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
|
||||
# `/review` targets all three agents; `/codex`, `/pi`, `/claude` target only
|
||||
# their own. A non-targeted agent is left untouched (no launch, no re-run).
|
||||
decide_if_targeted() {
|
||||
wf="$1"; key="$2"; agent="$3"
|
||||
if [ "$COMMAND" = review ] || [ "$COMMAND" = "$agent" ]; then
|
||||
decide "$wf" "$key" "$agent"
|
||||
else
|
||||
echo "$key: /$COMMAND does not target $agent -> skip"
|
||||
echo "$key=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
}
|
||||
|
||||
decide_if_targeted codex-pr-review.yml launch_codex codex
|
||||
decide_if_targeted pi-pr-review.yml launch_pi pi
|
||||
decide_if_targeted pr-ready-review.yml launch_claude claude
|
||||
|
||||
claude:
|
||||
needs: [parse, check-access, plan]
|
||||
if: |
|
||||
(
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) ||
|
||||
needs.check-access.outputs.authorized == 'true'
|
||||
) &&
|
||||
(needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'claude')
|
||||
needs.plan.outputs.launch_claude == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
@@ -97,13 +236,13 @@ jobs:
|
||||
WINDMILL_EE_PRIVATE_ACCESS: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
|
||||
codex:
|
||||
needs: [parse, check-access]
|
||||
needs: [parse, check-access, plan]
|
||||
if: |
|
||||
(
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) ||
|
||||
needs.check-access.outputs.authorized == 'true'
|
||||
) &&
|
||||
(needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'codex')
|
||||
needs.plan.outputs.launch_codex == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -119,13 +258,13 @@ jobs:
|
||||
WINDMILL_EE_PRIVATE_ACCESS: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
|
||||
pi:
|
||||
needs: [parse, check-access]
|
||||
needs: [parse, check-access, plan]
|
||||
if: |
|
||||
(
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) ||
|
||||
needs.check-access.outputs.authorized == 'true'
|
||||
) &&
|
||||
(needs.parse.outputs.command == 'review' || needs.parse.outputs.command == 'pi')
|
||||
needs.plan.outputs.launch_pi == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -138,3 +277,34 @@ jobs:
|
||||
secrets:
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
WINDMILL_EE_PRIVATE_ACCESS: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
|
||||
# Resolve the `review-launch/<agent>` head statuses that `plan` set to pending, so a
|
||||
# fresh launch's outcome is visible on the PR head (not just on main) and never lingers
|
||||
# as a stale pending check. Targets the exact SHA `plan` launched against, so a push
|
||||
# that moved the head mid-review does not stamp a status on the new head.
|
||||
finalize:
|
||||
needs: [plan, claude, codex, pi]
|
||||
if: always() && needs.plan.result == 'success' && needs.plan.outputs.head_sha != ''
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
statuses: write
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
HEAD_SHA: ${{ needs.plan.outputs.head_sha }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
steps:
|
||||
- name: Finalize launch statuses on the PR head
|
||||
run: |
|
||||
set -uo pipefail
|
||||
finalize() {
|
||||
agent="$1"; launched="$2"; result="$3"
|
||||
[ "$launched" = true ] || return 0
|
||||
state=$([ "$result" = success ] && echo success || echo failure)
|
||||
gh api -X POST "repos/$REPO/statuses/$HEAD_SHA" \
|
||||
-f "state=$state" -f "context=review-launch/$agent" -f "target_url=$RUN_URL" \
|
||||
-f "description=Review $result" >/dev/null 2>&1 || true
|
||||
}
|
||||
finalize codex "${{ needs.plan.outputs.launch_codex }}" "${{ needs.codex.result }}"
|
||||
finalize pi "${{ needs.plan.outputs.launch_pi }}" "${{ needs.pi.result }}"
|
||||
finalize claude "${{ needs.plan.outputs.launch_claude }}" "${{ needs.claude.result }}"
|
||||
|
||||
@@ -56,8 +56,12 @@ jobs:
|
||||
vcpkg.exe integrate install
|
||||
$env:VCPKGRS_DYNAMIC=1
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
cd backend
|
||||
# Stub the openapi specs to empty: they are compiled in via an ungated
|
||||
# include_str! but a worker binary never serves them, so this avoids
|
||||
# embedding ~2.5MB of spec.
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.json" -ItemType "File" -Force
|
||||
cargo build --release --features=ee_windows
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
|
||||
+277
@@ -1,5 +1,282 @@
|
||||
# Changelog
|
||||
|
||||
## [1.770.0](https://github.com/windmill-labs/windmill/compare/v1.769.0...v1.770.0) (2026-07-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add explore button for object storage resources ([#10306](https://github.com/windmill-labs/windmill/issues/10306)) ([28a79ce](https://github.com/windmill-labs/windmill/commit/28a79ced155e6bb3540ab76feb5415ea8f3d8f8f))
|
||||
* Add image when publishing a project ([#10310](https://github.com/windmill-labs/windmill/issues/10310)) ([48618cf](https://github.com/windmill-labs/windmill/commit/48618cff8c35a345babd12844653f83addbcd7e8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not apply workspace display name on git-sync pull ([#10308](https://github.com/windmill-labs/windmill/issues/10308)) ([992ed01](https://github.com/windmill-labs/windmill/commit/992ed0124492f5c6b9e3dcc6a309caeda8d6a7b1))
|
||||
* pass Windows system env vars to R renv install subprocess ([#10313](https://github.com/windmill-labs/windmill/issues/10313)) ([85008e4](https://github.com/windmill-labs/windmill/commit/85008e47b4a21313ec66496d3c68eb746fc8a151))
|
||||
* show scheduled singlestepflow runs in flow history sidebar ([#10312](https://github.com/windmill-labs/windmill/issues/10312)) ([f00fcb2](https://github.com/windmill-labs/windmill/commit/f00fcb2d1b1030b47c824bdefc9f0d12499bc6b6))
|
||||
* WAC wait_for_approval reads its own approval result, not the first ([#10314](https://github.com/windmill-labs/windmill/issues/10314)) ([2143d45](https://github.com/windmill-labs/windmill/commit/2143d45815af7a95182721aeb0dc937a12c5759f))
|
||||
|
||||
## [1.769.0](https://github.com/windmill-labs/windmill/compare/v1.768.0...v1.769.0) (2026-07-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **hub:** surface data pipelines in deploy-to-hub drawer ([#10299](https://github.com/windmill-labs/windmill/issues/10299)) ([010059a](https://github.com/windmill-labs/windmill/commit/010059a449f4d0cde0656086f1b3f03a02a1c78c))
|
||||
* **pipeline:** collapse secondary top-bar controls into an overflow menu ([#10300](https://github.com/windmill-labs/windmill/issues/10300)) ([1d25d75](https://github.com/windmill-labs/windmill/commit/1d25d7539ed7fb17c47a732d6b1cd7fc19202a04))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pin table actions column so it stays visible on narrow screens ([#10301](https://github.com/windmill-labs/windmill/issues/10301)) ([75acf72](https://github.com/windmill-labs/windmill/commit/75acf7207b151f0227600be555bfa5e14dfa8dd1))
|
||||
* pin validated DNS address to close SSRF DNS-rebinding TOCTOU ([#10303](https://github.com/windmill-labs/windmill/issues/10303)) ([3cf7a39](https://github.com/windmill-labs/windmill/commit/3cf7a390a37c07a818248c95defa4ecf5bf262e5))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* optimize get_datatable_full_schema to avoid timeout on large catalogs ([#10304](https://github.com/windmill-labs/windmill/issues/10304)) ([1478d12](https://github.com/windmill-labs/windmill/commit/1478d12eb352b1b7906ccfe3ab5eaafe60ffbe4f))
|
||||
|
||||
## [1.768.0](https://github.com/windmill-labs/windmill/compare/v1.767.0...v1.768.0) (2026-07-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add section to deploy projects to hub ([#9332](https://github.com/windmill-labs/windmill/issues/9332)) ([30eedf9](https://github.com/windmill-labs/windmill/commit/30eedf9ee1754cb2bbf46e82d2b3eac766bd3e6d))
|
||||
* **ai-chat:** let the session/global chat create email triggers ([#10282](https://github.com/windmill-labs/windmill/issues/10282)) ([248c875](https://github.com/windmill-labs/windmill/commit/248c8751b1fd5d6f7506a7c3fc8f500dd98af26e))
|
||||
* **ai:** enable data pipelines in AI sessions with alpha notice ([#10273](https://github.com/windmill-labs/windmill/issues/10273)) ([1f912f4](https://github.com/windmill-labs/windmill/commit/1f912f410401ec68789d43973d6e0a1f8e595f86))
|
||||
* alert on expired online license key ([#10295](https://github.com/windmill-labs/windmill/issues/10295)) ([30d8104](https://github.com/windmill-labs/windmill/commit/30d8104edcbdb7f0c63f186d22ac03000e0d4f07))
|
||||
* data-pipeline recorder, interactive player, and deploy-to-hub recording (WIN-2156) ([#10055](https://github.com/windmill-labs/windmill/issues/10055)) ([65e5041](https://github.com/windmill-labs/windmill/commit/65e504146d832b105603ceec89c6de0ad1d66e32))
|
||||
* let a workspace fall back to the instance critical alert channels ([#10292](https://github.com/windmill-labs/windmill/issues/10292)) ([717e38a](https://github.com/windmill-labs/windmill/commit/717e38a0c6b5bb2340e236a9d49645a4cebf4849))
|
||||
* **monitor:** make between-steps zombie flows hand-recoverable ([#10287](https://github.com/windmill-labs/windmill/issues/10287)) ([f02df7f](https://github.com/windmill-labs/windmill/commit/f02df7fc454b0c2a2afa9ae9848e26af0e379246))
|
||||
* surface workspace-script advanced settings in flow editor ([#10289](https://github.com/windmill-labs/windmill/issues/10289)) ([bf16e7d](https://github.com/windmill-labs/windmill/commit/bf16e7d49a7486d37cf9eb1907e80abae47a78a7))
|
||||
* **windows:** enable ruby and rlang on the windows worker ([#10279](https://github.com/windmill-labs/windmill/issues/10279)) ([0f1b864](https://github.com/windmill-labs/windmill/commit/0f1b8641f26f0eda8a8517db1a37efafdb173464))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ci:** make /review idempotent per head commit, re-run cancelled reviews in place ([#10283](https://github.com/windmill-labs/windmill/issues/10283)) ([3aaceb7](https://github.com/windmill-labs/windmill/commit/3aaceb7efb3bd232da73b79fcdcf4c712caad7d3))
|
||||
* **cli:** surface shared UI changes in sync push dry-run preview ([#10278](https://github.com/windmill-labs/windmill/issues/10278)) ([14c29b7](https://github.com/windmill-labs/windmill/commit/14c29b77e90842b8c3d4395ce50cb44666cd998c))
|
||||
* **jobs:** sanitize NUL in completed job result before jsonb insert ([#10274](https://github.com/windmill-labs/windmill/issues/10274)) ([c50a2ab](https://github.com/windmill-labs/windmill/commit/c50a2abad0c222bf8f76ff10b864450120ee9ee7))
|
||||
* **monitor:** diagnose zombie-flow OOM on the transition worker, not q.worker ([#10286](https://github.com/windmill-labs/windmill/issues/10286)) ([fa36442](https://github.com/windmill-labs/windmill/commit/fa3644281f30d90aa0c4ca86520a38db5d8b91f0))
|
||||
* resolve svelte/style export conditions in raw-app CLI bundler ([#10294](https://github.com/windmill-labs/windmill/issues/10294)) ([9713e60](https://github.com/windmill-labs/windmill/commit/9713e6074d2df55db1331cf31ee1357606b1dbe9))
|
||||
* **resources:** apply resource_type changes on update (git-sync pull) — Fixes GIT-932 ([#10277](https://github.com/windmill-labs/windmill/issues/10277)) ([d7a0078](https://github.com/windmill-labs/windmill/commit/d7a0078b58f74f1ffecc03b348e8935a70f02e55))
|
||||
* surface workspace ids on duplicate names and explain fork promotion ([#10291](https://github.com/windmill-labs/windmill/issues/10291)) ([9b182aa](https://github.com/windmill-labs/windmill/commit/9b182aaf3879d4d81b6a785222dca75f085e6fb7))
|
||||
* treat concurrent_limit/timeout <= 0 as unset instead of a zero cap ([#10288](https://github.com/windmill-labs/windmill/issues/10288)) ([8eb36ce](https://github.com/windmill-labs/windmill/commit/8eb36ce008b4efe2be9a9bfebc91af68070f7a6c))
|
||||
|
||||
## [1.767.0](https://github.com/windmill-labs/windmill/compare/v1.766.2...v1.767.0) (2026-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai-chat:** email triggers in flow/script chat + trigger-intent eval guards (WIN-2228) ([#10267](https://github.com/windmill-labs/windmill/issues/10267)) ([e41440b](https://github.com/windmill-labs/windmill/commit/e41440b344d94ebe5746a89237c3d1be522aa59d))
|
||||
* **ai:** improve data-pipeline building in AI sessions (prompt + evals + e2e) ([#10270](https://github.com/windmill-labs/windmill/issues/10270)) ([0819641](https://github.com/windmill-labs/windmill/commit/0819641f3a89abdf114cc5e341a9f585e5d71296))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ai-chat:** improve resource-type search tool description and scoring ([#10272](https://github.com/windmill-labs/windmill/issues/10272)) ([ad53673](https://github.com/windmill-labs/windmill/commit/ad53673a2855e787af9fe10dcc8998ffc7f1b960))
|
||||
* **embeddings:** retry on failed init instead of disabling for a day ([#10266](https://github.com/windmill-labs/windmill/issues/10266)) ([2318481](https://github.com/windmill-labs/windmill/commit/2318481f4f2932dc578d5cb64c4c705cf5c324a0))
|
||||
* manual resource type sync fetches from hub first, cache as fallback ([#10269](https://github.com/windmill-labs/windmill/issues/10269)) ([07d4b67](https://github.com/windmill-labs/windmill/commit/07d4b674f1dbd24c89f7d1e77094a40e528a3740))
|
||||
|
||||
## [1.766.2](https://github.com/windmill-labs/windmill/compare/v1.766.1...v1.766.2) (2026-07-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **python,windows:** cross-platform cross-process wheel-install lock ([#10264](https://github.com/windmill-labs/windmill/issues/10264)) ([7e2f1af](https://github.com/windmill-labs/windmill/commit/7e2f1afffb4982099870e7ce32592cf3a447e513))
|
||||
|
||||
## [1.766.1](https://github.com/windmill-labs/windmill/compare/v1.766.0...v1.766.1) (2026-07-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **jobs:** enforce self_approval_disabled on the UI resume path ([#10262](https://github.com/windmill-labs/windmill/issues/10262)) ([2d24b3a](https://github.com/windmill-labs/windmill/commit/2d24b3ac49b65469072bca929730d2c9ecd54b8f))
|
||||
|
||||
## [1.766.0](https://github.com/windmill-labs/windmill/compare/v1.765.0...v1.766.0) (2026-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai-sessions:** show item preview cards for tools ([#10254](https://github.com/windmill-labs/windmill/issues/10254)) ([703744f](https://github.com/windmill-labs/windmill/commit/703744fb8b76f70384cbc3341d5e1b3dbf30455e))
|
||||
* make content search a full CE feature ([#10252](https://github.com/windmill-labs/windmill/issues/10252)) ([d2c5d6f](https://github.com/windmill-labs/windmill/commit/d2c5d6f4b4ede9449407ed3831713d3e3d1d6972))
|
||||
* **sessions:** ship AI sessions as beta with legacy-chat opt-out ([#10242](https://github.com/windmill-labs/windmill/issues/10242)) ([0508cdd](https://github.com/windmill-labs/windmill/commit/0508cddf0a86b6a6a043cb31978ca68b76a95d02))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* accept ssh/scheme-less git repo urls and $var: refs in app repo resolution ([#10246](https://github.com/windmill-labs/windmill/issues/10246)) ([b948efd](https://github.com/windmill-labs/windmill/commit/b948efd3c81aada103bd141eae03fbe626bf6ee5))
|
||||
* **copilot:** stop write_flow forcing rawscript code into nested JSON ([#10260](https://github.com/windmill-labs/windmill/issues/10260)) ([ecb1a92](https://github.com/windmill-labs/windmill/commit/ecb1a92070fb18048a6a1467270469e8462a3cbf))
|
||||
* prevent u16 underflow in suspend count causing permanent flow deadlock ([#10256](https://github.com/windmill-labs/windmill/issues/10256)) ([68b1fcc](https://github.com/windmill-labs/windmill/commit/68b1fcc5cdd3d217ebb1b9e09c0d52334faf0a63))
|
||||
* **prompts:** prefer Bun over Deno for TypeScript runtime selection ([#10253](https://github.com/windmill-labs/windmill/issues/10253)) ([380cf75](https://github.com/windmill-labs/windmill/commit/380cf752ca8fc57eb70f7c8efd57290f7902a030))
|
||||
* **tutorials:** repair broken frontend tutorials after UI redesigns ([#10255](https://github.com/windmill-labs/windmill/issues/10255)) ([5685981](https://github.com/windmill-labs/windmill/commit/5685981c9902ff37c3bd271dc5c5ede948a0787a))
|
||||
|
||||
## [1.765.0](https://github.com/windmill-labs/windmill/compare/v1.764.0...v1.765.0) (2026-07-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai:** open the Compare & Deploy page from chat with item preselection ([#10232](https://github.com/windmill-labs/windmill/issues/10232)) ([572d69e](https://github.com/windmill-labs/windmill/commit/572d69e5ae8ae12207dca175fdb5ea378d852a45))
|
||||
* **ai:** session chat nits — empty sends, command picker polish, session-state prompt ([#10233](https://github.com/windmill-labs/windmill/issues/10233)) ([9bc1f62](https://github.com/windmill-labs/windmill/commit/9bc1f6212837155c964a7db7103aabbcf14cbce2))
|
||||
* attach text files to chat messages, read on demand via file tools ([#10215](https://github.com/windmill-labs/windmill/issues/10215)) ([d6cf1ef](https://github.com/windmill-labs/windmill/commit/d6cf1ef9872cd0db0f51e4881237bac383e45f61))
|
||||
* **git-sync:** enable per-item promotion mode on dev workspaces ([#10205](https://github.com/windmill-labs/windmill/issues/10205)) ([2ce21c9](https://github.com/windmill-labs/windmill/commit/2ce21c9ef86c608d73e12a7e278d2f328e06b6fa))
|
||||
* **pipelines:** catalog declared measures and dimensions ([#10190](https://github.com/windmill-labs/windmill/issues/10190)) ([fd51d40](https://github.com/windmill-labs/windmill/commit/fd51d40f1254a206de1f38e59cc3dadce8e13aa5))
|
||||
* **triggers:** add AMQP (RabbitMQ) trigger via lapin ([#10230](https://github.com/windmill-labs/windmill/issues/10230)) ([68debab](https://github.com/windmill-labs/windmill/commit/68debab877c6dc8ee3732e0c23d467db85fd4584))
|
||||
* unified read-only `diff` chat tool (drafts, fork vs parent, search) ([#10211](https://github.com/windmill-labs/windmill/issues/10211)) ([9739d5a](https://github.com/windmill-labs/windmill/commit/9739d5a2c2a21beded8848ea9049b2816196a898))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **apps:** let entitled viewers read pre-existing S3 files from deployed apps ([#10245](https://github.com/windmill-labs/windmill/issues/10245)) ([4a89824](https://github.com/windmill-labs/windmill/commit/4a898247a21ae918fa952a993fbe407ebc49e404))
|
||||
* **db:** repair s3 asset paths missing default-storage leading slash ([#10243](https://github.com/windmill-labs/windmill/issues/10243)) ([555c751](https://github.com/windmill-labs/windmill/commit/555c751016fea4087be09fa2520af331cc872bfc))
|
||||
* **frontend:** curl fallback for +Variable/+Resource in bash sandbox mode ([#10235](https://github.com/windmill-labs/windmill/issues/10235)) ([28966bd](https://github.com/windmill-labs/windmill/commit/28966bdbf190ac461aa5259e04d6c15cf699fd94))
|
||||
* **frontend:** limit compare & deploy rows to the active direction ([#10234](https://github.com/windmill-labs/windmill/issues/10234)) ([7ac27c1](https://github.com/windmill-labs/windmill/commit/7ac27c1ef240729fc38d5fa506b96bf60be74e25))
|
||||
* **frontend:** prevent browser back-swipe navigation over monaco editors ([#10229](https://github.com/windmill-labs/windmill/issues/10229)) ([b0bf256](https://github.com/windmill-labs/windmill/commit/b0bf25683ba3aac3e94bbe9d6bbd6e22545e734e))
|
||||
* **parsers:** keep s3 asset path suffix verbatim to preserve storage distinction ([#10241](https://github.com/windmill-labs/windmill/issues/10241)) ([7fb8a2e](https://github.com/windmill-labs/windmill/commit/7fb8a2e390cef3cd01a33c89ee55d3f53cb1e20f))
|
||||
* **parser:** spurious pg arg inferred from placeholders in comments ([#10226](https://github.com/windmill-labs/windmill/issues/10226)) ([d24e176](https://github.com/windmill-labs/windmill/commit/d24e1768163c10fbbc144bd1126457e42d282e37))
|
||||
* **pg:** actionable error when s3object input exceeds jsonb 256MB cap ([#10228](https://github.com/windmill-labs/windmill/issues/10228)) ([6e42633](https://github.com/windmill-labs/windmill/commit/6e4263364325e4d4616697c6da601bbc887ff43c))
|
||||
* **postgres-triggers:** enforce resource-path scopes on ancillary routes ([#10222](https://github.com/windmill-labs/windmill/issues/10222)) ([39058c0](https://github.com/windmill-labs/windmill/commit/39058c0a01c35cf553daae3af37c6d326a7c8763))
|
||||
* return to parent workspace when a fork is deleted remotely ([#9898](https://github.com/windmill-labs/windmill/issues/9898)) ([32994df](https://github.com/windmill-labs/windmill/commit/32994df427e6df6747caf1ba6371512d5a5be5c2))
|
||||
* **s3:** support instance-policy credentials in object storage tests ([#10238](https://github.com/windmill-labs/windmill/issues/10238)) ([ec63244](https://github.com/windmill-labs/windmill/commit/ec6324409d6c0e9ddcda5d110ac570ca79fe9e38))
|
||||
* **triggers:** apply scope-path filtering to list and fix update scope check ([#10220](https://github.com/windmill-labs/windmill/issues/10220)) ([adc555d](https://github.com/windmill-labs/windmill/commit/adc555d1722cb59aa5b88503b3105357d76f5b71))
|
||||
* **worker:** mount /dev/shm as tmpfs in the Docker v2 nsjail sandbox ([#10240](https://github.com/windmill-labs/windmill/issues/10240)) ([2caee41](https://github.com/windmill-labs/windmill/commit/2caee41fdfe1e69010a1f4544d45aaa5db49f590))
|
||||
|
||||
## [1.764.0](https://github.com/windmill-labs/windmill/compare/v1.763.0...v1.764.0) (2026-07-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai:** add npm package search tool to global chat ([#10204](https://github.com/windmill-labs/windmill/issues/10204)) ([0593ff7](https://github.com/windmill-labs/windmill/commit/0593ff7d7d598af0b9f52d08bb22147e3a83e6e9))
|
||||
* **ai:** expose get_db_schema tool in global chat ([#10207](https://github.com/windmill-labs/windmill/issues/10207)) ([f4308cf](https://github.com/windmill-labs/windmill/commit/f4308cf033b75e9a69c1e21041ff97f3bfa1787c))
|
||||
* **ai:** extract prompt cache token usage from OpenAI and Azure providers ([#10214](https://github.com/windmill-labs/windmill/issues/10214)) ([2b58df5](https://github.com/windmill-labs/windmill/commit/2b58df57fc5bae08155ffd7198643ed739e6a463))
|
||||
* **ai:** live web-search source list on chat tool cards ([#10210](https://github.com/windmill-labs/windmill/issues/10210)) ([542a484](https://github.com/windmill-labs/windmill/commit/542a4842a317a6607de973b4567bdb27dc088664))
|
||||
* cap queued jobs per concurrency key on cloud ([#10197](https://github.com/windmill-labs/windmill/issues/10197)) ([71f2d47](https://github.com/windmill-labs/windmill/commit/71f2d47cb4ff331cbcc7843bf0704ddb63cb77db))
|
||||
* detect server-handled git-sync so CLI picks git push vs wmill sync push ([#10201](https://github.com/windmill-labs/windmill/issues/10201)) ([b070f56](https://github.com/windmill-labs/windmill/commit/b070f56c5e6109c219a53cbb9628e28be4397fb7))
|
||||
* expose windmill api endpoint catalog to global ai chat ([#10199](https://github.com/windmill-labs/windmill/issues/10199)) ([83a354f](https://github.com/windmill-labs/windmill/commit/83a354f831cbc1821f2316e6d4e1c7f7a2cd7cdb))
|
||||
* **jobs:** cap total queued jobs per workspace on cloud ([#10218](https://github.com/windmill-labs/windmill/issues/10218)) ([ddec2ab](https://github.com/windmill-labs/windmill/commit/ddec2abbb3f56fc44d4c75fde60f00fb1c6373bb))
|
||||
* **sessions:** live DOM access for the raw-app preview in AI sessions ([#10129](https://github.com/windmill-labs/windmill/issues/10129)) ([b448af1](https://github.com/windmill-labs/windmill/commit/b448af1da7f6a351973724721e52682259212292))
|
||||
* **telemetry:** generic feature-usage telemetry with AI session metrics ([#10200](https://github.com/windmill-labs/windmill/issues/10200)) ([11fda89](https://github.com/windmill-labs/windmill/commit/11fda89b520cc8d1ce30cdba36bd10355cf025cc))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db:** grant schema usage and re-run windmill role grants ([#10212](https://github.com/windmill-labs/windmill/issues/10212)) ([38ceae1](https://github.com/windmill-labs/windmill/commit/38ceae1a098ae165c75f43aa9049b5afafdd3bac))
|
||||
* **forks:** show workspace settings link in sidebar for fork creators ([#10216](https://github.com/windmill-labs/windmill/issues/10216)) ([11bb37d](https://github.com/windmill-labs/windmill/commit/11bb37d7ca7fbd50cc66905f41851226a48e57fb))
|
||||
* **git-sync:** avoid percent-encoded colon in git-sync hub script path ([#10213](https://github.com/windmill-labs/windmill/issues/10213)) ([87be041](https://github.com/windmill-labs/windmill/commit/87be041c091dd7fdd8c6be907a9b2894779f9ac0))
|
||||
* **inputs:** add ownership check to saved-input args read endpoint ([#10194](https://github.com/windmill-labs/windmill/issues/10194)) ([f32d770](https://github.com/windmill-labs/windmill/commit/f32d7702bcb592a70f5cf7e01f9e830047645240))
|
||||
* **schedules:** apply scope-path filtering to schedule list endpoints ([#10192](https://github.com/windmill-labs/windmill/issues/10192)) ([70359e3](https://github.com/windmill-labs/windmill/commit/70359e3a76d2e1fa5994acce4909753410c80664))
|
||||
* **security:** enforce token scope filtering on folder list endpoints ([#10193](https://github.com/windmill-labs/windmill/issues/10193)) ([b5e69ff](https://github.com/windmill-labs/windmill/commit/b5e69ffba6afd49d147e9b5e88e2d7020c09142f))
|
||||
* **sessions:** session bar badge readouts, job persistence, refresh bounce ([#10217](https://github.com/windmill-labs/windmill/issues/10217)) ([c8870d3](https://github.com/windmill-labs/windmill/commit/c8870d36aed2283bfa14419cd0bb346083f27b9c))
|
||||
* steer ai chat away from draft-blind api catalog reads and runs ([#10202](https://github.com/windmill-labs/windmill/issues/10202)) ([0e04bc6](https://github.com/windmill-labs/windmill/commit/0e04bc6991486b2b2b05b13feed5b083e403ac80))
|
||||
|
||||
## [1.763.0](https://github.com/windmill-labs/windmill/compare/v1.762.2...v1.763.0) (2026-07-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai:** gate data pipelines in sessions behind a dev flag ([#10178](https://github.com/windmill-labs/windmill/issues/10178)) ([42da20a](https://github.com/windmill-labs/windmill/commit/42da20ae9743eab2f86b8f9c261ee567f8bbb0c5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** stop emitting has_on_behalf_of/has_permissioned_as: false on pull ([#10188](https://github.com/windmill-labs/windmill/issues/10188)) ([2d77e74](https://github.com/windmill-labs/windmill/commit/2d77e742077b71be192a04ed9e85d4a024dc7f91))
|
||||
|
||||
## [1.762.2](https://github.com/windmill-labs/windmill/compare/v1.762.1...v1.762.2) (2026-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **scripts:** populate auto_kind from draft JSON for draft-only scripts ([#10183](https://github.com/windmill-labs/windmill/issues/10183)) ([cba5f0d](https://github.com/windmill-labs/windmill/commit/cba5f0d8a8c2261a190d0f10396a6ad6aed89366))
|
||||
|
||||
## [1.762.1](https://github.com/windmill-labs/windmill/compare/v1.762.0...v1.762.1) (2026-07-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **schedules:** stop disabling schedules on transient push errors ([#10179](https://github.com/windmill-labs/windmill/issues/10179)) ([c82056c](https://github.com/windmill-labs/windmill/commit/c82056cfde93ab0e26cf82aa4f7650c060565542))
|
||||
* **worker:** gate ansible socket-dir name check to unix ([#10180](https://github.com/windmill-labs/windmill/issues/10180)) ([203f6c6](https://github.com/windmill-labs/windmill/commit/203f6c69dd24fc055a4bf604315aa757f35d0565))
|
||||
|
||||
## [1.762.0](https://github.com/windmill-labs/windmill/compare/v1.761.0...v1.762.0) (2026-07-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai-chat:** image attachments and agent raw-app screenshots ([#10130](https://github.com/windmill-labs/windmill/issues/10130)) ([7a139ab](https://github.com/windmill-labs/windmill/commit/7a139ab23e034321554d0c85540d54251e61cc72))
|
||||
* **forks:** let a fork's creator manage developers on it without being an admin ([#10166](https://github.com/windmill-labs/windmill/issues/10166)) ([97f4477](https://github.com/windmill-labs/windmill/commit/97f44770698eb3dd9e296012360b7d9a4492e28d))
|
||||
* **otel-tracing-proxy:** trust internal endpoints with untrusted CAs ([#10139](https://github.com/windmill-labs/windmill/issues/10139)) ([396fb1c](https://github.com/windmill-labs/windmill/commit/396fb1c4752b2b93ef0ec9d0c9c6be05aa403eef))
|
||||
* **worker-tags:** add `*` fork marker to workspace-scoped custom tags ([#10177](https://github.com/windmill-labs/windmill/issues/10177)) ([2ff5a91](https://github.com/windmill-labs/windmill/commit/2ff5a918d54074bc340b6e92222e9f5bb4369ba0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ai:** unbreak session chat compaction for Anthropic models ([#10171](https://github.com/windmill-labs/windmill/issues/10171)) ([a9fc9f7](https://github.com/windmill-labs/windmill/commit/a9fc9f74b2c5b280cc087f0bfbac50112081c5e8))
|
||||
* **ansible:** keep persistent-connection socket path under the AF_UNIX limit ([#10167](https://github.com/windmill-labs/windmill/issues/10167)) ([be57dd9](https://github.com/windmill-labs/windmill/commit/be57dd91e40551ff0d6fbbeccd97e2b98589c5b9))
|
||||
* **extra:** make the extra container runnable as a non-root UID ([#10173](https://github.com/windmill-labs/windmill/issues/10173)) ([dcb9e40](https://github.com/windmill-labs/windmill/commit/dcb9e40ea237c3744611ac6e83ff5ecc6528e697))
|
||||
* **flows:** make updateFlow body path optional so AI can update flows ([#10176](https://github.com/windmill-labs/windmill/issues/10176)) ([7d2c5ce](https://github.com/windmill-labs/windmill/commit/7d2c5ceb0fb8609aa58a1e28e61ae8ce86e48208))
|
||||
* **frontend:** scope session pipeline trigger editors to the session workspace ([#10032](https://github.com/windmill-labs/windmill/issues/10032)) ([8828341](https://github.com/windmill-labs/windmill/commit/8828341a2b0912cbf44d6c6d81b4f4e30c5fe631))
|
||||
* **mcp:** apply token scopes consistently across mcp endpoint tools ([#10162](https://github.com/windmill-labs/windmill/issues/10162)) ([ae3d9ce](https://github.com/windmill-labs/windmill/commit/ae3d9ce2c031beeafc4e8156e0c85f163647af0f))
|
||||
* parse all names in grouped go param declarations ([#10165](https://github.com/windmill-labs/windmill/issues/10165)) ([d0aa7dc](https://github.com/windmill-labs/windmill/commit/d0aa7dca13aedfec90b7e023954cc824dafa3d34))
|
||||
* **schedules:** re-arm enabled schedules left with no queued occurrence ([#10174](https://github.com/windmill-labs/windmill/issues/10174)) ([9762089](https://github.com/windmill-labs/windmill/commit/9762089fcbdb33dbf453d22342661e5857b2ad7e))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* lazy-load session preview editor views for snappy AI sessions switch ([#10172](https://github.com/windmill-labs/windmill/issues/10172)) ([1edee8a](https://github.com/windmill-labs/windmill/commit/1edee8aa34503d2892c1ef7b1deabf99e4b528b4))
|
||||
|
||||
## [1.761.0](https://github.com/windmill-labs/windmill/compare/v1.760.1...v1.761.0) (2026-07-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ai-sessions:** CRUD markdown artifacts in sessions ([#10046](https://github.com/windmill-labs/windmill/issues/10046)) ([0ea5705](https://github.com/windmill-labs/windmill/commit/0ea570570e65abf64b3b171e592a8dd3eea0b105))
|
||||
* **alerts:** include disk total and top consumers in low-disk alert ([#10144](https://github.com/windmill-labs/windmill/issues/10144)) ([4e0fd4d](https://github.com/windmill-labs/windmill/commit/4e0fd4db5589f91426ca025184da7b977d5a16d6))
|
||||
* automatic git-to-windmill sync (polling, webhooks, in-app PRs + checks) ([#9552](https://github.com/windmill-labs/windmill/issues/9552)) ([51d8db6](https://github.com/windmill-labs/windmill/commit/51d8db6602bc192879e50b54a5fe6b6b8beb63f4))
|
||||
* display openai reasoning summaries in ai chat ([#10147](https://github.com/windmill-labs/windmill/issues/10147)) ([4ee1d32](https://github.com/windmill-labs/windmill/commit/4ee1d32101eb527ef707c3c4af456892f0a059ab))
|
||||
* **forks:** add "Hide unchanged drafts" toggle to fork deploy-draft tab ([#10022](https://github.com/windmill-labs/windmill/issues/10022)) ([4fc3f30](https://github.com/windmill-labs/windmill/commit/4fc3f304c632eda15876c28cb6f473a799a41f48))
|
||||
* **frontend:** flatten workspace pickers, whole-tab picker trigger ([#10145](https://github.com/windmill-labs/windmill/issues/10145)) ([7fda6a0](https://github.com/windmill-labs/windmill/commit/7fda6a05345d14f62dc2124d682650d50eecbe98))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ai:** show the question in askUserQuestion tool-call labels ([#10153](https://github.com/windmill-labs/windmill/issues/10153)) ([fa03984](https://github.com/windmill-labs/windmill/commit/fa03984a14bb71f0ea2a27bfa23339a9d0d8e86b))
|
||||
* **alerts:** identify server replica in low-disk alert + per-host dedup tag ([#10143](https://github.com/windmill-labs/windmill/issues/10143)) ([3bd9f05](https://github.com/windmill-labs/windmill/commit/3bd9f0593866740935f79c8b2c1cb92829059105))
|
||||
* **frontend:** sanitize job result markup, gate it on unsandboxed public apps ([#10127](https://github.com/windmill-labs/windmill/issues/10127)) ([7b813d1](https://github.com/windmill-labs/windmill/commit/7b813d1f74baef200d9f1387e02925083b79b265))
|
||||
* **frontend:** show friendly draft path for draft-only items in pickers ([#10136](https://github.com/windmill-labs/windmill/issues/10136)) ([568dbbe](https://github.com/windmill-labs/windmill/commit/568dbbee852d59986581b0d16f5ac3e06d6bad6a))
|
||||
* **frontend:** surface real tool call errors in AI chat ([#10146](https://github.com/windmill-labs/windmill/issues/10146)) ([0694b84](https://github.com/windmill-labs/windmill/commit/0694b84da7bbb9404fd386e3ce109a574a0e9c93))
|
||||
* heartbeat job ping during s3object materialization in SQL executors ([#10152](https://github.com/windmill-labs/windmill/issues/10152)) ([7d5009e](https://github.com/windmill-labs/windmill/commit/7d5009e3928bb200150eb998ed0b3016c3395987))
|
||||
* **mcp:** push granular scope patterns into SQL so scoped scripts/flows aren't truncated ([#10140](https://github.com/windmill-labs/windmill/issues/10140)) ([91d6606](https://github.com/windmill-labs/windmill/commit/91d6606868d0e9c5f78ab48a592ef95ffaeeca61))
|
||||
* **migrations:** grant zombie_job_counter to windmill roles ([#10159](https://github.com/windmill-labs/windmill/issues/10159)) ([0e547ad](https://github.com/windmill-labs/windmill/commit/0e547adf23b615f0caccc88eb9101bb0604bb51b))
|
||||
* **raw-apps:** full code ui builder improvements ([c55ac53](https://github.com/windmill-labs/windmill/commit/c55ac5326fbb4532f22b7250a58b69396a4c868a))
|
||||
* **raw-apps:** prevent and surface the silent blank screen from an unmounted #root ([#10150](https://github.com/windmill-labs/windmill/issues/10150)) ([24750e6](https://github.com/windmill-labs/windmill/commit/24750e6ef1a1a975e0063df6809774f913b5d01b))
|
||||
* **self-host:** unbreak self-hosted Caddy after the caddy-l4 syntax change ([#10156](https://github.com/windmill-labs/windmill/issues/10156)) ([2f6c35b](https://github.com/windmill-labs/windmill/commit/2f6c35b15bf70f17eb8ff08281e0ecf8313e7b35))
|
||||
|
||||
## [1.760.1](https://github.com/windmill-labs/windmill/compare/v1.760.0...v1.760.1) (2026-07-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **apps:** honor presigned S3 signature on app display/preview routes ([#10141](https://github.com/windmill-labs/windmill/issues/10141)) ([8c725d9](https://github.com/windmill-labs/windmill/commit/8c725d9e44e38bf3b35f41b847cdf8745e07942e))
|
||||
|
||||
## [1.760.0](https://github.com/windmill-labs/windmill/compare/v1.759.0...v1.760.0) (2026-07-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **nsjail:** make python/ansible rlimit_as configurable per worker (GIT-921) ([#10138](https://github.com/windmill-labs/windmill/issues/10138)) ([1787201](https://github.com/windmill-labs/windmill/commit/17872018cc037e699b1f6e1589d0ad05e0883cca))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ai:** disable redirects on worker AI provider client (GHSA-5q4v) ([#10122](https://github.com/windmill-labs/windmill/issues/10122)) ([27ead8d](https://github.com/windmill-labs/windmill/commit/27ead8d0848cceacaf0c49fed0e8896472b851e3))
|
||||
* **ai:** stop sending the AI agent system prompt twice for OpenAI ([#10126](https://github.com/windmill-labs/windmill/issues/10126)) ([8bfe5c9](https://github.com/windmill-labs/windmill/commit/8bfe5c93404ba3f137394f16d0571a06d891dc3b))
|
||||
* **apps:** invalidate cached app policy on change or deletion (GHSA-r5v4-cxh9-7qhq) ([#10121](https://github.com/windmill-labs/windmill/issues/10121)) ([f7eb5c4](https://github.com/windmill-labs/windmill/commit/f7eb5c460d78792c24297e20cb062638522a4f68))
|
||||
* **bash:** normalize CRLF line endings before running scripts ([#10131](https://github.com/windmill-labs/windmill/issues/10131)) ([6407d9f](https://github.com/windmill-labs/windmill/commit/6407d9ff5ce51e71ff8b8fc503d89a2bdc2e1761))
|
||||
* **cli-image:** patch fixable CRITICAL CVEs in windmill-cli image (GIT-922) ([#10135](https://github.com/windmill-labs/windmill/issues/10135)) ([5626768](https://github.com/windmill-labs/windmill/commit/56267684718944ef4d4ecd3b610bad81132e1990))
|
||||
* **frontend:** graceful small-screen timeframe picker on the runs page ([#10073](https://github.com/windmill-labs/windmill/issues/10073)) ([af177ce](https://github.com/windmill-labs/windmill/commit/af177cefe07e6037e33cc90757088a98fb63a49f))
|
||||
* **frontend:** keep session-exit URL clean by syncing new_draft strip with the router ([#10101](https://github.com/windmill-labs/windmill/issues/10101)) ([9705d60](https://github.com/windmill-labs/windmill/commit/9705d602848966f850613233d6e23b73a753c259))
|
||||
* **frontend:** only carry custom-tag overrides on 'Run again' ([#10137](https://github.com/windmill-labs/windmill/issues/10137)) ([bd3adc9](https://github.com/windmill-labs/windmill/commit/bd3adc9781d8e77928c9feb03d0e05b63a1aaf7c))
|
||||
* **frontend:** treat a displaced draft save as superseded, not failed ([#10094](https://github.com/windmill-labs/windmill/issues/10094)) ([2fe999f](https://github.com/windmill-labs/windmill/commit/2fe999f66cd15acd81850f970ada31e9892abff2))
|
||||
* reject git URL fragment/query SSRF bypass (GHSA-p5cj-8cfh-mjv6) ([#10120](https://github.com/windmill-labs/windmill/issues/10120)) ([73c8d7f](https://github.com/windmill-labs/windmill/commit/73c8d7f08ad55cd2323d1bb9438f00a8ac30e046))
|
||||
* **security:** enforce variables:write scope on resource-delete var cascade (GHSA-xmr2-98m6-cjf7) ([#10123](https://github.com/windmill-labs/windmill/issues/10123)) ([188647a](https://github.com/windmill-labs/windmill/commit/188647a942a0cb496d63c71bf3e0f7a3136ec217))
|
||||
|
||||
## [1.759.0](https://github.com/windmill-labs/windmill/compare/v1.758.0...v1.759.0) (2026-07-15)
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
# site, silently disabling the HTTP proxy while the :25 layer4 listener stays up.
|
||||
bind {$ADDRESS:0.0.0.0 ::}
|
||||
|
||||
# Extra services: LSP, Multiplayer, Debugger (windmill_extra gateway)
|
||||
reverse_proxy /ws/* /ws_mp/* /ws_debug/* http://windmill_extra:3000
|
||||
# Extra services: LSP, Multiplayer, Debugger (windmill_extra gateway).
|
||||
# reverse_proxy only reads its first argument as a matcher, so listing
|
||||
# several paths inline turns the rest into upstream addresses. The paths
|
||||
# have to go through a named matcher.
|
||||
@extra path /ws/* /ws_mp/* /ws_debug/*
|
||||
reverse_proxy @extra http://windmill_extra:3000
|
||||
|
||||
# Search indexer, Enterprise Edition (windmill_indexer:8002)
|
||||
# reverse_proxy /api/srch/* http://windmill_indexer:8002
|
||||
|
||||
@@ -79,6 +79,8 @@ COPY /python-client/docs/ /frontend/static/pydocs/
|
||||
RUN npm run generate-backend-client
|
||||
ENV NODE_OPTIONS "--max-old-space-size=8192"
|
||||
ARG VITE_BASE_URL ""
|
||||
# Must be declared for the build-arg to reach the bundle. See frontend/svelte.config.js.
|
||||
ARG WM_BUILD_VERSION=""
|
||||
# Read more about macro in docker/dev.nu
|
||||
# -- MACRO-SPREAD-WASM-PARSER-DEV-ONLY -- #
|
||||
RUN npm run build
|
||||
|
||||
@@ -3,7 +3,7 @@ import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import type { AIProvider } from "$lib/gen/types.gen";
|
||||
import {
|
||||
globalTools,
|
||||
globalToolsFor,
|
||||
prepareGlobalSystemMessage,
|
||||
prepareGlobalUserMessage,
|
||||
} from "../../../../../frontend/src/lib/components/copilot/chat/global/core";
|
||||
@@ -43,6 +43,67 @@ const LIVE_EDITOR_ITEM_KINDS = {
|
||||
app: "raw_app",
|
||||
} as const;
|
||||
|
||||
// SessionArtifactsStore can't run here (bun has no IndexedDB, nor the compiled $state runes),
|
||||
// so mirror only its tool-facing shape; its own logic (scoping, race guard) is unit-tested.
|
||||
const EVAL_SESSION_ID = "eval-session";
|
||||
function createEvalArtifactHelpers() {
|
||||
const items = new Map<string, Record<string, unknown>>();
|
||||
let seq = 0;
|
||||
const store = {
|
||||
create: async (sessionId: string, input: Record<string, any>) => {
|
||||
const now = seq++;
|
||||
const artifact = {
|
||||
id: `eval-artifact-${now}`,
|
||||
sessionId,
|
||||
chatId: input.chatId,
|
||||
kind: input.kind ?? "md",
|
||||
name: input.name,
|
||||
content: input.content,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
items.set(artifact.id, artifact);
|
||||
return artifact;
|
||||
},
|
||||
get: async (id: string) => items.get(id),
|
||||
update: async (
|
||||
id: string,
|
||||
input: Record<string, any>,
|
||||
opts?: { sessionId?: string },
|
||||
) => {
|
||||
const existing = items.get(id);
|
||||
if (!existing) return undefined;
|
||||
if (
|
||||
opts?.sessionId !== undefined &&
|
||||
existing.sessionId !== opts.sessionId
|
||||
)
|
||||
return undefined;
|
||||
const updated = {
|
||||
...existing,
|
||||
name: input.name ?? existing.name,
|
||||
content: input.content ?? existing.content,
|
||||
updatedAt: seq++,
|
||||
};
|
||||
items.set(id, updated);
|
||||
return updated;
|
||||
},
|
||||
remove: async (id: string) => {
|
||||
items.delete(id);
|
||||
},
|
||||
listForSession: async (sessionId: string) =>
|
||||
[...items.values()].filter((a) => a.sessionId === sessionId),
|
||||
};
|
||||
return {
|
||||
helpers: {
|
||||
artifacts: store,
|
||||
sessionId: EVAL_SESSION_ID,
|
||||
getChatId: () => "eval-chat",
|
||||
openArtifact: () => {},
|
||||
},
|
||||
snapshot: () => [...items.values()],
|
||||
};
|
||||
}
|
||||
|
||||
export interface GlobalLiveEditorDraftFixture {
|
||||
type: keyof typeof LIVE_EDITOR_ITEM_KINDS;
|
||||
storagePath?: string;
|
||||
@@ -80,6 +141,8 @@ export interface GlobalEvalOptions {
|
||||
workspaceFixtures?: BenchmarkWorkspaceRunnables;
|
||||
liveEditorDrafts?: GlobalLiveEditorDraftFixture[];
|
||||
user?: GlobalUserFixture;
|
||||
// Emulate a session chat (preview tools + session prompt); default false = standalone baseline.
|
||||
sessionChat?: boolean;
|
||||
model?: string;
|
||||
maxIterations?: number;
|
||||
provider?: AIProvider;
|
||||
@@ -98,7 +161,10 @@ export async function runGlobalEval(
|
||||
(await mkdtemp(join(tmpdir(), "wmill-frontend-global-benchmark-")));
|
||||
|
||||
clearGlobalDrafts(workspaceRoot);
|
||||
registerBenchmarkWorkspaceRunnables(workspaceRoot, options.workspaceFixtures ?? {});
|
||||
registerBenchmarkWorkspaceRunnables(
|
||||
workspaceRoot,
|
||||
options.workspaceFixtures ?? {},
|
||||
);
|
||||
seedLiveEditorDrafts(workspaceRoot, options.liveEditorDrafts ?? []);
|
||||
|
||||
try {
|
||||
@@ -107,18 +173,25 @@ export async function runGlobalEval(
|
||||
process.env[DISABLE_ACTIVE_EDITOR_CONTEXT_ENV] !== "1";
|
||||
// Pass the seeded identity straight to the prompt builder rather than mutating
|
||||
// the process-global `userStore`, so concurrent cases never race on it.
|
||||
const evalArtifacts = createEvalArtifactHelpers();
|
||||
const rawResult = await runEval({
|
||||
userPrompt,
|
||||
systemMessage: prepareGlobalSystemMessage(undefined, { user: options.user }),
|
||||
systemMessage: prepareGlobalSystemMessage(undefined, {
|
||||
user: options.user,
|
||||
previewTools: options.sessionChat ?? false,
|
||||
}),
|
||||
userMessage: prepareGlobalUserMessage(
|
||||
userPrompt,
|
||||
[],
|
||||
injectActiveEditorContext ? { workspace: workspaceRoot } : {},
|
||||
),
|
||||
tools: getGlobalEvalTools(),
|
||||
helpers: {},
|
||||
tools: getGlobalEvalTools(options.sessionChat ?? false),
|
||||
helpers: evalArtifacts.helpers,
|
||||
apiKey,
|
||||
getOutput: () => collectGlobalDraftState(workspaceRoot),
|
||||
getOutput: async () => ({
|
||||
...(await collectGlobalDraftState(workspaceRoot)),
|
||||
artifacts: evalArtifacts.snapshot(),
|
||||
}),
|
||||
onAssistantMessageStart: options.runContext?.onAssistantMessageStart,
|
||||
onAssistantToken: options.runContext?.onAssistantChunk,
|
||||
onAssistantMessageEnd: options.runContext?.onAssistantMessageEnd,
|
||||
@@ -213,10 +286,15 @@ function clearLiveEditorDrafts(
|
||||
}
|
||||
}
|
||||
|
||||
function getGlobalEvalTools(): ProductionTool<{}>[] {
|
||||
// Gate session-preview tools on sessionChat, as production's globalToolsFor does.
|
||||
function getGlobalEvalTools(sessionChat: boolean): ProductionTool<{}>[] {
|
||||
const disableSearchApp = process.env[DISABLE_SEARCH_APP_ENV] === "1";
|
||||
return (globalTools as ProductionTool<{}>[])
|
||||
.filter((tool) => !(disableSearchApp && tool.def.function.name === "search_app"))
|
||||
return (
|
||||
globalToolsFor({ sessionPreview: sessionChat }) as ProductionTool<{}>[]
|
||||
)
|
||||
.filter(
|
||||
(tool) => !(disableSearchApp && tool.def.function.name === "search_app"),
|
||||
)
|
||||
.map((tool) => {
|
||||
if (!MUTATING_GLOBAL_TOOLS.has(tool.def.function.name)) {
|
||||
return tool;
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
import type {
|
||||
DataTableTables,
|
||||
DataTableTableSchema,
|
||||
EndpointTool,
|
||||
GetDraftForUserResponse,
|
||||
GetOwnDraftResponse,
|
||||
ListDraftsResponse,
|
||||
@@ -303,13 +304,20 @@ export function getBenchmarkJobLogs(workspace: string, jobId: string): string {
|
||||
*/
|
||||
const benchmarkDrafts = new Map<
|
||||
string,
|
||||
{ workspace: string; kind: UserDraftItemKind; path: string; value: unknown }
|
||||
{ workspace: string; kind: UserDraftItemKind; path: string; value: unknown; createdAt: string }
|
||||
>()
|
||||
|
||||
// Fixed timestamp so artifacts stay deterministic. No eval simulates a
|
||||
// concurrent writer, so every save is accepted and the conflict branch is
|
||||
// never taken — the syncer just records this as its `last_sync` baseline.
|
||||
const BENCHMARK_DRAFT_TIMESTAMP = '1970-01-01T00:00:00.000Z'
|
||||
// Counter-based timestamps: deterministic run-to-run (same event order → same
|
||||
// values) but MONOTONIC per update, because production bumps a draft row's
|
||||
// created_at on every upsert and the diff snapshot cache keys patch reuse on
|
||||
// it — a fixed timestamp would serve stale patches after an edit. No eval
|
||||
// simulates a concurrent writer, so every save is accepted and the conflict
|
||||
// branch is never taken.
|
||||
let benchmarkDraftClock = 0
|
||||
function nextBenchmarkDraftTimestamp(): string {
|
||||
benchmarkDraftClock += 1
|
||||
return new Date(benchmarkDraftClock * 1000).toISOString()
|
||||
}
|
||||
|
||||
function benchmarkDraftKey(workspace: string, kind: string, path: string): string {
|
||||
return `${workspace}::${kind}::${path}`
|
||||
@@ -340,7 +348,8 @@ export function seedBenchmarkDraft(
|
||||
workspace,
|
||||
kind,
|
||||
path,
|
||||
value
|
||||
value,
|
||||
createdAt: nextBenchmarkDraftTimestamp()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -353,6 +362,7 @@ export function updateBenchmarkDraft(input: {
|
||||
}): UpdateDraftResponse {
|
||||
const key = benchmarkDraftKey(input.workspace, input.kind, input.path)
|
||||
const value = input.requestBody?.value
|
||||
const createdAt = nextBenchmarkDraftTimestamp()
|
||||
if (value == null) {
|
||||
benchmarkDrafts.delete(key)
|
||||
} else {
|
||||
@@ -360,10 +370,11 @@ export function updateBenchmarkDraft(input: {
|
||||
workspace: input.workspace,
|
||||
kind: input.kind,
|
||||
path: input.path,
|
||||
value
|
||||
value,
|
||||
createdAt
|
||||
})
|
||||
}
|
||||
return { status: 'saved', current_timestamp: BENCHMARK_DRAFT_TIMESTAMP }
|
||||
return { status: 'saved', current_timestamp: createdAt }
|
||||
}
|
||||
|
||||
/** Mirror `DraftService.getDraftForUser`: 404-shaped throw when absent so the
|
||||
@@ -377,7 +388,7 @@ export function getBenchmarkDraftForUser(input: {
|
||||
if (!entry) {
|
||||
throw Object.assign(new Error(`no draft for "${input.path}"`), { status: 404 })
|
||||
}
|
||||
return { value: entry.value, created_at: BENCHMARK_DRAFT_TIMESTAMP }
|
||||
return { value: entry.value, created_at: entry.createdAt }
|
||||
}
|
||||
|
||||
/** Mirror `DraftService.getOwnDraft`: `null` (200) when absent — unlike
|
||||
@@ -391,7 +402,18 @@ export function getBenchmarkOwnDraft(input: {
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
return { value: entry.value, created_at: BENCHMARK_DRAFT_TIMESTAMP }
|
||||
return { value: entry.value, created_at: entry.createdAt }
|
||||
}
|
||||
|
||||
/** Whether a deployed benchmark item exists for a draft row's kind+path —
|
||||
* drives `draft_only`, which production computes against the deployed tables. */
|
||||
function benchmarkDeployedExists(workspace: string, kind: UserDraftItemKind, path: string): boolean {
|
||||
if (kind === 'script') return Boolean(getBenchmarkScriptByPath(workspace, path))
|
||||
if (kind === 'flow') return Boolean(getBenchmarkFlowByPath(workspace, path))
|
||||
if (kind === 'app' || kind === 'raw_app') return Boolean(getBenchmarkAppByPath(workspace, path))
|
||||
// Drawer kinds (variables/resources/schedules/triggers) have no deployed
|
||||
// benchmark stores today.
|
||||
return false
|
||||
}
|
||||
|
||||
/** Mirror `DraftService.listDrafts`: metadata rows (no value) for a workspace. */
|
||||
@@ -402,9 +424,9 @@ export function listBenchmarkDrafts(workspace: string): ListDraftsResponse {
|
||||
kind: entry.kind,
|
||||
path: entry.path,
|
||||
summary: (entry.value as { summary?: string } | null)?.summary,
|
||||
draft_only: true,
|
||||
draft_only: !benchmarkDeployedExists(workspace, entry.kind, entry.path),
|
||||
legacy_draft: false,
|
||||
created_at: BENCHMARK_DRAFT_TIMESTAMP
|
||||
created_at: entry.createdAt
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -694,3 +716,138 @@ function buildBenchmarkApp(app: BenchmarkWorkspaceApp): AppWithLastVersion {
|
||||
raw_app: true
|
||||
}
|
||||
}
|
||||
|
||||
// ============= API endpoint catalog (McpService.listMcpTools + raw fetch) =============
|
||||
// The global chat's API catalog tools list endpoints via McpService and execute
|
||||
// them with a plain relative fetch('/api/...'), which has no meaning in the
|
||||
// vitest environment. A representative slice of the real catalog is served here,
|
||||
// and `handleBenchmarkApiFetch` answers the executed calls.
|
||||
|
||||
const BENCHMARK_MCP_TOOLS: EndpointTool[] = [
|
||||
{
|
||||
name: 'listWorkers',
|
||||
description: 'List workers',
|
||||
instructions: 'List all workers with their last ping and job counts.',
|
||||
path: '/workers/list',
|
||||
method: 'GET',
|
||||
query_params_schema: {
|
||||
type: 'object',
|
||||
properties: { page: { type: 'integer' }, per_page: { type: 'integer' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'listQueue',
|
||||
description: 'List queued jobs',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/jobs/queue/list',
|
||||
method: 'GET',
|
||||
path_params_schema: {
|
||||
type: 'object',
|
||||
properties: { workspace: { type: 'string' } },
|
||||
required: ['workspace']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'runScriptByPath',
|
||||
description: 'Run the deployed version of a script by path',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/jobs/run/p/{path}',
|
||||
method: 'POST',
|
||||
path_params_schema: {
|
||||
type: 'object',
|
||||
properties: { workspace: { type: 'string' }, path: { type: 'string' } },
|
||||
required: ['workspace', 'path']
|
||||
},
|
||||
body_schema: { type: 'object', properties: {} }
|
||||
},
|
||||
{
|
||||
name: 'runFlowByPath',
|
||||
description: 'Run the deployed version of a flow by path',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/jobs/run/f/{path}',
|
||||
method: 'POST',
|
||||
path_params_schema: {
|
||||
type: 'object',
|
||||
properties: { workspace: { type: 'string' }, path: { type: 'string' } },
|
||||
required: ['workspace', 'path']
|
||||
},
|
||||
body_schema: { type: 'object', properties: {} }
|
||||
},
|
||||
// Draft-covered endpoints, present so steering cases exercise the guard the
|
||||
// way production does (hidden from search, refused at call time).
|
||||
{
|
||||
name: 'getScriptByPath',
|
||||
description: 'Get a script by path',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/scripts/get/p/{path}',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: 'createFlow',
|
||||
description: 'Create a flow',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/flows/create',
|
||||
method: 'POST'
|
||||
},
|
||||
{
|
||||
name: 'deleteSchedule',
|
||||
description: 'Delete a schedule',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/schedules/delete/{path}',
|
||||
method: 'DELETE'
|
||||
},
|
||||
{
|
||||
name: 'getVariable',
|
||||
description: 'Get a variable',
|
||||
instructions: '',
|
||||
path: '/w/{workspace}/variables/get/{path}',
|
||||
method: 'GET'
|
||||
}
|
||||
]
|
||||
|
||||
export function listBenchmarkMcpTools(): EndpointTool[] {
|
||||
return BENCHMARK_MCP_TOOLS
|
||||
}
|
||||
|
||||
const BENCHMARK_WORKERS = [
|
||||
{
|
||||
worker: 'wk-benchmark-1',
|
||||
worker_instance: 'benchmark-host',
|
||||
last_ping: 2,
|
||||
started_at: BENCHMARK_TIMESTAMP,
|
||||
jobs_executed: 42,
|
||||
custom_tags: null,
|
||||
worker_group: 'default',
|
||||
wm_version: 'benchmark'
|
||||
},
|
||||
{
|
||||
worker: 'wk-benchmark-2',
|
||||
worker_instance: 'benchmark-host',
|
||||
last_ping: 5,
|
||||
started_at: BENCHMARK_TIMESTAMP,
|
||||
jobs_executed: 17,
|
||||
custom_tags: null,
|
||||
worker_group: 'default',
|
||||
wm_version: 'benchmark'
|
||||
}
|
||||
]
|
||||
|
||||
/** True when `handleBenchmarkApiFetch` has an answer for this `/api/...` url.
|
||||
* Any other relative fetch must keep its normal (non-benchmark) behavior —
|
||||
* intercepting it with a synthetic 404 sends the model into retry loops. */
|
||||
export function hasBenchmarkApiHandler(url: string): boolean {
|
||||
const path = url.split('?')[0]
|
||||
return path === '/api/workers/list' || /^\/api\/w\/[^/]+\/jobs\/queue\/list$/.test(path)
|
||||
}
|
||||
|
||||
/** Answer a relative `/api/...` fetch issued by the API catalog executor. */
|
||||
export function handleBenchmarkApiFetch(url: string): Response {
|
||||
const path = url.split('?')[0]
|
||||
if (path === '/api/workers/list') {
|
||||
return Response.json(BENCHMARK_WORKERS)
|
||||
}
|
||||
if (/^\/api\/w\/[^/]+\/jobs\/queue\/list$/.test(path)) {
|
||||
return Response.json([])
|
||||
}
|
||||
return Response.json({ error: `no benchmark handler for ${path}` }, { status: 404 })
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@ import { expect, it, vi } from 'vitest'
|
||||
import { mkdir, writeFile } from 'fs/promises'
|
||||
// @ts-ignore - Node.js path
|
||||
import { dirname, resolve } from 'path'
|
||||
import { handleBenchmarkApiFetch, hasBenchmarkApiHandler } from './mockBackend'
|
||||
|
||||
// The API catalog executor issues relative fetch('/api/...') calls, which have
|
||||
// no meaning in the vitest environment — serve the ones the benchmark handles.
|
||||
// Every other relative fetch keeps its normal behavior (it fails the same way
|
||||
// it does without this stub) so unrelated tools see an unchanged environment.
|
||||
const ORIGINAL_FETCH = globalThis.fetch
|
||||
globalThis.fetch = (async (input: unknown, init?: RequestInit) => {
|
||||
const url = typeof input === 'string' ? input : ((input as Request | URL | null)?.url ?? '')
|
||||
if (typeof url === 'string' && hasBenchmarkApiHandler(url)) {
|
||||
return handleBenchmarkApiFetch(url)
|
||||
}
|
||||
return ORIGINAL_FETCH(input as Parameters<typeof fetch>[0], init)
|
||||
}) as typeof fetch
|
||||
|
||||
vi.mock('monaco-editor', () => ({
|
||||
editor: {},
|
||||
@@ -57,7 +71,8 @@ vi.mock('$lib/gen', async () => {
|
||||
runBenchmarkDatatableSql,
|
||||
runBenchmarkFlowByPath,
|
||||
runBenchmarkScriptPreview,
|
||||
updateBenchmarkDraft
|
||||
updateBenchmarkDraft,
|
||||
listBenchmarkMcpTools
|
||||
} = await import('./mockBackend')
|
||||
|
||||
function wrapService<T extends object>(target: T, overrides: Record<string, unknown>): T {
|
||||
@@ -111,13 +126,32 @@ vi.mock('$lib/gen', async () => {
|
||||
hasBenchmarkWorkspace(data.workspace)
|
||||
? Boolean(getBenchmarkScriptByPath(data.workspace, data.path))
|
||||
: actual.ScriptService.existsScriptByPath(data),
|
||||
getScriptByPath: async (data: { workspace: string; path: string }) => {
|
||||
getScriptByPath: async (data: { workspace: string; path: string; getDraft?: boolean }) => {
|
||||
if (hasBenchmarkWorkspace(data.workspace)) {
|
||||
const script = getBenchmarkScriptByPath(data.workspace, data.path)
|
||||
// `getDraft` mirrors production's overlay: the row plus the caller's
|
||||
// draft and a `no_deployed` marker (draft-only item). The diff tool
|
||||
// reads through this shape — without it every draft looks absent.
|
||||
const draft = data.getDraft
|
||||
? getBenchmarkOwnDraft({ workspace: data.workspace, kind: 'script', path: data.path })
|
||||
: null
|
||||
if (!script) {
|
||||
throw new Error(`Script "${data.path}" not found in benchmark workspace`)
|
||||
if (data.getDraft && draft) {
|
||||
return {
|
||||
...(draft.value as Record<string, unknown>),
|
||||
path: data.path,
|
||||
draft: draft.value,
|
||||
no_deployed: true
|
||||
}
|
||||
}
|
||||
throw Object.assign(
|
||||
new Error(`Script "${data.path}" not found in benchmark workspace`),
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
return script
|
||||
return data.getDraft
|
||||
? { ...script, draft: draft?.value ?? undefined, no_deployed: false }
|
||||
: script
|
||||
}
|
||||
return actual.ScriptService.getScriptByPath(data)
|
||||
},
|
||||
@@ -151,13 +185,30 @@ vi.mock('$lib/gen', async () => {
|
||||
hasBenchmarkWorkspace(data.workspace)
|
||||
? Boolean(getBenchmarkFlowByPath(data.workspace, data.path))
|
||||
: actual.FlowService.existsFlowByPath(data),
|
||||
getFlowByPath: async (data: { workspace: string; path: string }) => {
|
||||
getFlowByPath: async (data: { workspace: string; path: string; getDraft?: boolean }) => {
|
||||
if (hasBenchmarkWorkspace(data.workspace)) {
|
||||
const flow = getBenchmarkFlowByPath(data.workspace, data.path)
|
||||
// Mirror production's `getDraft` overlay (see getScriptByPath above).
|
||||
const draft = data.getDraft
|
||||
? getBenchmarkOwnDraft({ workspace: data.workspace, kind: 'flow', path: data.path })
|
||||
: null
|
||||
if (!flow) {
|
||||
throw new Error(`Flow "${data.path}" not found in benchmark workspace`)
|
||||
if (data.getDraft && draft) {
|
||||
return {
|
||||
...(draft.value as Record<string, unknown>),
|
||||
path: data.path,
|
||||
draft: draft.value,
|
||||
no_deployed: true
|
||||
}
|
||||
}
|
||||
throw Object.assign(
|
||||
new Error(`Flow "${data.path}" not found in benchmark workspace`),
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
return flow
|
||||
return data.getDraft
|
||||
? { ...flow, draft: draft?.value ?? undefined, no_deployed: false }
|
||||
: flow
|
||||
}
|
||||
return actual.FlowService.getFlowByPath(data)
|
||||
},
|
||||
@@ -299,6 +350,12 @@ vi.mock('$lib/gen', async () => {
|
||||
queryResourceTypes: async (data: { workspace: string }) =>
|
||||
hasBenchmarkWorkspace(data.workspace) ? [] : actual.ResourceService.queryResourceTypes(data)
|
||||
}),
|
||||
McpService: wrapService(actual.McpService, {
|
||||
listMcpTools: async (data: { workspace: string }) =>
|
||||
hasBenchmarkWorkspace(data.workspace)
|
||||
? listBenchmarkMcpTools()
|
||||
: actual.McpService.listMcpTools(data)
|
||||
}),
|
||||
VariableService: wrapService(actual.VariableService, {
|
||||
existsVariable: async (data: { workspace: string; path: string }) =>
|
||||
hasBenchmarkWorkspace(data.workspace) ? false : actual.VariableService.existsVariable(data),
|
||||
@@ -320,13 +377,37 @@ vi.mock('$lib/gen', async () => {
|
||||
hasBenchmarkWorkspace(data.workspace)
|
||||
? (listBenchmarkApps(data.workspace) ?? [])
|
||||
: actual.AppService.listApps(data),
|
||||
getAppByPath: async (data: { workspace: string; path: string }) => {
|
||||
getAppByPath: async (data: {
|
||||
workspace: string
|
||||
path: string
|
||||
getDraft?: boolean
|
||||
rawApp?: boolean
|
||||
}) => {
|
||||
if (hasBenchmarkWorkspace(data.workspace)) {
|
||||
const app = getBenchmarkAppByPath(data.workspace, data.path)
|
||||
// Mirror production's `getDraft` overlay (see getScriptByPath above).
|
||||
// Benchmark app drafts live under the raw_app kind.
|
||||
const draft = data.getDraft
|
||||
? getBenchmarkOwnDraft({ workspace: data.workspace, kind: 'raw_app', path: data.path })
|
||||
: null
|
||||
if (!app) {
|
||||
throw new Error(`App "${data.path}" not found in benchmark workspace`)
|
||||
if (data.getDraft && draft) {
|
||||
return {
|
||||
...(draft.value as Record<string, unknown>),
|
||||
path: data.path,
|
||||
raw_app: true,
|
||||
draft: draft.value,
|
||||
no_deployed: true
|
||||
}
|
||||
}
|
||||
throw Object.assign(
|
||||
new Error(`App "${data.path}" not found in benchmark workspace`),
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
return app
|
||||
return data.getDraft
|
||||
? { ...app, draft: draft?.value ?? undefined, no_deployed: false }
|
||||
: app
|
||||
}
|
||||
return actual.AppService.getAppByPath(data)
|
||||
}
|
||||
|
||||
@@ -480,3 +480,85 @@
|
||||
judgeChecklist:
|
||||
- "the flow includes a final top-level step named `webhook_response`"
|
||||
- "`webhook_response` returns `ok: true` and the order summary"
|
||||
|
||||
- id: flow-test17-implicit-schedule-intent
|
||||
prompt: |-
|
||||
I want this order processing flow to run on its own every morning at 07:30 UTC.
|
||||
Set that up for me. Do not ask me for the flow path.
|
||||
initial: ai_evals/fixtures/frontend/flow/initial/scheduled_order_flow.json
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- create_schedule
|
||||
toolCallArgs:
|
||||
- tool: create_schedule
|
||||
field: path
|
||||
stringStartsWithAnyOf:
|
||||
- f/
|
||||
- u/
|
||||
stringMustNotStartWithAnyOf:
|
||||
- schedules/
|
||||
- tool: create_schedule
|
||||
field: schedule
|
||||
stringIncludesAnyOf:
|
||||
- 30 7
|
||||
- tool: create_schedule
|
||||
field: timezone
|
||||
stringIncludesAnyOf:
|
||||
- UTC
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- "a schedule is created for the flow that runs daily at 07:30 UTC"
|
||||
|
||||
- id: flow-test18-implicit-http-trigger-intent
|
||||
prompt: |-
|
||||
I need to be able to kick off this order processing flow by sending it an HTTP POST
|
||||
from an external system, with no authentication. Use route path `ai-evals/order-processing-implicit`.
|
||||
Do not ask me for the flow path.
|
||||
initial: ai_evals/fixtures/frontend/flow/initial/scheduled_order_flow.json
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- create_trigger
|
||||
toolCallArgs:
|
||||
- tool: create_trigger
|
||||
field: path
|
||||
stringStartsWithAnyOf:
|
||||
- f/
|
||||
- u/
|
||||
stringMustNotStartWithAnyOf:
|
||||
- schedules/
|
||||
- tool: create_trigger
|
||||
field: kind
|
||||
stringStartsWithAnyOf:
|
||||
- http
|
||||
- tool: create_trigger
|
||||
field: config.http_method
|
||||
stringIncludesAnyOf:
|
||||
- post
|
||||
- tool: create_trigger
|
||||
field: config.authentication_method
|
||||
stringIncludesAnyOf:
|
||||
- none
|
||||
- tool: create_trigger
|
||||
field: config.route_path
|
||||
stringIncludesAnyOf:
|
||||
- ai-evals/order-processing-implicit
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- "an HTTP trigger is created for the flow that accepts unauthenticated POST requests"
|
||||
|
||||
- id: flow-test19-implicit-email-trigger-intent
|
||||
prompt: |-
|
||||
Make this order processing flow run automatically whenever an email is received.
|
||||
Do not ask me for the flow path.
|
||||
initial: ai_evals/fixtures/frontend/flow/initial/scheduled_order_flow.json
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- create_trigger
|
||||
toolCallArgs:
|
||||
- tool: create_trigger
|
||||
field: kind
|
||||
stringStartsWithAnyOf:
|
||||
- email
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- "an email trigger (kind email) is created, or the user is told how to enable email triggering on the instance"
|
||||
|
||||
+371
-3
@@ -1050,11 +1050,44 @@
|
||||
- opens the Kafka triggers page
|
||||
- does not write, deploy, or delete anything
|
||||
|
||||
- id: global-openpage7-compare-review
|
||||
prompt: |-
|
||||
Create a TypeScript script draft at f/evals/global/compare_review_demo that returns the string "ok" (no need to test it), then open the review page so I can look over the pending change and deploy it myself.
|
||||
runtime:
|
||||
maxTurns: 8
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
- open_page
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
toolCallArgs:
|
||||
- tool: open_page
|
||||
field: page
|
||||
stringIncludesAnyOf:
|
||||
- compare
|
||||
# The eval chat is untracked (no modified-items mask), so the model must scope
|
||||
# the review by passing the item it changed explicitly — an omitted mask would
|
||||
# preselect every pending change in the workspace.
|
||||
- tool: open_page
|
||||
field: items
|
||||
stringIncludesAnyOf:
|
||||
- f/evals/global/compare_review_demo
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- creates the script draft, then opens the Compare & Deploy review page instead of deploying itself
|
||||
- preselects only the created script on the review page
|
||||
- does not deploy or delete anything
|
||||
|
||||
- id: global-closepage1-close-runs-tab
|
||||
prompt: |-
|
||||
You just opened the runs page for me in the side panel. Close that tab, I'm done looking at it.
|
||||
runtime:
|
||||
maxTurns: 6
|
||||
sessionChat: true
|
||||
validate:
|
||||
draftCountExactly: 0
|
||||
toolExpect:
|
||||
@@ -1451,9 +1484,15 @@
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
judgeChecklist:
|
||||
# A pipeline node is DECLARATIVE: triggers are declared by `-- on <ref>`
|
||||
# annotations (the trigger row is created separately) and a `-- materialize`
|
||||
# output is a MANAGED write where the body is a bare SELECT that the runtime
|
||||
# wraps in the create/replace. Do not expect a separate trigger config or a
|
||||
# hand-written CREATE TABLE / INSERT — those would be wrong for a materialize node.
|
||||
- builds a data pipeline node as a script (not a flow)
|
||||
- marks the script as a pipeline member with the pipeline annotation in the script's comment syntax (`-- pipeline` for a DuckDB/SQL node, not `// pipeline`)
|
||||
- declares a schedule trigger and writes its output to a managed DuckLake table
|
||||
- declares the schedule trigger with the `-- on schedule` annotation comment (this annotation is the correct and complete way a pipeline node binds a schedule; no separate trigger configuration is expected)
|
||||
- declares the managed DuckLake output with `-- materialize ducklake://<table>` and writes the body as a bare SELECT (materialize is a managed write, so the node correctly does NOT hand-write its own CREATE TABLE / INSERT)
|
||||
- leaves the result as an AI draft and does not deploy or save it
|
||||
|
||||
- id: global-test-pipeline-two-node-chain
|
||||
@@ -1467,6 +1506,12 @@
|
||||
maxTurns: 14
|
||||
validate:
|
||||
draftCountAtLeast: 2
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
pathStartsWith: f/evals/global/
|
||||
valueIncludes:
|
||||
- pipeline
|
||||
- ducklake
|
||||
forbiddenDrafts:
|
||||
- type: flow
|
||||
pathStartsWith: f/evals/global/
|
||||
@@ -1478,12 +1523,65 @@
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
judgeChecklist:
|
||||
# Pipeline nodes are declarative: `-- on <ref>` binds inputs/triggers and
|
||||
# `-- materialize ducklake://<table>` is a managed write whose body is a bare
|
||||
# SELECT. Do not expect hand-written CREATE TABLE / INSERT on a materialize node.
|
||||
- creates two data pipeline nodes as scripts (not a flow) in f/evals/global
|
||||
- both scripts carry the pipeline annotation in their comment syntax (`-- pipeline` for DuckDB/SQL nodes, not `// pipeline`)
|
||||
- the first ingests orders into a DuckLake table
|
||||
- the second reads that same table and writes a daily rollup, wired to the first step's output asset
|
||||
- the first ingests orders into a DuckLake table (a `-- materialize ducklake://<table>` output with a bare SELECT body is correct; no hand-written CREATE TABLE / INSERT is expected)
|
||||
- the second reads that same table via `-- on ducklake://<that-table>` and materializes a daily rollup table, wiring it to the first step's output asset
|
||||
- leaves both as AI drafts without deploying
|
||||
|
||||
- id: global-test-pipeline-complex-incremental
|
||||
prompt: |-
|
||||
Build a data pipeline in the `f/evals/global` folder for our web shop's
|
||||
orders. It has three steps:
|
||||
1. On a schedule, ingest the raw order CSVs under `s3://raw/orders/` into a
|
||||
managed DuckLake table.
|
||||
2. An incremental daily rollup: read that raw orders table and, on each run,
|
||||
append just the current day's order count and total revenue into a second
|
||||
DuckLake table. It should process one day at a time, not rebuild the whole
|
||||
table every run.
|
||||
3. A final step that reads the daily rollup table and exports the latest data
|
||||
as a Parquet file to `s3://reports/` for the BI team.
|
||||
Wire each step to the previous step's output so they form one pipeline. Keep
|
||||
everything as AI drafts — don't deploy or save.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 18
|
||||
validate:
|
||||
draftCountAtLeast: 3
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
pathStartsWith: f/evals/global/
|
||||
valueIncludes:
|
||||
- pipeline
|
||||
- ducklake
|
||||
forbiddenDrafts:
|
||||
- type: flow
|
||||
pathStartsWith: f/evals/global/
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
forbiddenToolsUsed:
|
||||
- write_flow
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
judgeChecklist:
|
||||
# Pipeline nodes are declarative: `-- on <ref>` binds inputs/triggers, and a
|
||||
# DuckLake `-- materialize` output is a managed write whose body is a bare SELECT
|
||||
# (the runtime performs the create/replace/append/merge). Do not expect a
|
||||
# separate trigger config or hand-written CREATE TABLE / INSERT on a
|
||||
# materialize node. S3/Parquet output is NOT materialize: the body writes it.
|
||||
- builds the pipeline as three independent scripts (not a flow) in f/evals/global
|
||||
- every node carries the pipeline annotation in its own comment syntax (`-- pipeline` for DuckDB/SQL nodes, not `// pipeline`)
|
||||
- step 1 binds a schedule with `-- on schedule` and declares a managed DuckLake output with `-- materialize ducklake://<table>` and a bare SELECT body (no separate trigger config or hand-written CREATE TABLE is expected)
|
||||
- "step 2 is incremental: each run adds only that day's rows to a second DuckLake table rather than rebuilding the whole table every run (e.g. an `append` or `key=<col>` merge materialize mode, not a full replace). Selecting the day via the `-- partitioned daily` + `{partition}` / `wm_partition(...)` idiom is the idiomatic form, but an equivalent current-day filter also satisfies this; a full-refresh/replace of the whole table does not"
|
||||
- step 2 reads the same DuckLake table step 1 writes (via `-- on ducklake://<that-table>`), wiring it to step 1's output asset
|
||||
- step 3 reads the daily rollup table and exports it as a Parquet file to S3
|
||||
- does not misuse `-- materialize` for the S3 Parquet export (materialize is DuckLake-only; the S3 output is written by the script body, e.g. a DuckDB COPY or an SDK write)
|
||||
- leaves all three nodes as AI drafts without deploying or saving
|
||||
|
||||
- id: global-path5-create-folder-then-draft
|
||||
prompt: |-
|
||||
Create a new shared folder called "analytics" for our data work, then draft a
|
||||
@@ -1509,3 +1607,273 @@
|
||||
- creates a new shared folder named "analytics" via create_folder
|
||||
- drafts a script placed in that folder (f/analytics/...) returning an ISO timestamp
|
||||
- leaves the script as a draft only
|
||||
|
||||
- id: global-artifact-plan-create
|
||||
prompt: |-
|
||||
I'm about to build a customer onboarding flow, but first I want a short written plan I can review and iterate on before any code.
|
||||
Draft a markdown plan with a title, a one-sentence summary, and three or four bullet steps.
|
||||
Keep it as something I can reopen and revise later — don't build the flow itself yet.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_empty.json
|
||||
runtime:
|
||||
maxTurns: 6
|
||||
sessionChat: true
|
||||
validate:
|
||||
draftCountExactly: 0
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- create_artifact
|
||||
forbiddenToolsUsed:
|
||||
- write_flow
|
||||
- write_script
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- saves the plan as a markdown artifact via create_artifact rather than only replying inline
|
||||
- the artifact content has a title, a one-line summary, and three or four bullet steps for onboarding
|
||||
- does not create a flow or script draft yet
|
||||
|
||||
- id: global-npm1-script-search-package
|
||||
prompt: |-
|
||||
Find a good npm package for parsing RSS/Atom feeds and use it to create a draft Bun script
|
||||
at `f/evals/global/fetch_feed`. The script should take a string `url` input and return the
|
||||
feed title along with the titles of the 5 most recent items. Tell me which package you
|
||||
picked and link its documentation. Leave it as an AI draft only; do not deploy.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 12
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/fetch_feed
|
||||
language: bun
|
||||
valueIncludes:
|
||||
- url
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- search_npm_packages
|
||||
- write_script
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
judgeChecklist:
|
||||
- searches npm for a feed-parsing package before writing the code
|
||||
- the draft Bun script imports the chosen npm package rather than hand-rolling an XML/RSS parser
|
||||
- the script takes a url input and returns the feed title plus the recent item titles
|
||||
- the reply names the chosen package and links its documentation
|
||||
- the result stays as an AI draft and is not deployed
|
||||
|
||||
- id: global-dbschema1-postgres-resource-tables
|
||||
prompt: |-
|
||||
I have a postgres resource at f/data/reports_pg in this workspace.
|
||||
What tables does that database have?
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_empty.json
|
||||
runtime:
|
||||
maxTurns: 12
|
||||
validate:
|
||||
draftCountExactly: 0
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- get_db_schema
|
||||
forbiddenToolsUsed:
|
||||
- write_script
|
||||
- write_resource
|
||||
- test_run_script
|
||||
- deploy_workspace_item
|
||||
toolCallArgs:
|
||||
- tool: get_db_schema
|
||||
field: resourcePath
|
||||
stringIncludesAnyOf:
|
||||
- f/data/reports_pg
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- fetches the schema through get_db_schema with the resource path f/data/reports_pg
|
||||
- when the lookup fails, tells the user instead of inventing table names
|
||||
- does not write scripts or resources to answer a read-only question
|
||||
|
||||
# --- API catalog (search_api_endpoints / call_api_get / call_api_endpoint) ---
|
||||
# The harness serves the catalog and the executed calls itself (mock
|
||||
# listMcpTools + benchmark fetch handlers in adapters/frontend), so these cases
|
||||
# do not require an mcp-enabled eval backend.
|
||||
|
||||
- id: global-test30-api-catalog-workers
|
||||
prompt: |-
|
||||
Are any workers online right now? Give me a quick summary of the workers and when they last pinged.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_empty.json
|
||||
runtime:
|
||||
maxTurns: 8
|
||||
validate:
|
||||
draftCountExactly: 0
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- search_api_endpoints
|
||||
- call_api_get
|
||||
forbiddenToolsUsed:
|
||||
- call_api_endpoint
|
||||
- write_script
|
||||
- deploy_workspace_item
|
||||
toolCallArgs:
|
||||
- tool: call_api_get
|
||||
field: name
|
||||
stringIncludesAnyOf:
|
||||
- listWorkers
|
||||
# Read-only workspace inspection produces no draft; validate via tool use.
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- discovers the workers endpoint through the API catalog instead of guessing or fabricating
|
||||
- reports worker status from the returned data
|
||||
|
||||
- id: global-test31-draft-test-run-not-deployed
|
||||
prompt: |-
|
||||
Update `f/evals/global/format_greeting` so the provided name is uppercased in the greeting, then run it with name "ada" to check it works.
|
||||
Keep it as an AI draft only; do not deploy or save it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/format_greeting_script.json
|
||||
runtime:
|
||||
maxTurns: 10
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/format_greeting
|
||||
language: bun
|
||||
valueIncludes:
|
||||
- toUpperCase
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- test_run_script
|
||||
forbiddenToolsUsed:
|
||||
- call_api_endpoint
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
# The judge only sees the drafts artifact and cannot observe runs, so it always
|
||||
# docks the prompt's "run it" requirement — validate deterministically instead:
|
||||
# draft content via valueIncludes, the test run via toolExpect (test_run_script
|
||||
# required, call_api_endpoint forbidden).
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- creates an AI draft of f/evals/global/format_greeting with the name uppercased in the greeting
|
||||
- does not deploy or save the draft
|
||||
|
||||
- id: global-test32-delete-via-dedicated-tool
|
||||
prompt: |-
|
||||
Delete the deployed script `f/evals/global/send_report_digest` from the workspace — we don't use it anymore.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/report_digest_script.json
|
||||
runtime:
|
||||
maxTurns: 6
|
||||
validate:
|
||||
draftCountExactly: 0
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- delete_workspace_item
|
||||
forbiddenToolsUsed:
|
||||
- call_api_endpoint
|
||||
- search_api_endpoints
|
||||
- write_script
|
||||
# Deletion produces no draft; validate via tool use.
|
||||
skipJudge: true
|
||||
judgeChecklist:
|
||||
- deletes the deployed script via delete_workspace_item rather than a raw API endpoint
|
||||
|
||||
- id: global-draft-diff-report
|
||||
prompt: |-
|
||||
Update the existing workspace script at `f/evals/global/format_greeting` so the returned message ends with an exclamation mark, keeping everything else the same.
|
||||
Then show me exactly what your draft changes compared to the deployed version.
|
||||
Leave the result as an AI draft only; do not deploy or save it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/format_greeting_script.json
|
||||
runtime:
|
||||
maxTurns: 8
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/format_greeting
|
||||
language: bun
|
||||
valueIncludes:
|
||||
- "!"
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- diff
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
toolCallArgs:
|
||||
- tool: diff
|
||||
field: path
|
||||
stringIncludesAnyOf:
|
||||
- f/evals/global/format_greeting
|
||||
judgeChecklist:
|
||||
- creates an AI draft for the existing f/evals/global/format_greeting script with the exclamation-mark change
|
||||
- the draft changes only the returned message's punctuation — summary, language, path, and the rest of the code are untouched
|
||||
- does not deploy or save the draft to the workspace
|
||||
|
||||
- id: global-resource-manual-credentials
|
||||
prompt: |-
|
||||
Set up a resource for our production Postgres database at `f/evals/global/prod_db` (host db.internal.example.com, port 5432, database `orders`, user `app`).
|
||||
I don't want to paste the password into this chat — prepare everything so I can enter it myself.
|
||||
Leave it as an AI draft only; do not deploy or save it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 12
|
||||
validate:
|
||||
draftCountAtLeast: 1
|
||||
requiredDrafts:
|
||||
- type: resource
|
||||
path: f/evals/global/prod_db
|
||||
valueIncludes:
|
||||
- db.internal.example.com
|
||||
- orders
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_resource
|
||||
- open_page
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
toolCallArgs:
|
||||
# The model may land the user in the resource's drawer or in the drawer of
|
||||
# the secret variable it created for the password — both are correct.
|
||||
- tool: open_page
|
||||
field: page
|
||||
stringIncludesAnyOf:
|
||||
- resources
|
||||
- variables
|
||||
- tool: open_page
|
||||
field: open
|
||||
stringIncludesAnyOf:
|
||||
- prod_db
|
||||
- password
|
||||
judgeChecklist:
|
||||
- creates a postgres resource draft at f/evals/global/prod_db with the provided host, port, database, and user
|
||||
- the password is left for the user to provide (empty, a placeholder, or a secret variable reference) — no invented password value presented as real
|
||||
- does not deploy or save anything to the workspace
|
||||
|
||||
- id: global-test29-email-trigger-draft
|
||||
prompt: |-
|
||||
Set up a draft auto-reply job.
|
||||
Create a Bun script at `f/evals/global/email_pong` that returns the string "pong".
|
||||
Then set it up so it runs whenever an email is received at the inbox `pong`.
|
||||
Leave everything as AI drafts only; do not deploy or save anything to the workspace.
|
||||
runtime:
|
||||
maxTurns: 10
|
||||
validate:
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/email_pong
|
||||
language: bun
|
||||
valueIncludes:
|
||||
- pong
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
- write_trigger
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
- delete_workspace_item
|
||||
toolCallArgs:
|
||||
# "Runs when an email is received" must resolve to the native email trigger kind,
|
||||
# never a faked HTTP webhook. Assert the recorded tool-call kind (not the draft):
|
||||
# it holds even on a CE backend where email trigger routes (smtp+private) 404.
|
||||
- tool: write_trigger
|
||||
field: kind
|
||||
stringIncludesAnyOf:
|
||||
- email
|
||||
skipJudge: true
|
||||
|
||||
@@ -31,6 +31,8 @@ export interface EvalCaseRuntimeSpec {
|
||||
maxTurns?: number;
|
||||
backendPreview?: EvalCaseRuntimeBackendPreview;
|
||||
appContext?: EvalCaseRuntimeAppContextSpec;
|
||||
// Global mode: run as a session chat (preview tools + session prompt) vs the standalone chat.
|
||||
sessionChat?: boolean;
|
||||
}
|
||||
|
||||
export interface FlowValidationSpec {
|
||||
|
||||
@@ -174,6 +174,40 @@ describe("validateToolExpectations", () => {
|
||||
expect(checks.every((check) => check.passed)).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts a stringIncludesAnyOf substring inside an array-valued field", () => {
|
||||
const checks = validateToolExpectations({
|
||||
run: {
|
||||
success: true,
|
||||
actual: {},
|
||||
assistantMessageCount: 1,
|
||||
toolCallCount: 1,
|
||||
toolsUsed: ["open_page"],
|
||||
toolCallDetails: [
|
||||
{
|
||||
name: "open_page",
|
||||
arguments: {
|
||||
page: "compare",
|
||||
items: ["script:f/evals/global/compare_review_demo"],
|
||||
},
|
||||
},
|
||||
],
|
||||
skillsInvoked: [],
|
||||
},
|
||||
toolExpect: {
|
||||
requiredToolsUsed: ["open_page"],
|
||||
toolCallArgs: [
|
||||
{
|
||||
tool: "open_page",
|
||||
field: "items",
|
||||
stringIncludesAnyOf: ["f/evals/global/compare_review_demo"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(checks.every((check) => check.passed)).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts stringIncludesAnyOf when only one of several calls matches", () => {
|
||||
// Existential: a mutation mixed with verification SELECTs still passes.
|
||||
const checks = validateToolExpectations({
|
||||
|
||||
@@ -239,9 +239,15 @@ export function validateToolExpectations(input: {
|
||||
// model mixes the requested statement (e.g. an UPDATE) with verification
|
||||
// SELECTs that would otherwise fail an "all calls" check.
|
||||
const needles = rule.stringIncludesAnyOf.map((needle) => needle.toLowerCase());
|
||||
const hasMatch = values.some(
|
||||
(value) =>
|
||||
typeof value === "string" && needles.some((needle) => value.toLowerCase().includes(needle))
|
||||
// Array-valued fields (e.g. open_page.items) match on any element.
|
||||
const haystacks = (value: unknown): string[] =>
|
||||
typeof value === "string"
|
||||
? [value]
|
||||
: Array.isArray(value)
|
||||
? value.filter((v): v is string => typeof v === "string")
|
||||
: [];
|
||||
const hasMatch = values.some((value) =>
|
||||
haystacks(value).some((hay) => needles.some((needle) => hay.toLowerCase().includes(needle)))
|
||||
);
|
||||
checks.push(
|
||||
check(
|
||||
|
||||
@@ -41,6 +41,7 @@ export function createGlobalModeRunner(
|
||||
workspaceFixtures: initial?.workspace,
|
||||
liveEditorDrafts: initial?.liveEditorDrafts,
|
||||
user: initial?.user,
|
||||
sessionChat: context.evalCase?.runtime?.sessionChat,
|
||||
maxIterations: context.evalCase?.runtime?.maxTurns,
|
||||
provider: modelConfig.provider,
|
||||
model: modelConfig.model,
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT args->'__git_sync_auto_pull' FROM v2_job WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "020cc73e39782e7be50a146373096bd4378988af6447feeafd243262c246a9f5"
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO amqp_trigger (\n amqp_resource_path,\n queue_name,\n exchange,\n options,\n workspace_id,\n path,\n script_path,\n is_flow,\n permissioned_as,\n mode,\n edited_by,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "trigger_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"enabled",
|
||||
"disabled",
|
||||
"suspended"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "02e39bb9957d3ccfe3d573994cd281a22467010d2c4a0611c8ae02742336f433"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM feature_usage WHERE day < CURRENT_DATE - 60",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0390d9e4fae597aabdeef940aa5b5014e1c889ba9181ee3331f128c56c9d22de"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings (workspace_id, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, deploy_to, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, teams_command_script, teams_team_id, teams_team_name, git_app_installations, ducklake, slack_oauth_client_id, slack_oauth_client_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts) SELECT $1, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, deploy_to, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, teams_command_script, teams_team_id, teams_team_name, git_app_installations, ducklake, slack_oauth_client_id, slack_oauth_client_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts FROM workspace_settings WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "042c42957221352891f7433f2ec0d6b7f686d9c7d505590eec7ba3bfeeb406ca"
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_completed AS cj\n ( workspace_id\n , id\n , started_at\n , duration_ms\n , result\n , result_columns\n , canceled_by\n , canceled_reason\n , flow_status\n , workflow_as_code_status\n , memory_peak\n , status\n , worker\n )\n SELECT q.workspace_id, q.id, started_at, COALESCE($9::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE(started_at, now()))))*1000), $3, $10, $5, $6,\n flow_status, workflow_as_code_status,\n $8, CASE WHEN $4::BOOL THEN 'canceled'::job_status\n WHEN $7::BOOL THEN 'skipped'::job_status\n WHEN $2::BOOL THEN 'success'::job_status\n ELSE 'failure'::job_status END AS status,\n q.worker\n FROM v2_job_queue q LEFT JOIN v2_job_status USING (id) WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $3 RETURNING duration_ms AS \"duration_ms!\"",
|
||||
"query": "INSERT INTO v2_job_completed AS cj\n ( workspace_id\n , id\n , started_at\n , duration_ms\n , result\n , result_columns\n , canceled_by\n , canceled_reason\n , flow_status\n , workflow_as_code_status\n , memory_peak\n , status\n , worker\n )\n SELECT q.workspace_id, q.id, started_at, COALESCE($9::bigint, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE(started_at, now()))))*1000), $3::text::jsonb, $10, $5, $6,\n flow_status, workflow_as_code_status,\n $8, CASE WHEN $4::BOOL THEN 'canceled'::job_status\n WHEN $7::BOOL THEN 'skipped'::job_status\n WHEN $2::BOOL THEN 'success'::job_status\n ELSE 'failure'::job_status END AS status,\n q.worker\n FROM v2_job_queue q LEFT JOIN v2_job_status USING (id) WHERE q.id = $1\n ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status, result = $3::text::jsonb RETURNING duration_ms AS \"duration_ms!\"",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -13,7 +13,7 @@
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Text",
|
||||
@@ -27,5 +27,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "36c4e57afcab22f4b6825ccebe47767b8a8fe0a638250f7c7777e5a9f7530e5c"
|
||||
"hash": "042ff3003bf82d11a78a1074a63379c19880e4d66dbf4dd5391f053b6dfa6b01"
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT kind, path, script_path, is_flow FROM (\n SELECT 'schedule' AS kind, path, script_path, is_flow FROM schedule\n WHERE workspace_id = $1\n AND script_path IS NOT NULL\n UNION ALL\n SELECT 'email', path, script_path, is_flow FROM email_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'kafka', path, script_path, is_flow FROM kafka_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'mqtt', path, script_path, is_flow FROM mqtt_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'amqp', path, script_path, is_flow FROM amqp_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'nats', path, script_path, is_flow FROM nats_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'postgres', path, script_path, is_flow FROM postgres_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'sqs', path, script_path, is_flow FROM sqs_trigger\n WHERE workspace_id = $1\n UNION ALL\n SELECT 'gcp', path, script_path, is_flow FROM gcp_trigger\n WHERE workspace_id = $1\n ) t\n WHERE ($2::text IS NULL OR script_path LIKE $2)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "05843e117361d6b7cd9da652596d3be7a970d602d3809396d9253d7e5dc88152"
|
||||
}
|
||||
+5
-5
@@ -46,11 +46,11 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT parent_workspace_id, is_dev_workspace FROM workspace WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "parent_workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "is_dev_workspace",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "079e1dab42a783a1e5e4cba5faa854b4b727b1fab89e42f2efaf97c63dad6384"
|
||||
}
|
||||
+2
-1
@@ -36,7 +36,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n (elem->>'installation_id')::bigint as installation_id,\n elem->>'github_base_url' as github_base_url\n FROM workspace_settings,\n LATERAL jsonb_array_elements(git_app_installations) AS elem\n WHERE workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "installation_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "github_base_url",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "0bb3de62f920a4f6c5da6f0a7bfc780a5ac35fa7639c4ee3a449b693add24241"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, workspace_id, tag) VALUES ($1, 'test-workspace', 'other')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0bd853be57b43a7820cc1d73d92b8babd50b1092d4ad70e022431bdcf32e378f"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE descendants AS (\n SELECT id, 0 AS depth FROM workspace\n WHERE parent_workspace_id = $1 AND NOT deleted\n UNION ALL\n SELECT w.id, d.depth + 1 FROM workspace w\n JOIN descendants d ON w.parent_workspace_id = d.id\n WHERE NOT w.deleted AND d.depth < 10\n )\n SELECT id as \"id!\" FROM descendants WHERE (id = $2 OR id = $3)\n ORDER BY (id = $2) DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "0e016aaa6842767ac636e5b3e1febfa9cee08b93e0562beb629d84c4c689f4e7"
|
||||
}
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COALESCE(username, split_part(email, '@', 1)) AS \"username!\", email FROM password WHERE super_admin = true AND disabled = false ORDER BY email LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "username!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "17cdf02b4912078459526205849246fd2bdf9e3fce89852120aa4ad4ad6abfc3"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id FROM workspace WHERE parent_workspace_id = $1 AND NOT deleted AND is_dev_workspace AND COALESCE(dev_workspace_label, 'dev') = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1b32339718406531acd2d051386d2817e9ba0b3a66f7eb4fc1177764563a0665"
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n j.id AS \"id!\", j.workspace_id AS \"workspace_id!\", j.parent_job, j.flow_step_id IS NOT NULL AS \"is_flow_step?\",\n COALESCE(s.flow_status, s.workflow_as_code_status)::text AS \"flow_status: Box<str>\", r.ping AS last_ping, j.same_worker AS \"same_worker?\",\n q.worker AS \"worker?\",\n wp.ping_at AS \"worker_last_ping?\",\n wp.memory_usage AS \"worker_memory_usage?\",\n wp.wm_memory_usage AS \"worker_wm_memory_usage?\",\n wp.memory AS \"worker_memory_total?\",\n wp.worker_group AS \"worker_group?\",\n wp.wm_version AS \"worker_version?\",\n wp.current_job_id AS \"worker_current_job_id?\",\n wp.worker_instance AS \"worker_instance?\"\n FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_runtime r USING (id) LEFT JOIN v2_job_status s USING (id)\n LEFT JOIN worker_ping wp ON wp.worker = q.worker\n WHERE q.running = true AND q.suspend = 0 AND q.suspend_until IS null AND q.scheduled_for <= now()\n AND (j.kind = 'flow' OR j.kind = 'flowpreview' OR j.kind = 'flownode' OR j.kind = 'singlestepflow')\n AND r.ping IS NOT NULL AND r.ping < NOW() - ($1 || ' seconds')::interval\n AND q.canceled_by IS NULL\n\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "workspace_id!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "parent_job",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "is_flow_step?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "flow_status: Box<str>",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "last_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "same_worker?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "worker?",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "worker_last_ping?",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "worker_memory_usage?",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "worker_wm_memory_usage?",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "worker_memory_total?",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "worker_group?",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "worker_version?",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "worker_current_job_id?",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "worker_instance?",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "1bf87fe9667f860c6089bffb803291f4ff5c979be956369fa4406d789cca92fc"
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT w.parent_workspace_id, w.is_dev_workspace, w.dev_workspace_label,\n p.is_dev_workspace as \"parent_is_dev_workspace?\", p.dev_workspace_label as \"parent_dev_label?\"\n FROM workspace w LEFT JOIN workspace p ON p.id = w.parent_workspace_id\n WHERE w.id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "parent_workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "is_dev_workspace",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "dev_workspace_label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "parent_is_dev_workspace?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "parent_dev_label?",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1c018b483795d9d33baa7be2ee41f48da6fe47bc29b6987cf60edd56bb1663b6"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH t1 AS (UPDATE websocket_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t2 AS (UPDATE kafka_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t3 AS (UPDATE postgres_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t4 AS (UPDATE mqtt_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t5 AS (UPDATE nats_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t6 AS (UPDATE sqs_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4), t7 AS (UPDATE amqp_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4) UPDATE gcp_trigger SET script_path = $1, server_id = NULL WHERE script_path = $2 AND workspace_id = $3 AND is_flow = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1cd16f93bdc90b24ceb1e86495069c3592218d34e58d617253e8b563bc661a7a"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag, running)\n VALUES ($1, $2, now() + ($3::bigint::text || ' s')::interval, 'other', $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar",
|
||||
"Int8",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1ce0728a6b0942fecf8ce5f4a06857a2601b10b057877c3b2795b8ea9c928c1b"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag, running)\n VALUES ($1, 'test-workspace', now() + ($2::bigint::text || ' s')::interval, 'other', $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Int8",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "20ae664da5ebd70a6d37609f6b9a3e10d8d524fbaaa8eddf74f12a906b3c8306"
|
||||
}
|
||||
+4
-2
@@ -40,7 +40,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -79,7 +80,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT s.workspace_id, s.path\n FROM schedule s JOIN workspace w ON w.id = s.workspace_id AND NOT w.deleted\n WHERE s.enabled IS TRUE\n AND NOT EXISTS (\n SELECT 1 FROM v2_job_queue q JOIN v2_job j USING (id)\n WHERE j.workspace_id = s.workspace_id\n AND j.trigger_kind = 'schedule'\n AND j.trigger = s.path\n AND j.runnable_path = s.script_path\n AND j.parent_job IS NULL\n )",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2756fab586489be33a3031fabfdc87be642091a2f8a6973d001e59be892a984d"
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar"
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO feature_usage (feature, kind, key, entity_id, value)\n SELECT * FROM UNNEST($1::text[], $2::text[], $3::text[], $4::text[], $5::bigint[])\n ON CONFLICT (feature, kind, key, entity_id, day)\n DO UPDATE SET value = feature_usage.value + EXCLUDED.value, updated_at = now()",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"TextArray",
|
||||
"TextArray",
|
||||
"TextArray",
|
||||
"TextArray",
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2cdb9076747b61c01b6d389157e42bcbf66f0cc5900c7777141df47776e32fa3"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings SELECT $1, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, deploy_to, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, teams_command_script, teams_team_id, teams_team_name, git_app_installations, ducklake, slack_oauth_client_id, slack_oauth_client_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts FROM workspace_settings WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2dc5a74c4e614b43148925cdbac234f6a2f295a9c46897f5e9aab8f7298ff0d7"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT username, email FROM usr WHERE workspace_id = $1 AND is_admin = true AND operator = false AND disabled = false ORDER BY username LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3202bed875693ae923f496272cd8ad89b2f17a9d3ef4659c2d2284415177b32c"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE up AS (\n SELECT id, parent_workspace_id, 0 AS depth FROM workspace WHERE id = $1\n UNION ALL\n SELECT w.id, w.parent_workspace_id, up.depth + 1\n FROM workspace w JOIN up ON w.id = up.parent_workspace_id\n WHERE up.depth < 10\n )\n SELECT git_sync FROM workspace_settings\n WHERE workspace_id = (SELECT id FROM up WHERE parent_workspace_id IS NULL LIMIT 1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3451cbd6a783da68324f64c2e95ad146e2ca8c1cd7c10891fe21a7638aedc381"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT count(*) FROM v2_job_completed\n WHERE workspace_id = $1 AND id = ANY($2) AND status = 'success'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "387e135bf363533089493c35eb39dd4864f4e0aa0093df47df7d444ee748dbb2"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE schedule SET error = $1 WHERE workspace_id = $2 AND path = $3 AND enabled IS TRUE",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "392681cc6aa1ed2119d33aa68c8de907889fdfab77fa3af85a96ddaa068af733"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET git_sync = jsonb_set(\n git_sync,\n '{repositories}',\n (SELECT jsonb_agg(\n CASE WHEN elem->>'git_repo_resource_path' = $2\n AND jsonb_typeof(elem->'auto_pull') = 'object'\n THEN jsonb_set(elem, '{auto_pull}',\n ((elem->'auto_pull') - 'webhook_id' - 'webhook_secret' - 'webhook_error') || $3)\n ELSE elem END)\n FROM jsonb_array_elements(git_sync->'repositories') AS elem)\n )\n WHERE workspace_id = $1\n AND jsonb_typeof(git_sync->'repositories') = 'array'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3b7498768197d7e002c3965ad16e6b88daa38ba272d22f846bd4267a134c404c"
|
||||
}
|
||||
+2
-1
@@ -129,7 +129,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT provider, model, mode,\n COUNT(*)::BIGINT as \"session_count!\",\n COALESCE(SUM(message_count), 0)::BIGINT as \"message_count!\"\n FROM ai_chat_usage\n WHERE created_at > NOW() - INTERVAL '30 days'\n GROUP BY provider, model, mode\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "provider",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "model",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "mode",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "session_count!",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "message_count!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3ec92c1682f3ce701028f66f7ce83030e7a7ce32971a5621aceb738a3673f943"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET error_handler_fallback_to_instance_alerts = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "40c591ab93adf3bb4f17598f78b76705eeda7d17d49cafb3e9e6285dee6804c9"
|
||||
}
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT (kind = 'flow' AND parent_job IS NULL) AS \"restartable!\"\n FROM v2_job WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "restartable!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "4371e59b85cc279dba03800aacde8a4b96c9629cdc785edc7a389c3eb7269608"
|
||||
}
|
||||
+2
-1
@@ -33,7 +33,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_completed SET status = 'canceled', canceled_by = 'monitor',\n flow_status = $2 WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4a35f9a0201e283d854bd77f7f79d4c24699c2c7abad4086e0bb876c5c6b2c38"
|
||||
}
|
||||
+2
-1
@@ -81,7 +81,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -40,7 +40,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -47,7 +47,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -35,7 +35,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -76,7 +77,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM v2_job_queue",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "521367aaea7beefe4ff9cdb66273f8e3cddfbaf598536f1753f0824f84604826"
|
||||
}
|
||||
+2
-1
@@ -36,7 +36,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT script_path, table_path, kind, name, expr, filter FROM data_metric dm WHERE dm.workspace_id = $1 AND ($2::text IS NULL OR dm.table_path = $2) AND ($3::text IS NULL OR dm.script_path = $3 OR dm.script_path LIKE $4) AND ($6::text IS NULL OR (dm.table_path, dm.kind, dm.name, dm.script_path) > ($6, $7, $8, $9)) AND ( $10 OR dm.script_path = ANY($11) OR EXISTS ( SELECT 1 FROM unnest($12::text[]) AS pfx WHERE dm.script_path = pfx OR left(dm.script_path, length(pfx) + 1) = pfx || '/' ) ) AND EXISTS ( SELECT 1 FROM script s WHERE s.workspace_id = dm.workspace_id AND s.path = dm.script_path AND s.archived = false AND s.deleted = false ) ORDER BY dm.table_path, dm.kind, dm.name, dm.script_path LIMIT $5",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "table_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "kind",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "expr",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "filter",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Int8",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool",
|
||||
"TextArray",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "58a79f32f880c5ea0ee8821ca4ad07f0f0519a31300e597776ad8b77130501d2"
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n workspace_id,\n slack_team_id,\n teams_team_id,\n teams_team_name,\n teams_team_guid,\n slack_name,\n slack_command_script,\n teams_command_script,\n slack_email,\n slack_oauth_client_id,\n slack_oauth_client_secret,\n customer_id,\n plan,\n webhook,\n deploy_to,\n ai_config,\n large_file_storage,\n datatable,\n ducklake,\n git_sync,\n deploy_ui,\n default_app,\n default_scripts,\n mute_critical_alerts,\n color,\n operator_settings,\n git_app_installations,\n auto_invite,\n error_handler,\n success_handler,\n public_app_execution_limit_per_minute,\n error_handler_fallback_to_instance_alerts\n FROM\n workspace_settings\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "slack_team_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "teams_team_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "teams_team_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "teams_team_guid",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "slack_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "slack_command_script",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "teams_command_script",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "slack_email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "customer_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "plan",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "datatable",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "ducklake",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "git_app_installations",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"name": "auto_invite",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"name": "error_handler",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"name": "success_handler",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"name": "public_app_execution_limit_per_minute",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"name": "error_handler_fallback_to_instance_alerts",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5ae9ad14effe923f1952d0e1d1f67ad00ff6c13918d46dd474cc48824b12ccaf"
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n amqp_trigger\n SET\n amqp_resource_path = $1,\n queue_name = $2,\n exchange = $3,\n options = $4,\n is_flow = $5,\n edited_by = $6,\n permissioned_as = $7,\n script_path = $8,\n path = $9,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE\n workspace_id = $10 AND\n path = $11\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5f2e66e53166840c68efbc521fe9393023e79f948976e6ffc0e9bfc94623393c"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_completed SET status = 'canceled', canceled_by = 'admin',\n flow_status = $2 WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "60201b41a0bfea89a201a25ae2431fe94999ca4f11934fbab4638b2653a678dc"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET deploy_to = $1, error_handler_fallback_to_instance_alerts = false WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "620c9efca071cb5fb8b33857129e63954f39b291f525e36e6ece916b98b4d2c9"
|
||||
}
|
||||
+2
-1
@@ -38,7 +38,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT username FROM usr\n WHERE workspace_id = $1 AND email = $2 AND NOT operator AND NOT disabled",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "67d86d334029276d78bf8fa14b25702eba0b0d674b99f952af94487b3e54211c"
|
||||
}
|
||||
+2
-1
@@ -162,7 +162,8 @@
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness"
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT count(*) FROM (\n SELECT ck.job_id FROM concurrency_key ck\n WHERE ck.key = $1 AND ck.ended_at IS NULL\n LIMIT $2\n ) s WHERE EXISTS (\n SELECT 1 FROM v2_job_queue q WHERE q.id = s.job_id AND q.running = false\n )",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "694d787e06b5ef9fbb5ca1315652023ecc0b3122501b72ec7f825966419759ac"
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT w.parent_workspace_id, w.is_dev_workspace, w.dev_workspace_label,\n p.is_dev_workspace as \"parent_is_dev_workspace?\", p.dev_workspace_label as \"parent_dev_label?\"\n FROM workspace w LEFT JOIN workspace p ON p.id = w.parent_workspace_id\n WHERE w.id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "parent_workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "is_dev_workspace",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "dev_workspace_label",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "parent_is_dev_workspace?",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "parent_dev_label?",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "69d0c59bb11a01ffe9934f62feb93c4e64166a6d4d999911dfab743c43bdac35"
|
||||
}
|
||||
+2
-1
@@ -47,7 +47,8 @@
|
||||
"trigger_nextcloud",
|
||||
"trigger_google",
|
||||
"trigger_github",
|
||||
"data_pipeline"
|
||||
"data_pipeline",
|
||||
"trigger_amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -32,7 +32,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET git_sync = jsonb_set(\n git_sync,\n '{repositories}',\n (SELECT jsonb_agg(\n CASE WHEN elem->>'git_repo_resource_path' IN ($2, '$res:' || $2)\n THEN CASE WHEN $3::text IS NULL THEN elem - 'open_pr_error'\n ELSE jsonb_set(elem, '{open_pr_error}', to_jsonb($3::text), true) END\n ELSE elem END)\n FROM jsonb_array_elements(git_sync->'repositories') AS elem)\n )\n WHERE workspace_id = $1\n AND jsonb_typeof(git_sync->'repositories') = 'array'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "71faf95db96589e5554882e1dd036bd19923e0b0581efbae448197d6accf01f3"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE schedule SET error = NULL WHERE workspace_id = $1 AND path = $2 AND enabled IS TRUE",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "72b44f728dea65b0542cb72244259703e5029536898469a484ebed23d6226e82"
|
||||
}
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"nextcloud",
|
||||
"google",
|
||||
"github",
|
||||
"azure"
|
||||
"azure",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO v2_job (id, workspace_id, tag) VALUES ($1, $2, 'other')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "750eb7365e2590a42d9f3c4cbbf193ea4129874c36e2c334e9e73f4b4c54ad07"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_completed\n SET status = 'canceled', canceled_by = 'monitor', canceled_reason = 'zombie flow',\n flow_status = $2\n WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7d2923f940cf5a8cbbdc51de91c7e00942a8f4f5251502ef8efc0f510a857551"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings\n (workspace_id, color, error_handler_fallback_to_instance_alerts)\n VALUES ($1, $2, $3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7da2e04f64cef6634256d7b3b12d8a46dc7320e7dcdd64befe808dec8cbe2265"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user