mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-12 16:02:56 +00:00
Merge origin/main into fix-runnable-test-draft-app
Four conflicts, all where main reshaped code this branch had added to. `open_preview` grew an access-gated `schemaFor` and a view/edit `mode`, and `openSessionPreview` gained `workspace` + `access` parameters: kept both, with this branch's `draftTarget` and `ctx.target` threaded through the new signature. Main also added an early return for pipelines, which makes this branch's `kind === 'pipeline'` guard on the path unreachable — dropped it, along with the pipeline comment the restructure stranded above non-pipeline code. `draftWriteFailure` took main's `DRAFT_CONFLICT_RESULT` / `DRAFT_SAVE_FAILED_RESULT` constants with this branch's `displayPath`, and `init_app` took main's rewritten template message with this branch's resolved target. `setDeployedInSessionHandler`: main replaced `syncPreviewWithDeployed` with `itemDeployed`, which also stops sync, discards, bumps the deploy revision and invalidates both workspace caches, and added those invalidations to the not-mounted path. Kept all of it, with this branch's forget-and-retarget when the deploy moved the item, and `itemDeployed` now called at the path the tab was retargeted to — identical to the addressed path when nothing moved. Outside the conflicts, main renamed `isEditorTabFor(url, target)` to `isItemTabFor(url, target, side)` for its editor/viewer tab split. The merge kept this branch's two calls to the old name without flagging it, so `retargetEditorItem` threw at runtime while compiling clean; both now pass `'edit'`, the side whose cell can be raced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,15 @@ authoring and the full run reference.
|
||||
|
||||
Run the affected mode **before** your change and **after**, same model(s), same cases.
|
||||
|
||||
## Where guidance goes
|
||||
|
||||
What the model should know about Windmill itself (flow shapes, groups, data tables, app
|
||||
access, secrets…) belongs in `system_prompts/base/` or `system_prompts/languages/`, which also
|
||||
feed the CLI's skills — not in a `*/core.ts` prompt builder, where only this chat would see it.
|
||||
The builders add tool plumbing and runtime values only. Scope a sentence to one consumer with
|
||||
`<!-- chat-only -->` / `<!-- cli-only -->`, keep chat tool names out of the shared files, and
|
||||
rerun `python system_prompts/generate.py`. `system_prompts/README.md` has the mechanics.
|
||||
|
||||
## Measure the window first, and cumulative second
|
||||
|
||||
Optimize **`finalContextTokens`** (window occupancy — what drives overflow and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: local-review-codex
|
||||
description: Run the CI Codex PR review locally against this branch's unpushed work (committed + uncommitted) before pushing. Same policy and reasoning effort as the codex-pr-review GitHub action, on a newer model.
|
||||
description: Run the CI Codex PR review locally against this branch's unpushed work (committed + uncommitted) before pushing. Same policy, model and reasoning effort as the codex-pr-review GitHub action.
|
||||
---
|
||||
|
||||
# Local Codex Review (pre-push)
|
||||
@@ -11,18 +11,18 @@ before the PR exists. Use this before `git push` on a non-trivial change.
|
||||
|
||||
**Correspondence with CI** — identical:
|
||||
- Policy: `REVIEW.md` (severity triage, public-surface checklist, AGENTS.md compliance, test coverage).
|
||||
- Model: `gpt-6.1-sol`.
|
||||
- Reasoning effort: `model_reasoning_effort="xhigh"`.
|
||||
- Output: markdown starting with `## Codex Review`, findings tagged P0 / P1 / P2 with file:line.
|
||||
|
||||
**Differences from CI** — local-only:
|
||||
- Model is `gpt-6-astra`; CI stays on `gpt-5.6-sol`. Not an oversight to reconcile: `gpt-6-astra` is confirmed on the ChatGPT auth `codex login` uses locally, while CI authenticates with `OPENAI_API_KEY` (`codex-pr-review.yml` prefers it over `CODEX_AUTH_JSON`) and that tier is unverified for the model. Move CI once API access is confirmed, or once CI switches to `CODEX_AUTH_JSON`.
|
||||
- Scope is the current branch vs `main` at the merge-base, **including uncommitted changes** (CI reviews a pushed PR diff).
|
||||
- Sandbox is `read-only` (CI uses `danger-full-access` on an ephemeral runner). Codex reads the diff and files but cannot modify your working tree.
|
||||
- Fresh context is inherent: `codex exec` is a separate cold process, so it does not anchor on the current chat session — the same reason `local-review` insists on a subagent.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `codex` CLI **>= 0.153.4** installed and authed via `codex login` (an `OPENAI_API_KEY` in the environment takes priority and may not reach `gpt-6-astra` — see the model note above). Older CLIs reject the model with "requires a newer version of Codex"; `run.sh` checks the version up front. Upgrade with `npm install --global @openai/codex@0.153.4` (may need `sudo` for a global install). This matches the pin in `.github/workflows/codex-pr-review.yml` — the CLI version is the same on both sides, only the model differs.
|
||||
- `codex` CLI **>= 0.159.3** installed and authed via `codex login` or an `OPENAI_API_KEY` in the environment (which takes priority). Older CLIs reject the model with "requires a newer version of Codex"; `run.sh` checks the version up front. Upgrade with `npm install --global @openai/codex@0.159.3` (may need `sudo` for a global install). This matches the pin in `.github/workflows/codex-pr-review.yml` — the CLI version is the same on both sides.
|
||||
- `git fetch` the base ref if it's stale, so the merge-base is accurate.
|
||||
|
||||
## Run
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local Codex review — mirrors the .github/workflows/codex-pr-review.yml CI job,
|
||||
# but scoped to this branch's unpushed work (committed + uncommitted) so you can
|
||||
# review before pushing. Same policy (REVIEW.md) and reasoning effort (xhigh) as CI.
|
||||
#
|
||||
# The model deliberately differs from CI: gpt-6-astra is confirmed available on the
|
||||
# ChatGPT auth `codex login` uses here, but CI authenticates with OPENAI_API_KEY and
|
||||
# that tier is unverified for it, so codex-pr-review.yml stays on gpt-5.6-sol.
|
||||
# review before pushing. Same policy (REVIEW.md), model and reasoning effort (xhigh)
|
||||
# as CI.
|
||||
#
|
||||
# Usage: run.sh [BASE_REF] (BASE_REF defaults to "main")
|
||||
set -euo pipefail
|
||||
|
||||
MODEL="gpt-6-astra"
|
||||
CODEX_MIN="0.153.4"
|
||||
MODEL="gpt-6.1-sol"
|
||||
CODEX_MIN="0.159.3"
|
||||
|
||||
BASE_REF="${1:-main}"
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
@@ -119,8 +119,8 @@ Form components (TextInput, Toggle, Select, etc.) should use the unified size sy
|
||||
New user-facing UX is the main source of `feature_usage` counters — propose them in the plan, not
|
||||
as a separate question, and read `docs/feature-telemetry.md` first. `logFeatureUsage()` from
|
||||
`$lib/utils/featureUsage` is only half the change: the `(feature, kind)` pair must also be
|
||||
registered in the backend allowlist or every event is silently discarded, and the disclosure copy
|
||||
in `InstanceSettings.svelte` must name what you added.
|
||||
registered in the backend allowlist or every event is silently discarded. The disclosure copy in
|
||||
`InstanceSettings.svelte` stays at the category level; see the recipe's step 3.
|
||||
|
||||
## Svelte MCP Server
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
name: AI Evals (scheduled)
|
||||
|
||||
# Full ai_evals suites on current models, posted to the AI evals dashboard in
|
||||
# the windmill-prod workspace (f/ai/ai_evals_dashboard) so quality is tracked
|
||||
# over time. Weekly, since one pass of every suite at --runs 3 costs about 50M
|
||||
# tokens per model: every suite runs on MODELS, and global (the mode users get)
|
||||
# also runs on GLOBAL_EXTRA_MODELS, one flagship per other provider. Run it by
|
||||
# hand to measure a branch against main.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 1"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
modes:
|
||||
description: "Space-separated modes"
|
||||
default: "global flow app script cli"
|
||||
models:
|
||||
description: "Space-separated model aliases (bun run cli -- models)"
|
||||
default: "sonnet-5.5"
|
||||
global_extra_models:
|
||||
description: "Extra model aliases for global mode only"
|
||||
default: "gpt-6-astra gemini-3.8-flash"
|
||||
runs:
|
||||
description: "Runs per case"
|
||||
default: "3"
|
||||
reasoning:
|
||||
description: "Reasoning effort for frontend modes (empty: the product default)"
|
||||
default: ""
|
||||
|
||||
concurrency:
|
||||
group: ai-evals-scheduled-${{ github.ref }}
|
||||
|
||||
env:
|
||||
MODELS: ${{ inputs.models || 'sonnet-5.5' }}
|
||||
GLOBAL_EXTRA_MODELS: ${{ inputs.global_extra_models || 'gpt-6-astra gemini-3.8-flash' }}
|
||||
RUNS: ${{ inputs.runs || '3' }}
|
||||
REASONING: ${{ inputs.reasoning }}
|
||||
INGEST_URL: https://app.windmill.dev/api/r/f/ai/ingest_ai_eval_run
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
modes: ${{ steps.modes.outputs.modes }}
|
||||
steps:
|
||||
- id: modes
|
||||
env:
|
||||
MODES: ${{ inputs.modes || 'global flow app script cli' }}
|
||||
run: |
|
||||
echo "modes=$(jq -cn --arg m "$MODES" '$m | split(" ")
|
||||
| map(select(IN("global", "flow", "app", "script", "cli")))')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
evals:
|
||||
needs: setup
|
||||
runs-on: ubicloud-standard-16
|
||||
timeout-minutes: 330
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
mode: ${{ fromJSON(needs.setup.outputs.modes) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_DB: windmill
|
||||
POSTGRES_PASSWORD: changeme
|
||||
options: >-
|
||||
--health-cmd pg_isready --health-interval 10s --health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache-workspaces: backend
|
||||
toolchain: 1.97.0
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.4.0
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
- name: Build Windmill
|
||||
working-directory: ./backend
|
||||
env:
|
||||
SQLX_OFFLINE: true
|
||||
CARGO_BUILD_JOBS: 12
|
||||
RUSTFLAGS: ""
|
||||
run: cargo build --features quickjs
|
||||
|
||||
- name: Start Windmill
|
||||
working-directory: ./backend
|
||||
env:
|
||||
DATABASE_URL: postgres://postgres:changeme@localhost:5432/windmill
|
||||
RUST_LOG: info
|
||||
run: |
|
||||
mkdir -p ../ai_evals/logs
|
||||
./target/debug/windmill > ../ai_evals/logs/windmill.log 2>&1 &
|
||||
for i in $(seq 1 60); do
|
||||
curl -sf http://localhost:8000/api/version > /dev/null 2>&1 && break
|
||||
sleep 2
|
||||
done
|
||||
curl -sf http://localhost:8000/api/version > /dev/null || { tail -50 ../ai_evals/logs/windmill.log; exit 1; }
|
||||
|
||||
- name: Install frontend deps + generate client
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
npm ci
|
||||
npm run generate-backend-client
|
||||
|
||||
- name: Install CLI deps + generate CLI client
|
||||
working-directory: ./cli
|
||||
run: bun install && ./gen_wm_client.sh && ./windmill-utils-internal/gen_wm_client.sh
|
||||
|
||||
- name: Run ${{ matrix.mode }} evals
|
||||
working-directory: ./ai_evals
|
||||
env:
|
||||
MODE: ${{ matrix.mode }}
|
||||
WMILL_AI_EVAL_BACKEND_URL: http://localhost:8000
|
||||
WMILL_AI_EVAL_BACKEND_WORKSPACE: integration-tests
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
run: |
|
||||
bun install
|
||||
mkdir -p results
|
||||
models="$MODELS"
|
||||
[ "$MODE" = global ] && models="$models $GLOBAL_EXTRA_MODELS"
|
||||
reasoning=()
|
||||
[ -n "$REASONING" ] && [ "$MODE" != cli ] && reasoning=(--reasoning "$REASONING")
|
||||
for m in $models; do
|
||||
bun run cli -- run "$MODE" --model "$m" --runs "$RUNS" "${reasoning[@]}" \
|
||||
--output "$PWD/results/$MODE-$m.json" || echo "::warning::$MODE on $m errored"
|
||||
done
|
||||
|
||||
- name: Post results to the dashboard
|
||||
if: always()
|
||||
working-directory: ./ai_evals
|
||||
env:
|
||||
MODE: ${{ matrix.mode }}
|
||||
INGEST_TOKEN: ${{ secrets.AI_EVALS_INGEST_TOKEN }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
[ -n "$INGEST_TOKEN" ] || { echo "::warning::AI_EVALS_INGEST_TOKEN is not set"; exit 0; }
|
||||
shopt -s nullglob
|
||||
for f in results/"$MODE"-*.json; do
|
||||
# Keep what the dashboard reads; the full traces stay in the run's artifacts.
|
||||
jq -c --arg ref "$GITHUB_REF" --arg trigger "$GITHUB_EVENT_NAME" --arg url "$RUN_URL" '
|
||||
{result_json: (del(.cases[].prompt, .cases[].initialPath, .cases[].expectedPath)
|
||||
| .cases[].attempts[] |= {attempt, passed, durationMs, toolCallCount, judgeScore,
|
||||
judgeSummary, error, tokenUsage, checks: [.checks[]? | {name, passed}]}),
|
||||
git_ref: $ref, trigger: $trigger, run_url: $url}' "$f" \
|
||||
| curl -sf --retry 3 -X POST "$INGEST_URL" \
|
||||
-H "Authorization: Bearer $INGEST_TOKEN" -H 'content-type: application/json' \
|
||||
--data-binary @- && echo " <- $f" || echo "::warning::failed to post $f"
|
||||
done
|
||||
|
||||
- name: Archive logs and results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: ai-evals-${{ matrix.mode }}
|
||||
path: |
|
||||
ai_evals/logs
|
||||
ai_evals/results
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
mkdir -p results
|
||||
# One cheap model per provider (anthropic/openai/googleai/deepseek).
|
||||
fail=0
|
||||
for m in haiku 4o gemini-3-flash-preview deepseek-v4-flash; do
|
||||
for m in haiku gpt-6-luna gemini-3.8-flash deepseek-flash; do
|
||||
echo "::group::global-test1-script-create ($m)"
|
||||
if ! bun run cli -- run global global-test1-script-create \
|
||||
--model "$m" --execution-only --output "$PWD/results/ci-$m.json"; then
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
allowed_bots: 'windmill-internal-app[bot]'
|
||||
trigger_phrase: '/plan'
|
||||
claude_args: |
|
||||
--model claude-opus-5
|
||||
--model claude-opus-5-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-5
|
||||
--model claude-opus-5-5
|
||||
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
|
||||
- name: Install Codex CLI
|
||||
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
|
||||
run: npm install --global @openai/codex@0.153.4
|
||||
run: npm install --global @openai/codex@0.159.3
|
||||
|
||||
- name: Configure Codex auth
|
||||
if: steps.codex_config.outputs.enabled == 'true' && steps.pr.outputs.skip != 'true'
|
||||
@@ -359,7 +359,7 @@ jobs:
|
||||
# e.g. a GitHub Action's index.js, which then runs with our credentials.
|
||||
codex exec \
|
||||
-C "$GITHUB_WORKSPACE" \
|
||||
-m gpt-5.6-sol \
|
||||
-m gpt-6.1-sol \
|
||||
-c 'model_reasoning_effort="xhigh"' \
|
||||
-s "$SANDBOX_MODE" \
|
||||
-o "$RUNNER_TEMP/codex-final-message.md" \
|
||||
|
||||
@@ -208,4 +208,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-5
|
||||
--model claude-opus-5-5
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build test image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/DockerfileExtra
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "1.819.0"
|
||||
".": "1.820.0"
|
||||
}
|
||||
|
||||
@@ -29,10 +29,16 @@ Open-source platform for internal tools, workflows, API integrations, background
|
||||
- **Agent workers**: `docs/agent-worker-e2e.md` — building and running one locally. An agent
|
||||
reaches the DB only through the API, so `Connection::Http` paths are never taken by a plain
|
||||
`cargo run`; a normal build cannot start one at all.
|
||||
- **External instance data tables**: `docs/external-instance-datatables.md` — the cluster Windmill
|
||||
administers behind `external_instance` data tables and Ducklake catalogs: its invariants (one
|
||||
lifecycle lock, managed-object markers, the setup gate, per-cluster roles, fork copy ownership)
|
||||
and how to run one locally
|
||||
- **Enterprise**: `docs/enterprise.md` — EE file conventions and PR workflow
|
||||
- **Operator write rights**: `docs/operator-write-rights.md` — which `operator_settings` flags are
|
||||
enforced rather than cosmetic, and why a right that is granted-unless-withdrawn needs `Option`
|
||||
fields and a jsonb merge rather than a serde default
|
||||
- **Operator builder rights**: `docs/operator-builder-rights.md` — the workspace setting that lets
|
||||
operators compose flows, what the composition check must cover, and why it costs a full seat
|
||||
- **Auth surface**: `docs/auth-surface.md` — credential precedence, session/cache invalidation
|
||||
scope, which token labels email their owner at expiry, how OAuth login matches `login_type`, and
|
||||
that every superadmin route refuses `$WM_TOKEN`. Read before designing anything that creates
|
||||
@@ -43,11 +49,12 @@ Open-source platform for internal tools, workflows, API integrations, background
|
||||
- **Backend patterns**: use the `rust-backend` skill when writing Rust code
|
||||
- **Frontend patterns**: use the `svelte-frontend` skill when writing Svelte code. Do NOT edit svelte files unless you have read that skill.
|
||||
- **Frontend UUIDs**: do not call `crypto.randomUUID()` in frontend code. Import `randomUUID` from `$lib/utils/uuid` instead.
|
||||
- **Code review**: review the current PR or branch against the shared review policy in `REVIEW.md` (severity triage, public-surface checklist, AGENTS.md compliance, test-coverage assessment). The skill at `.agents/skills/local-review/SKILL.md` orchestrates it. All three CLIs auto-discover the same SKILL — Claude reads `.claude/skills/` (symlinked to the canonical `.agents/skills/` file), Codex and Pi read `.agents/skills/` directly. Invoke with `/local-review` in Claude Code, `$local-review` (or `/skills` selector) in Codex, or `pi --skill local-review` / `/skill:local-review` in Pi. For a Codex-driven pass that mirrors the `codex-pr-review` GitHub action against your unpushed work (committed + uncommitted) before you push, use `/local-review-codex` (`.agents/skills/local-review-codex/`) — same `REVIEW.md` policy and `xhigh` reasoning, on `gpt-6-astra` rather than the action's `gpt-5.6-sol`; requires the `codex` CLI >= 0.153.4.
|
||||
- **Code review**: review the current PR or branch against the shared review policy in `REVIEW.md` (severity triage, public-surface checklist, AGENTS.md compliance, test-coverage assessment). The skill at `.agents/skills/local-review/SKILL.md` orchestrates it. All three CLIs auto-discover the same SKILL — Claude reads `.claude/skills/` (symlinked to the canonical `.agents/skills/` file), Codex and Pi read `.agents/skills/` directly. Invoke with `/local-review` in Claude Code, `$local-review` (or `/skills` selector) in Codex, or `pi --skill local-review` / `/skill:local-review` in Pi. For a Codex-driven pass that mirrors the `codex-pr-review` GitHub action against your unpushed work (committed + uncommitted) before you push, use `/local-review-codex` (`.agents/skills/local-review-codex/`) — same `REVIEW.md` policy, `gpt-6.1-sol` model and `xhigh` reasoning as the action; requires the `codex` CLI >= 0.159.3.
|
||||
- **Domain guides**: `.claude/skills/native-trigger/`
|
||||
- **Brand/UI guidelines**: `frontend/brand-guidelines.md`
|
||||
- **Domain vocabulary**: `CONTEXT.md` — the words this codebase uses for its own concepts (step, step setting, trigger step, …). Name things the way it does.
|
||||
- **CLI commands**: when adding/modifying/removing a command, subcommand, option, or description in `cli/src/commands/`, run `python system_prompts/generate.py` to refresh `system_prompts/auto-generated/` and `cli/src/guidance/skills.gen.ts`. The CLI docs the agents use to operate `wmill` are derived from the source — stale generated files give agents the wrong flags.
|
||||
- **AI guidance** (chat prompts and CLI skills): what the AI knows about Windmill itself — how flows, scripts, apps, pipelines and resources work — is written once, in `system_prompts/base/` and `system_prompts/languages/`, and `system_prompts/generate.py` builds both the chat's `$system_prompts` exports and the CLI's skills from it. Add or change such guidance there, never inline in one consumer: a sentence only one side should see goes in a `<!-- cli-only -->` / `<!-- chat-only -->` block, and a new file reaches both through the `TOPICS` table. The chat prompt builders under `frontend/src/lib/components/copilot/chat/` add only tool plumbing and runtime values, and shared markdown names no chat tool, since it reaches every chat mode. Rerun `generate.py` after editing; `system_prompts/README.md` has the details.
|
||||
- **Session recorder**: `frontend/src/lib/components/recording/` is also the recorder `wmill app dev --recording` serves, vendored into the CLI as `cli/src/commands/app/devRecorderBundle.gen.ts`. After changing `rawAppSnapshot.ts` or `rawAppRecording.svelte.ts`, run `bun run gen:dev-recorder` from `cli/` (`cli/test/dev_recorder_bundle_unit.test.ts` fails otherwise).
|
||||
- **Raw-app policy**: `frontend/src/lib/components/raw_apps/rawAppPolicy.ts` also derives the policy the server's raw-app deploy stores, vendored into the bundle job as `backend/windmill-api/src/apps_raw_policy.gen.js`. After changing it or anything it imports, run `bun run gen:app-policy` from `cli/` (`cli/test/app_policy_bundle_unit.test.ts` fails otherwise). It rides in the job rather than being read from the CLI the job runs because the images install `windmill-cli` unpinned, so an image can carry one older than its server.
|
||||
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## [1.820.0](https://github.com/windmill-labs/windmill/compare/v1.819.0...v1.820.0) (2026-09-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add pull_batch to claim jobs for many waiting workers at once ([#11350](https://github.com/windmill-labs/windmill/issues/11350)) ([9f40cdc](https://github.com/windmill-labs/windmill/commit/9f40cdca6258a9218770ebe1e7b89179e7d6667f))
|
||||
* add tree view to the schedules page ([#11360](https://github.com/windmill-labs/windmill/issues/11360)) ([e68ff0d](https://github.com/windmill-labs/windmill/commit/e68ff0d969edaacafc0fc5e246ac3945ae28109b))
|
||||
* **ai-agent:** add compaction memory that summarizes older context ([#10928](https://github.com/windmill-labs/windmill/issues/10928)) ([c1b59f7](https://github.com/windmill-labs/windmill/commit/c1b59f70dd0e1f4a7597d31cff031ce6c0e7d0ab))
|
||||
* detect and alert when a schedule skips occurrences ([#10917](https://github.com/windmill-labs/windmill/issues/10917)) ([97fa55b](https://github.com/windmill-labs/windmill/commit/97fa55b719a874067d1a81a1d680a42d1f1a4c98))
|
||||
* external instance cluster for data tables and Ducklake catalogs ([#11197](https://github.com/windmill-labs/windmill/issues/11197)) ([8741843](https://github.com/windmill-labs/windmill/commit/8741843d2e884783eed1c00bbc2293e526f3e80d))
|
||||
* refuse OIDC tokens to previews that impersonate a path their user cannot write ([#11396](https://github.com/windmill-labs/windmill/issues/11396)) ([d1260d7](https://github.com/windmill-labs/windmill/commit/d1260d7c5a3d132d4427079b794452309e03e28e))
|
||||
* restart perpetual runs on the version a deploy makes runnable ([#11200](https://github.com/windmill-labs/windmill/issues/11200)) ([cf5c49c](https://github.com/windmill-labs/windmill/commit/cf5c49c3dca2201f739fb872d68bcd6c0c7665f7))
|
||||
* run turns in several flow chat conversations at once ([#11202](https://github.com/windmill-labs/windmill/issues/11202)) ([f367eaf](https://github.com/windmill-labs/windmill/commit/f367eaf6d0258649ca0b3d47b1270af9a3fcd484))
|
||||
* share AI guidance with the CLI skills and lint flow groups ([#11397](https://github.com/windmill-labs/windmill/issues/11397)) ([ede4103](https://github.com/windmill-labs/windmill/commit/ede4103b00c6ada3b5ea8ba3e98bf6b78a791d63))
|
||||
* show native web search in the assistant settings modal ([#11394](https://github.com/windmill-labs/windmill/issues/11394)) ([235410c](https://github.com/windmill-labs/windmill/commit/235410c1e417bb64b432c96efed870cab74cb364))
|
||||
* stop cross-origin isolating the raw app editor ([#11411](https://github.com/windmill-labs/windmill/issues/11411)) ([6064aec](https://github.com/windmill-labs/windmill/commit/6064aecdecc49facde3870446b570abd8e8124b2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bound a resumed session fork's wait, keep its intent while in flight ([#11413](https://github.com/windmill-labs/windmill/issues/11413)) ([e6df8d7](https://github.com/windmill-labs/windmill/commit/e6df8d78d496cf9640bd5a1e9d7e8f4c2cc2adf7))
|
||||
* create workspace forks in the background, with progress ([#11406](https://github.com/windmill-labs/windmill/issues/11406)) ([054109d](https://github.com/windmill-labs/windmill/commit/054109d8559cc0a41ef361786c4d1d99eab9c502))
|
||||
* drop caller-supplied _MODULES at push for every job ([#11418](https://github.com/windmill-labs/windmill/issues/11418)) ([c5f59d6](https://github.com/windmill-labs/windmill/commit/c5f59d6ccc71d08fc34ede47e7a77b35a197b9db))
|
||||
* keep new-menu submenus open while crossing the gutter ([#11414](https://github.com/windmill-labs/windmill/issues/11414)) ([2f87bc6](https://github.com/windmill-labs/windmill/commit/2f87bc6a2d237382641ad3f0e7a8df36f0b74c15))
|
||||
* keep secret variable values hidden when toggling secret ([#11383](https://github.com/windmill-labs/windmill/issues/11383)) ([da5c58d](https://github.com/windmill-labs/windmill/commit/da5c58de2a0c297cfa81a601f9f543f46107e3f6))
|
||||
* refuse flow preview restarts from runs the caller cannot read ([#11407](https://github.com/windmill-labs/windmill/issues/11407)) ([c34abf7](https://github.com/windmill-labs/windmill/commit/c34abf7330e7f3fbbf5b68e325f0f0991c6c60d5))
|
||||
* report a worker's last job when it ran under one poll interval ([#11399](https://github.com/windmill-labs/windmill/issues/11399)) ([797147e](https://github.com/windmill-labs/windmill/commit/797147ea7a4a911d3c82e9ac9cdaf066ad2795d6))
|
||||
* run custom schedule handlers as the schedule ([#11398](https://github.com/windmill-labs/windmill/issues/11398)) ([bda00c6](https://github.com/windmill-labs/windmill/commit/bda00c6cad2de2ce0927651fe0e53653bdd20a96))
|
||||
* surface raw app build errors to the session AI ([#11415](https://github.com/windmill-labs/windmill/issues/11415)) ([50ffad5](https://github.com/windmill-labs/windmill/commit/50ffad52d765497fa3a561ebe8842b40fc8bd58e))
|
||||
* turn the default raw app into a feature tour ([#11417](https://github.com/windmill-labs/windmill/issues/11417)) ([83b8954](https://github.com/windmill-labs/windmill/commit/83b8954ae26f8d453d38e867e62366341cfe7729))
|
||||
|
||||
## [1.819.0](https://github.com/windmill-labs/windmill/compare/v1.818.0...v1.819.0) (2026-09-28)
|
||||
|
||||
|
||||
|
||||
+18
@@ -48,8 +48,26 @@ _Avoid_: thread, session (that names an AI session, a different thing), chat (th
|
||||
One question and the answer to it: the run the question started, the handle that stops it,
|
||||
and the rows it is writing. At most one per conversation, and the chat is held for its whole
|
||||
length — from the moment the question takes the chat, before it has a job, until it is ended.
|
||||
The server holds the same rule: a question sent to a conversation whose turn is still running
|
||||
is refused, whoever sends it. Several conversations of one flow can each have a turn running.
|
||||
_Avoid_: request, exchange, message round
|
||||
|
||||
**Running turn**:
|
||||
A turn whose run has not finished. Which conversations have one is the server's to say, so a
|
||||
chat opened after the turn started — a reload, another tab — still sees it and follows it.
|
||||
_Avoid_: busy, active, in flight
|
||||
|
||||
**Queued message**:
|
||||
A question typed into a conversation while its turn runs, sent when that turn ends answered.
|
||||
At most one per conversation; typing another adds to it. A turn that fails, or is stopped,
|
||||
hands it back to the composer instead of sending it.
|
||||
_Avoid_: pending message (a pending message is one already sent and not yet confirmed)
|
||||
|
||||
**Unread**:
|
||||
The answers that arrived in a conversation while it was not the one shown. Counted per open
|
||||
chat and forgotten on reload.
|
||||
_Avoid_: new messages, notifications
|
||||
|
||||
**Transcript**:
|
||||
The rows a conversation's chat holds. Not the conversation: it is the newest page plus
|
||||
whatever older pages the reader has scrolled back through, so a question it cannot answer
|
||||
|
||||
+3
-14
@@ -77,31 +77,20 @@ Public CLI surface:
|
||||
- `--verbose`: stream assistant output for frontend runs
|
||||
- `--skip-judge`: skip LLM judge scoring for the run
|
||||
- `--execution-only`: only require the model/proxy/frontend loop to complete; skip validators, tool expectations, backend artifact validation, and judge scoring
|
||||
- `--reasoning <effort>`: reasoning effort for frontend modes (`off`, `low`, `medium`, `high`, `max`, …); without it the product's default applies (`high` on models that can reason). The effort is appended to the recorded model label (`anthropic:claude-sonnet-5-5@max`)
|
||||
- `--record`: append a compact tracked summary line to `ai_evals/history/<mode>.jsonl` for full-suite runs only
|
||||
- `--backend-validation <mode>`: optional backend smoke validation (`off` or `preview`) for `script` and `flow` evals
|
||||
|
||||
## Models
|
||||
|
||||
Use `bun run cli -- models` to see the current aliases.
|
||||
|
||||
Today:
|
||||
|
||||
- `haiku`
|
||||
- `sonnet`
|
||||
- `opus`
|
||||
- `4o`
|
||||
- `gpt-5.5`
|
||||
- `gemini-3-flash-preview`
|
||||
- `gemini-3.1-pro-preview`
|
||||
- `deepseek-v4-flash`
|
||||
- `deepseek-v4-pro`
|
||||
Use `bun run cli -- models` to see the current aliases; `core/models.ts` is the list.
|
||||
|
||||
Notes:
|
||||
|
||||
- the command also prints accepted alias spellings such as `gpt-4o`, `gpt-55`, `claude-opus-4.6`, and `claude-haiku-4.5`
|
||||
- frontend modes (`flow`, `script`, `app`, `global`) can use Anthropic, OpenAI, Gemini, and DeepSeek-backed aliases
|
||||
- `cli` mode always uses the Anthropic agent SDK, so only Anthropic aliases are valid there
|
||||
- the judge model is separate and currently defaults to `claude-sonnet-4-6`; use `--skip-judge` for deterministic-only runs
|
||||
- the judge model is separate and currently defaults to `claude-sonnet-5-5`; use `--skip-judge` for deterministic-only runs
|
||||
|
||||
## Case Format
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { runEval } from "../shared";
|
||||
import type { ModeRunContext } from "../../../../core/types";
|
||||
import type { TokenUsage, ToolCallDetail } from "../shared/types";
|
||||
import type { WindmillBackendSettings } from "../../../../core/windmillBackendSettings";
|
||||
import { evalReasoningEffort } from "../shared/providerConfig";
|
||||
|
||||
export interface ScriptEvalResult {
|
||||
success: boolean;
|
||||
@@ -41,14 +42,15 @@ export interface ScriptEvalOptions {
|
||||
function resolveModelProvider(
|
||||
model: string,
|
||||
provider?: AIProvider,
|
||||
): AIProviderModel {
|
||||
): AIProviderModel & { reasoning?: string } {
|
||||
const reasoning = evalReasoningEffort();
|
||||
if (provider) {
|
||||
return { provider, model };
|
||||
return { provider, model, reasoning };
|
||||
}
|
||||
if (model.startsWith("claude")) {
|
||||
return { provider: "anthropic", model };
|
||||
return { provider: "anthropic", model, reasoning };
|
||||
}
|
||||
return { provider: "openai", model };
|
||||
return { provider: "openai", model, reasoning };
|
||||
}
|
||||
|
||||
export async function runScriptEval(
|
||||
|
||||
@@ -12,6 +12,12 @@ export interface EvalClients {
|
||||
export interface ResolvedEvalModelProvider {
|
||||
provider: FrontendEvalProvider;
|
||||
model: string;
|
||||
reasoning?: string;
|
||||
}
|
||||
|
||||
/** `run --reasoning` hands the effort to the frontend runtime through the environment. */
|
||||
export function evalReasoningEffort(): string | undefined {
|
||||
return process.env.WMILL_AI_EVAL_REASONING || undefined;
|
||||
}
|
||||
|
||||
export interface WindmillAiProxyClientConfig {
|
||||
@@ -73,6 +79,13 @@ export function createEvalClients(input: {
|
||||
export function resolveEvalModelProvider(
|
||||
model: string,
|
||||
provider?: FrontendEvalProvider,
|
||||
): ResolvedEvalModelProvider {
|
||||
return { ...resolveProvider(model, provider), reasoning: evalReasoningEffort() };
|
||||
}
|
||||
|
||||
function resolveProvider(
|
||||
model: string,
|
||||
provider?: FrontendEvalProvider,
|
||||
): ResolvedEvalModelProvider {
|
||||
if (provider) {
|
||||
return { provider, model };
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import confluenceFixture from '../../fixtures/frontend/global/hub/confluence.json'
|
||||
import servicenowFixture from '../../fixtures/frontend/global/hub/servicenow.json'
|
||||
import outreachFixture from '../../fixtures/frontend/global/hub/outreach.json'
|
||||
import type {
|
||||
AppWithLastVersion,
|
||||
CompletedJob,
|
||||
@@ -250,9 +253,7 @@ export function listBenchmarkApps(workspace: string): ListableApp[] | null {
|
||||
}
|
||||
|
||||
export function getBenchmarkAppByPath(workspace: string, path: string): AppWithLastVersion | null {
|
||||
const app = benchmarkWorkspaceRunnables
|
||||
.get(workspace)
|
||||
?.apps?.find((entry) => entry.path === path)
|
||||
const app = benchmarkWorkspaceRunnables.get(workspace)?.apps?.find((entry) => entry.path === path)
|
||||
|
||||
return app ? buildBenchmarkApp(app) : null
|
||||
}
|
||||
@@ -448,7 +449,10 @@ export function createBenchmarkCompletedJob(input: {
|
||||
labels: input.label ? [input.label] : undefined
|
||||
}
|
||||
|
||||
benchmarkJobs.set(benchmarkJobKey(input.workspace, jobId), { workspace: input.workspace, job })
|
||||
benchmarkJobs.set(benchmarkJobKey(input.workspace, jobId), {
|
||||
workspace: input.workspace,
|
||||
job
|
||||
})
|
||||
return jobId
|
||||
}
|
||||
|
||||
@@ -532,7 +536,13 @@ export function getBenchmarkFlowAllResults(workspace: string, jobId: string) {
|
||||
*/
|
||||
const benchmarkDrafts = new Map<
|
||||
string,
|
||||
{ workspace: string; kind: UserDraftItemKind; path: string; value: unknown; createdAt: string }
|
||||
{
|
||||
workspace: string
|
||||
kind: UserDraftItemKind
|
||||
path: string
|
||||
value: unknown
|
||||
createdAt: string
|
||||
}
|
||||
>()
|
||||
|
||||
// Counter-based timestamps: deterministic run-to-run (same event order → same
|
||||
@@ -614,7 +624,9 @@ export function getBenchmarkDraftForUser(input: {
|
||||
}): GetDraftForUserResponse {
|
||||
const entry = benchmarkDrafts.get(benchmarkDraftKey(input.workspace, input.kind, input.path))
|
||||
if (!entry) {
|
||||
throw Object.assign(new Error(`no draft for "${input.path}"`), { status: 404 })
|
||||
throw Object.assign(new Error(`no draft for "${input.path}"`), {
|
||||
status: 404
|
||||
})
|
||||
}
|
||||
return { value: entry.value, created_at: entry.createdAt }
|
||||
}
|
||||
@@ -635,7 +647,11 @@ export function getBenchmarkOwnDraft(input: {
|
||||
|
||||
/** 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 {
|
||||
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))
|
||||
@@ -762,10 +778,11 @@ export function runBenchmarkDatatableSql(input: {
|
||||
* shape `pollJobResult` consumes. The job is created synchronously before
|
||||
* polling, so it is always present and completed.
|
||||
*/
|
||||
export function getBenchmarkCompletedJobResultMaybe(input: {
|
||||
workspace: string
|
||||
id: string
|
||||
}): { success: boolean; completed: boolean; result: unknown } {
|
||||
export function getBenchmarkCompletedJobResultMaybe(input: { workspace: string; id: string }): {
|
||||
success: boolean
|
||||
completed: boolean
|
||||
result: unknown
|
||||
} {
|
||||
const job = getBenchmarkCompletedJob(input.workspace, input.id)
|
||||
if (!job) {
|
||||
throw new Error(`Job "${input.id}" not found in benchmark workspace`)
|
||||
@@ -896,7 +913,9 @@ export function previewBenchmarkSchedule(input: {
|
||||
}): Record<string, unknown> {
|
||||
const schedule = input.requestBody?.schedule
|
||||
if (typeof schedule !== 'string' || schedule.trim().split(/\s+/).length !== 6) {
|
||||
throw new Error(`schedule must use a six-field cron expression, got ${JSON.stringify(schedule)}`)
|
||||
throw new Error(
|
||||
`schedule must use a six-field cron expression, got ${JSON.stringify(schedule)}`
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -928,7 +947,9 @@ export function createBenchmarkHttpTrigger(input: {
|
||||
typeof input.requestBody.route_path === 'string' &&
|
||||
input.requestBody.route_path.startsWith('/')
|
||||
) {
|
||||
throw new Error(`HTTP trigger route_path must not start with /, got "${input.requestBody.route_path}"`)
|
||||
throw new Error(
|
||||
`HTTP trigger route_path must not start with /, got "${input.requestBody.route_path}"`
|
||||
)
|
||||
}
|
||||
return {
|
||||
path: input.requestBody.path,
|
||||
@@ -1024,7 +1045,39 @@ function buildBenchmarkApp(app: BenchmarkWorkspaceApp): AppWithLastVersion {
|
||||
* hub tools throw and no case can exercise hub reuse. Serving fixtures rather
|
||||
* than the live hub also keeps assertions on script content stable as the real
|
||||
* hub republishes new versions. */
|
||||
const BENCHMARK_INTEGRATION_META_PATH = /^\/api\/integrations\/hub\/([^/]+)\/meta$/
|
||||
|
||||
/** One integration lifted verbatim from the content repo — its shipped scripts, its
|
||||
* authored meta.json and its resource type. Hand-written fixtures make both routes to
|
||||
* an integration's conventions look equally cheap; a real one is the only way to tell
|
||||
* whether reading the metadata beats reading a script. */
|
||||
const REAL_HUB_INTEGRATIONS = [
|
||||
confluenceFixture,
|
||||
servicenowFixture,
|
||||
outreachFixture
|
||||
] as unknown as Array<{
|
||||
app: string
|
||||
display_name: string
|
||||
description: string
|
||||
docs_url: string
|
||||
curated: boolean | null
|
||||
meta: unknown
|
||||
resource_type: { name: string; description: string; schema: unknown }
|
||||
scripts: Array<{
|
||||
version_id: number
|
||||
app: string
|
||||
summary: string
|
||||
description: string
|
||||
terms: string
|
||||
kind: string
|
||||
language: string
|
||||
content: string
|
||||
schema: unknown
|
||||
}>
|
||||
}>
|
||||
|
||||
const BENCHMARK_HUB_SCRIPTS = [
|
||||
...REAL_HUB_INTEGRATIONS.flatMap((integration) => integration.scripts),
|
||||
{
|
||||
version_id: 22235,
|
||||
app: 'holded',
|
||||
@@ -1112,6 +1165,87 @@ export async function main(discord_webhook: DiscordWebhook, message: string) {
|
||||
message: { type: 'string' }
|
||||
}
|
||||
}
|
||||
},
|
||||
// Baremetrics carries no annotation script, on purpose: it stands for an
|
||||
// integration whose conventions (an `apiKey` resource, bearer auth, the /v1
|
||||
// base) are only learnable by reading a script that does something else.
|
||||
{
|
||||
version_id: 8995,
|
||||
app: 'baremetrics',
|
||||
summary: 'List Sources',
|
||||
description: 'List all the sources attached to a Baremetrics account.',
|
||||
terms: 'baremetrics sources list revenue metrics',
|
||||
language: 'bunnative',
|
||||
content: `//native
|
||||
type Baremetrics = {
|
||||
apiKey: string;
|
||||
};
|
||||
export async function main(resource: Baremetrics) {
|
||||
const response = await fetch("https://api.baremetrics.com/v1/sources", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
Authorization: \`Bearer \${resource.apiKey}\`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(\`\${response.status} \${await response.text()}\`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
`,
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['resource'],
|
||||
properties: {
|
||||
resource: { type: 'object', format: 'resource-baremetrics' }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
version_id: 8996,
|
||||
app: 'baremetrics',
|
||||
summary: 'Create Customer',
|
||||
description: 'Create a customer on a Baremetrics source.',
|
||||
terms: 'baremetrics customer create source',
|
||||
language: 'bunnative',
|
||||
content: `//native
|
||||
type Baremetrics = {
|
||||
apiKey: string;
|
||||
};
|
||||
export async function main(
|
||||
resource: Baremetrics,
|
||||
sourceId: string,
|
||||
body: { oid: string; name?: string; email?: string },
|
||||
) {
|
||||
const response = await fetch(
|
||||
\`https://api.baremetrics.com/v1/\${sourceId}/customers\`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
Authorization: \`Bearer \${resource.apiKey}\`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(\`\${response.status} \${await response.text()}\`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
`,
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['resource', 'sourceId', 'body'],
|
||||
properties: {
|
||||
resource: { type: 'object', format: 'resource-baremetrics' },
|
||||
sourceId: { type: 'string' },
|
||||
body: { type: 'object' }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1121,20 +1255,26 @@ export async function main(discord_webhook: DiscordWebhook, message: string) {
|
||||
* integration, or overlapping on three meaningful words. A looser bar answers
|
||||
* "send a Slack message" with the Discord fixture, handing an unrelated case a
|
||||
* plausible-looking wrong integration. */
|
||||
function searchBenchmarkHubScripts(text: string) {
|
||||
function searchBenchmarkHubScripts(text: string, app: string | null, kind: string | null) {
|
||||
const tokens = new Set(
|
||||
text
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/)
|
||||
.filter((token) => token.length > 2)
|
||||
)
|
||||
return BENCHMARK_HUB_SCRIPTS.map((script) => {
|
||||
const words = new Set(
|
||||
`${script.app} ${script.summary} ${script.terms}`.toLowerCase().split(/[^a-z0-9]+/)
|
||||
)
|
||||
const score = [...tokens].filter((token) => words.has(token)).length
|
||||
return { script, score, namesApp: tokens.has(script.app) }
|
||||
})
|
||||
// The real endpoint filters by app and kind before ranking, so honour both here —
|
||||
// otherwise a narrowed search silently returns other integrations' scripts, and a
|
||||
// trigger answers a search production would never have shown it to.
|
||||
return BENCHMARK_HUB_SCRIPTS.filter(
|
||||
(script) => (!app || script.app === app) && (!kind || (script.kind ?? 'script') === kind)
|
||||
)
|
||||
.map((script) => {
|
||||
const words = new Set(
|
||||
`${script.app} ${script.summary} ${script.terms}`.toLowerCase().split(/[^a-z0-9]+/)
|
||||
)
|
||||
const score = [...tokens].filter((token) => words.has(token)).length
|
||||
return { script, score, namesApp: tokens.has(script.app) }
|
||||
})
|
||||
.filter((entry) => entry.namesApp || entry.score >= 3)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map(({ script }, index) => ({
|
||||
@@ -1142,12 +1282,104 @@ function searchBenchmarkHubScripts(text: string) {
|
||||
id: script.version_id,
|
||||
version_id: script.version_id,
|
||||
summary: script.summary,
|
||||
description: script.description ?? null,
|
||||
app: script.app,
|
||||
kind: 'script',
|
||||
kind: script.kind ?? 'script',
|
||||
score: 1 - index * 0.01
|
||||
}))
|
||||
}
|
||||
|
||||
/** Listing an integration is unranked and description-bearing, matching the hub's
|
||||
* top-scripts endpoint — that asymmetry with the semantic search is the whole
|
||||
* reason the chat browses by app when no script matches the task. */
|
||||
function listBenchmarkHubScriptsByApp(app: string | null, kind: string | null) {
|
||||
return BENCHMARK_HUB_SCRIPTS.filter(
|
||||
(script) => (!app || script.app === app) && (!kind || (script.kind ?? 'script') === kind)
|
||||
).map((script) => ({
|
||||
id: script.version_id,
|
||||
ask_id: script.version_id,
|
||||
version_id: script.version_id,
|
||||
summary: script.summary,
|
||||
description: script.description ?? null,
|
||||
app: script.app,
|
||||
kind: script.kind ?? 'script',
|
||||
views: 0,
|
||||
votes: 0
|
||||
}))
|
||||
}
|
||||
|
||||
/** What `/integrations/hub/<app>/meta` serves. Carries the integration's conventions
|
||||
* and none of the endpoints a case asks the model to write, so the tool shortens the
|
||||
* path to that knowledge without supplying answers. */
|
||||
/** The integrations a case writes against, none of which the content repo documents —
|
||||
* the same majority shape as the live hub, where 18 of ~216 carry a meta.json. Their
|
||||
* conventions live in their shipped scripts, which is what these cases exercise. */
|
||||
const BENCHMARK_HUB_INTEGRATION_META: Record<
|
||||
string,
|
||||
{
|
||||
display_name: string
|
||||
description: string
|
||||
docs_url: string
|
||||
curated: boolean | null
|
||||
meta: unknown
|
||||
}
|
||||
> = {
|
||||
baremetrics: {
|
||||
display_name: 'Baremetrics',
|
||||
description: 'Subscription analytics for recurring-revenue businesses.',
|
||||
docs_url: 'https://developers.baremetrics.com/reference',
|
||||
curated: null,
|
||||
meta: null
|
||||
},
|
||||
holded: {
|
||||
display_name: 'Holded',
|
||||
description: 'Invoicing, accounting and CRM for small businesses.',
|
||||
docs_url: 'https://developers.holded.com/reference',
|
||||
curated: null,
|
||||
meta: null
|
||||
}
|
||||
}
|
||||
|
||||
/** Mirrors the hub's own derivation: hosts seen in the shipped scripts, whether they
|
||||
* call the provider directly, and how many there are of each kind. */
|
||||
function benchmarkDerivedFacts(app: string) {
|
||||
const scripts = BENCHMARK_HUB_SCRIPTS.filter((script) => script.app === app)
|
||||
const hosts = new Map<string, number>()
|
||||
const languages: Record<string, number> = {}
|
||||
for (const script of scripts) {
|
||||
languages[script.language] = (languages[script.language] ?? 0) + 1
|
||||
for (const match of script.content.matchAll(/https?:\/\/([a-zA-Z0-9._-]+)/g)) {
|
||||
hosts.set(match[1], (hosts.get(match[1]) ?? 0) + 1)
|
||||
}
|
||||
}
|
||||
return {
|
||||
api_hosts: [...hosts.entries()]
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([host, count]) => ({ host, count })),
|
||||
style: 'fetch',
|
||||
languages,
|
||||
script_counts: {
|
||||
total: scripts.length,
|
||||
by_kind: scripts.reduce<Record<string, number>>((acc, script) => {
|
||||
const kind = script.kind ?? 'script'
|
||||
acc[kind] = (acc[kind] ?? 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
top_scripts: scripts.map((script) => ({
|
||||
path: `hub/${script.version_id}/${script.app}/${script.summary.toLowerCase().replaceAll(/\s+/g, '_')}`,
|
||||
ask_id: script.version_id,
|
||||
version_id: script.version_id,
|
||||
summary: script.summary,
|
||||
description: script.description ?? null,
|
||||
kind: script.kind ?? 'script',
|
||||
language: script.language,
|
||||
views: 0,
|
||||
votes: 0
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/** The hub keys a script by its version id; the app and slug segments that
|
||||
* follow are descriptive, so match on the id exactly as the real hub does. */
|
||||
function getBenchmarkHubScript(path: string) {
|
||||
@@ -1190,6 +1422,9 @@ export function hasBenchmarkApiHandler(url: string): boolean {
|
||||
return (
|
||||
path === '/api/workers/list' ||
|
||||
path === '/api/embeddings/query_hub_scripts' ||
|
||||
path === '/api/scripts/hub/top' ||
|
||||
path === '/api/integrations/hub/list' ||
|
||||
BENCHMARK_INTEGRATION_META_PATH.test(path) ||
|
||||
path.startsWith('/api/scripts/hub/get_full/') ||
|
||||
BENCHMARK_AI_MODELS_PATH.test(path)
|
||||
)
|
||||
@@ -1213,8 +1448,81 @@ export function handleBenchmarkApiFetch(url: string, init?: RequestInit): Respon
|
||||
return Response.json({ data: (seed?.models ?? []).map((id) => ({ id })) })
|
||||
}
|
||||
if (path === '/api/embeddings/query_hub_scripts') {
|
||||
const text = new URLSearchParams(url.split('?')[1] ?? '').get('text') ?? ''
|
||||
return Response.json(searchBenchmarkHubScripts(text))
|
||||
const params = new URLSearchParams(url.split('?')[1] ?? '')
|
||||
return Response.json(
|
||||
searchBenchmarkHubScripts(params.get('text') ?? '', params.get('app'), params.get('kind'))
|
||||
)
|
||||
}
|
||||
if (path === '/api/scripts/hub/top') {
|
||||
const params = new URLSearchParams(url.split('?')[1] ?? '')
|
||||
return Response.json({
|
||||
asks: listBenchmarkHubScriptsByApp(params.get('app'), params.get('kind'))
|
||||
})
|
||||
}
|
||||
const integrationMeta = BENCHMARK_INTEGRATION_META_PATH.exec(path)
|
||||
if (integrationMeta) {
|
||||
const app = decodeURIComponent(integrationMeta[1])
|
||||
const real = REAL_HUB_INTEGRATIONS.find((integration) => integration.app === app)
|
||||
if (real) {
|
||||
return Response.json({
|
||||
app,
|
||||
display_name: real.display_name,
|
||||
description: real.description,
|
||||
docs_url: real.docs_url,
|
||||
curated: real.curated,
|
||||
metadata_source: real.meta ? 'curated' : 'derived',
|
||||
meta: real.meta,
|
||||
meta_updated_at: null,
|
||||
derived: benchmarkDerivedFacts(app),
|
||||
resource_types: [{ id: 1, ...real.resource_type }]
|
||||
})
|
||||
}
|
||||
const entry = BENCHMARK_HUB_INTEGRATION_META[app]
|
||||
if (!entry) {
|
||||
return Response.json({ error: 'integration not found' }, { status: 404 })
|
||||
}
|
||||
return Response.json({
|
||||
app,
|
||||
display_name: entry.display_name,
|
||||
description: entry.description,
|
||||
docs_url: entry.docs_url,
|
||||
curated: entry.curated,
|
||||
metadata_source: entry.meta ? 'curated' : 'derived',
|
||||
meta: entry.meta,
|
||||
meta_updated_at: null,
|
||||
derived: benchmarkDerivedFacts(app),
|
||||
resource_types: [
|
||||
{
|
||||
id: 1,
|
||||
name: app,
|
||||
description: `${entry.display_name} credentials`,
|
||||
schema: {
|
||||
type: 'object',
|
||||
required: ['apiKey'],
|
||||
properties: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
description: `${entry.display_name} API key`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
if (path === '/api/integrations/hub/list') {
|
||||
const apps = [...new Set(BENCHMARK_HUB_SCRIPTS.map((script) => script.app))].sort()
|
||||
// Read off both fixtures the metadata endpoint serves from, or the list would
|
||||
// call an integration undocumented and then hand back its authored notes.
|
||||
const documented = new Set([
|
||||
...REAL_HUB_INTEGRATIONS.filter((integration) => integration.meta).map(
|
||||
(integration) => integration.app
|
||||
),
|
||||
...Object.entries(BENCHMARK_HUB_INTEGRATION_META)
|
||||
.filter(([, entry]) => entry.meta)
|
||||
.map(([app]) => app)
|
||||
])
|
||||
return Response.json(apps.map((name) => ({ name, documented: documented.has(name) })))
|
||||
}
|
||||
if (path.startsWith('/api/scripts/hub/get_full/')) {
|
||||
const script = getBenchmarkHubScript(path)
|
||||
|
||||
@@ -16,3 +16,21 @@ describe('benchmark API fetch handlers', () => {
|
||||
expect(body[0]).toMatchObject({ worker: expect.any(String), worker_group: expect.any(String) })
|
||||
})
|
||||
})
|
||||
|
||||
// A benchmark whose list calls an integration undocumented while its metadata endpoint
|
||||
// hands back authored notes teaches the model the flag means nothing.
|
||||
describe('benchmark hub integration list', () => {
|
||||
it('flags exactly the integrations whose metadata carries authored notes', async () => {
|
||||
const listed = (await handleBenchmarkApiFetch('/api/integrations/hub/list').json()) as Array<{
|
||||
name: string
|
||||
documented: boolean
|
||||
}>
|
||||
expect(listed.length).toBeGreaterThan(0)
|
||||
|
||||
for (const { name, documented } of listed) {
|
||||
const res = handleBenchmarkApiFetch(`/api/integrations/hub/${name}/meta`)
|
||||
const authored = res.status === 200 && !!((await res.json()) as { meta?: unknown }).meta
|
||||
expect(authored).toBe(documented)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { expect, it, vi } from 'vitest'
|
||||
// @ts-ignore - Node.js fs/promises
|
||||
import { mkdir, writeFile } from 'fs/promises'
|
||||
import { mkdir, readFile, writeFile } from 'fs/promises'
|
||||
// @ts-ignore - Node.js path
|
||||
import { dirname, resolve } from 'path'
|
||||
// @ts-ignore - Node.js url
|
||||
import { fileURLToPath } from 'url'
|
||||
import { handleBenchmarkApiFetch, hasBenchmarkApiHandler } from './mockBackend'
|
||||
|
||||
const FRONTEND_DIR = fileURLToPath(new URL('../../../frontend/', import.meta.url))
|
||||
|
||||
// Some tools reach the backend by relative fetch('/api/...'), which has 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
|
||||
@@ -24,9 +28,30 @@ globalThis.fetch = (async (input: unknown, init?: RequestInit) => {
|
||||
if (typeof url === 'string' && hasBenchmarkApiHandler(url)) {
|
||||
return handleBenchmarkApiFetch(url, init)
|
||||
}
|
||||
// The parsers behind inferArgs load their wasm from a vite `?url` path, which only a dev
|
||||
// server serves. Unserved, every script schema infers as empty, and the tools then tell the
|
||||
// model its arguments are undeclared, so it rewrites a correct script until it runs out of turns.
|
||||
const wasmPath = typeof url === 'string' ? wasmFilePath(url) : undefined
|
||||
if (wasmPath) {
|
||||
return new Response(await readFile(wasmPath), {
|
||||
headers: { 'Content-Type': 'application/wasm' }
|
||||
})
|
||||
}
|
||||
return ORIGINAL_FETCH(input as Parameters<typeof fetch>[0], init)
|
||||
}) as typeof fetch
|
||||
|
||||
function wasmFilePath(url: string): string | undefined {
|
||||
const raw = url.split(/[?#]/)[0]
|
||||
if (!raw.endsWith('.wasm')) return undefined
|
||||
const pathname = decodeURIComponent(raw)
|
||||
// Located past whatever base path the config prefixes.
|
||||
const fs = pathname.indexOf('/@fs/')
|
||||
if (fs !== -1) return pathname.slice(fs + '/@fs'.length)
|
||||
const modules = pathname.indexOf('/node_modules/')
|
||||
if (modules !== -1) return resolve(FRONTEND_DIR, `.${pathname.slice(modules)}`)
|
||||
return undefined
|
||||
}
|
||||
|
||||
vi.mock('monaco-editor', () => ({
|
||||
editor: {},
|
||||
languages: {},
|
||||
@@ -640,6 +665,13 @@ benchmarkIt(
|
||||
async () => {
|
||||
const { resetBenchmarkMockBackend } = await import('./mockBackend')
|
||||
resetBenchmarkMockBackend()
|
||||
// The tools swallow inference failures, so a wasm the fetch stub stops serving would
|
||||
// only show up as empty schemas the model chases, never as an error.
|
||||
const { inferArgs } = await import('$lib/infer')
|
||||
const { emptySchema } = await import('$lib/utils')
|
||||
const probe = emptySchema()
|
||||
await inferArgs('bun', 'export async function main(name: string) {}', probe)
|
||||
expect(Object.keys(probe.properties)).toEqual(['name'])
|
||||
const { runFrontendBenchmarkFromEnv } = await import('./benchmarkRunner')
|
||||
try {
|
||||
const payload = await runFrontendBenchmarkFromEnv()
|
||||
|
||||
+29
-91
@@ -5,8 +5,8 @@
|
||||
"": {
|
||||
"name": "windmill-ai-evals",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.25",
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.284",
|
||||
"@anthropic-ai/sdk": "^0.129.0",
|
||||
"commander": "^14.0.3",
|
||||
"openai": "^6.9.1",
|
||||
"yaml": "^2.8.3",
|
||||
@@ -18,66 +18,44 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.87", "", { "dependencies": { "@anthropic-ai/sdk": "^0.74.0", "@modelcontextprotocol/sdk": "^1.27.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.34.2", "@img/sharp-darwin-x64": "^0.34.2", "@img/sharp-linux-arm": "^0.34.2", "@img/sharp-linux-arm64": "^0.34.2", "@img/sharp-linux-x64": "^0.34.2", "@img/sharp-linuxmusl-arm64": "^0.34.2", "@img/sharp-linuxmusl-x64": "^0.34.2", "@img/sharp-win32-arm64": "^0.34.2", "@img/sharp-win32-x64": "^0.34.2" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-WWmgBPxPhBOvNT0ujI8vPTI2lK+w5YEkEZ/y1mH0EDkK/0kBnxVJNhCtG5vnueiAViwLoUOFn66pbkDiivijdA=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.284", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.284", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.284", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.284", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.284", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.284", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.284", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.284", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.284" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-NSoJwEq6nFSf8dtaacYx37QdGgqApI3eHFUlxclMLYi8irb6ZJwEaUnPnLUCyAyg9W/tMkgZC0GXWLRCc01I0w=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.39.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.284", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gKY9MUjY83398uCiPLHsd87kyzu7agIM7ApqWpJkpSINep6hxx4rNoR8bUbNWg49Aoe/PW/DJkQByAQuNzR9rg=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.284", "", { "os": "darwin", "cpu": "x64" }, "sha512-P+q6Z7sKeYz99uE7RJc4au1IK+4SiWe69pyHY3QXMnjZq8hSFU/5Mo8iSurLk9jRlihsMltV1rLA5naouYyCAQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.284", "", { "os": "linux", "cpu": "arm64" }, "sha512-LDpuYDaz+pCdG29iy1pw4P1D2YVtpZOb4rWOPxxSpg2fFyUeQ5WrfnuszOxhDWpNh1k1ekDiKm3A9OzGcrkdtA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.284", "", { "os": "linux", "cpu": "arm64" }, "sha512-U3/uAv1TS8sMWsgWQSxYbdND5AaRXXpP5RxHU1oYC5ZizlZE8/xAVum9dj1QX3rVZJg8EzsqZveq/AkEv5ABfQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.284", "", { "os": "linux", "cpu": "x64" }, "sha512-yGytBCCwJvWeg1FzFpgnLM9BOM2vKVExtv6pMJHMtF82J5yhKHcxodRaYlQVd/HzJJ2nruKb9EnrhRBZ//4yRw=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.284", "", { "os": "linux", "cpu": "x64" }, "sha512-4x0Q8CFkCNbeENE7pRhuUmkwh/sQfTq1yWi4ZE56stve9pgoKzu6ZDa1SEtvK1YIooGE/qM5rVS8xFLE1gbQ0w=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.284", "", { "os": "win32", "cpu": "arm64" }, "sha512-zOXkdPHhElyxyFJ6eY5R+YGUswqBDZ+ZHNaRmQGJ28RkQYgwG5imNy/nfFR+/CPGlMnH9ZzbGc2rKYApm1K4Tg=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.284", "", { "os": "win32", "cpu": "x64" }, "sha512-VGaFRDCOPloj5IjvJTyy5JsSl9sE2HR3W+A6f/cjgh23/7Y5vK7/ka+1JQD+AkoGO9tbHqP2O6god3IBrLvOaw=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.129.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1", "standardwebhooks": "^1.0.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-MH7LB20kNpGLUpPTe2OG5XvL/KhX8HUO9XyBaFjgFk6TLS4Xjt0VPIuMKywe7POKKPslX+QxSlVok9e+8kG5Nw=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.12", "", { "peerDependencies": { "hono": "^4" } }, "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
|
||||
|
||||
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
|
||||
|
||||
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
|
||||
|
||||
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||
|
||||
"@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||
|
||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||
@@ -88,8 +66,6 @@
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
|
||||
@@ -106,8 +82,6 @@
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
@@ -122,14 +96,10 @@
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||
|
||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||
|
||||
"eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
|
||||
@@ -140,16 +110,12 @@
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||
|
||||
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
|
||||
|
||||
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
@@ -164,16 +130,12 @@
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
@@ -208,10 +170,6 @@
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||
|
||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
@@ -262,30 +220,24 @@
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"standardwebhooks": ["standardwebhooks@1.1.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
|
||||
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||
|
||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
@@ -295,19 +247,5 @@
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk/@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.74.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-srbJV7JKsc5cQ6eVuFzjZO7UR3xEPJqPamHFIe29bs38Ij2IripoAhC0S5NslNbaFUYqBKypmmpzMTpqfHEUDw=="],
|
||||
|
||||
"@types/node-fetch/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"bun-types/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||
|
||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"@types/node-fetch/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"bun-types/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
forbiddenSkills:
|
||||
- write-script-python3
|
||||
- write-flow
|
||||
orderedAssistantMentions:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
orderedProposedCommands:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
forbiddenExecutedCommands:
|
||||
- ^wmill generate-metadata
|
||||
- ^wmill sync push
|
||||
@@ -38,12 +32,6 @@
|
||||
- write-flow
|
||||
forbiddenSkills:
|
||||
- write-script-python3
|
||||
orderedAssistantMentions:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
orderedProposedCommands:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
forbiddenExecutedCommands:
|
||||
- ^wmill generate-metadata
|
||||
- ^wmill sync push
|
||||
@@ -65,12 +53,6 @@
|
||||
forbiddenSkills:
|
||||
- write-script-bun
|
||||
- write-flow
|
||||
orderedAssistantMentions:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
orderedProposedCommands:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
forbiddenExecutedCommands:
|
||||
- ^wmill generate-metadata
|
||||
- ^wmill sync push
|
||||
@@ -114,12 +96,6 @@
|
||||
- write-flow
|
||||
requiredSkillsBeforeFirstMutation:
|
||||
- write-flow
|
||||
orderedAssistantMentions:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
orderedProposedCommands:
|
||||
- wmill generate-metadata
|
||||
- wmill sync push
|
||||
forbiddenExecutedCommands:
|
||||
- ^wmill generate-metadata
|
||||
- ^wmill sync push
|
||||
|
||||
@@ -2312,6 +2312,105 @@
|
||||
- email
|
||||
skipJudge: true
|
||||
|
||||
# `List Pages` takes a cursor without ever following one, so where the next cursor
|
||||
# comes from (`_links.next`) is stated in the authored metadata and in no script body.
|
||||
# Paginating is therefore the one task where reading scripts is not an equal route.
|
||||
- id: global-hub4-confluence-paginate-all-pages
|
||||
prompt: |-
|
||||
I need a script that returns every page in one of my Confluence spaces, not just the
|
||||
first batch — it should keep fetching until there are no more.
|
||||
Save it as a draft at `f/evals/global/confluence_all_pages` and leave it as an AI
|
||||
draft; do not deploy it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 14
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/confluence_all_pages
|
||||
valueIncludes:
|
||||
- /wiki/
|
||||
- Basic
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
# Either route to the integration's conventions is fine; the checklist is where the
|
||||
# difference between them has to show up.
|
||||
requiredToolsAnyOf:
|
||||
- - search_hub_scripts
|
||||
- get_hub_integration
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- the script loops until the API stops returning a next link, rather than making a single request
|
||||
- the next cursor is read from the response's `_links.next` or the Link header, not from an invented field such as `next_cursor`, `nextPageToken` or `offset`
|
||||
- it authenticates with HTTP Basic built from the Confluence resource's email and API token rather than a bearer token
|
||||
- the script takes a Confluence resource as an input parameter rather than loose credential strings
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# Unlike the Confluence case, both facts this needs — the {"result"} envelope and
|
||||
# PATCH-not-PUT — are visible in ServiceNow's own shipped scripts. That makes it the
|
||||
# control for whether the metadata earns its place when the scripts already answer.
|
||||
- id: global-hub5-servicenow-assign-incident
|
||||
prompt: |-
|
||||
Write me a script that assigns an existing ServiceNow incident to a person, looking
|
||||
that person up by their email address.
|
||||
Save it as a draft at `f/evals/global/servicenow_assign_incident` and leave it as an
|
||||
AI draft; do not deploy it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 14
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/servicenow_assign_incident
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
requiredToolsAnyOf:
|
||||
- - search_hub_scripts
|
||||
- get_hub_integration
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- it looks the person up in the sys_user table by email rather than assuming a sys_id was supplied
|
||||
- it reads records out of the `result` envelope the ServiceNow API wraps responses in
|
||||
- it sets assigned_to to the user's sys_id rather than to the raw email address
|
||||
- it updates the incident with a partial update (PATCH) rather than a full replace that would clear the fields it did not send
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# Outreach is JSON:API. Its content type and payload envelope are both stated in the
|
||||
# metadata and visible in the shipped update scripts, so this is a second control.
|
||||
- id: global-hub6-outreach-reschedule-task
|
||||
prompt: |-
|
||||
Write me a script that moves an existing Outreach task to a new due date.
|
||||
Save it as a draft at `f/evals/global/outreach_reschedule_task` and leave it as an AI
|
||||
draft; do not deploy it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 14
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
- type: script
|
||||
path: f/evals/global/outreach_reschedule_task
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- write_script
|
||||
requiredToolsAnyOf:
|
||||
- - search_hub_scripts
|
||||
- get_hub_integration
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- the request sends the JSON:API content type application/vnd.api+json for its body
|
||||
- the payload wraps the change as data.type and data.attributes rather than sending bare top-level fields
|
||||
- it identifies the task by id inside the payload rather than only in the URL, and treats the id as a number
|
||||
- it updates the existing task with PATCH rather than creating a new one
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# --- Windmill Hub reuse (search_hub_scripts + read_workspace_item on a hub/ path) ---
|
||||
# Holded's API is obscure enough that a model writing from memory cannot reproduce
|
||||
# its endpoint and `key` auth header — so the draft's fidelity to the published
|
||||
@@ -2358,6 +2457,78 @@
|
||||
- the document type, document id, and recipient emails are inputs to the script
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# Same task as global-hub1 with the hub never mentioned: this is what tells us the
|
||||
# model reaches for the hub on its own rather than only when pointed at it.
|
||||
- id: global-hub2-checks-hub-unprompted
|
||||
prompt: |-
|
||||
I want to email one of my Holded invoices to a customer from Windmill.
|
||||
Save it as a draft script at `f/evals/global/holded_send_document` and leave it as
|
||||
an AI draft; do not deploy it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 12
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
# No language assertion: the Holded script's body is `//native`, so a faithful
|
||||
# fork lands on bun or bunnative depending on how literally it is copied.
|
||||
- type: script
|
||||
path: f/evals/global/holded_send_document
|
||||
valueIncludes:
|
||||
- api.holded.com/api/invoicing/v1/documents
|
||||
- mailTemplateId
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- search_hub_scripts
|
||||
- write_script
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- the draft sends an existing Holded document by email rather than creating one
|
||||
- the request targets Holded's document send endpoint, not an invented URL
|
||||
- authentication uses Holded's own key header rather than a bearer token
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# The fixture hub carries two Baremetrics scripts, neither for annotations, so nothing
|
||||
# does what is asked. Its conventions — an `apiKey` resource, bearer auth, the
|
||||
# api.baremetrics.com/v1 base — are only knowable by reading one of those near-misses.
|
||||
- id: global-hub3-integration-as-example
|
||||
prompt: |-
|
||||
Write me a script that adds an annotation to my Baremetrics account.
|
||||
Save it as a draft at `f/evals/global/baremetrics_create_annotation` and leave it as
|
||||
an AI draft; do not deploy it.
|
||||
initial: ai_evals/fixtures/frontend/global/initial/user_admin_evals_folder.json
|
||||
runtime:
|
||||
maxTurns: 14
|
||||
validate:
|
||||
draftCountExactly: 1
|
||||
requiredDrafts:
|
||||
# No language assertion: matching the hub script's own dialect is the point,
|
||||
# and Baremetrics' scripts are `//native`, so both bun and bunnative are right.
|
||||
- type: script
|
||||
path: f/evals/global/baremetrics_create_annotation
|
||||
valueIncludes:
|
||||
- api.baremetrics.com/v1
|
||||
- apiKey
|
||||
toolExpect:
|
||||
requiredToolsUsed:
|
||||
- search_hub_scripts
|
||||
- write_script
|
||||
# Reading a near-miss script and reading the integration's own metadata are two
|
||||
# routes to the same conventions, so requiring either keeps the case about the
|
||||
# outcome rather than about which route the model took.
|
||||
requiredToolsAnyOf:
|
||||
- - read_workspace_item
|
||||
- get_hub_integration
|
||||
forbiddenToolsUsed:
|
||||
- deploy_workspace_item
|
||||
judgeChecklist:
|
||||
- the script takes a Baremetrics resource as an input parameter rather than a bare API key string
|
||||
- the resource type it declares carries an apiKey field, matching how other Baremetrics hub scripts take credentials
|
||||
- the request authenticates with a bearer token built from that resource
|
||||
- the script posts an annotation rather than reading data back
|
||||
- the result stays an AI draft and is not deployed
|
||||
|
||||
# The value of a secret variable is unreadable, so a metadata-only edit must leave
|
||||
# it untouched: passing any `value` here means inventing one, which silently
|
||||
# replaces the real secret at deploy.
|
||||
|
||||
@@ -108,6 +108,10 @@ async function main() {
|
||||
"--record",
|
||||
"append a compact summary line to ai_evals/history/<mode>.jsonl",
|
||||
)
|
||||
.option(
|
||||
"--reasoning <effort>",
|
||||
"reasoning effort for frontend modes (e.g. off, low, medium, high, max); default: the product's",
|
||||
)
|
||||
.option(
|
||||
"--backend-validation <mode>",
|
||||
`backend smoke validation (${BACKEND_VALIDATION_MODES.join(", ")})`,
|
||||
@@ -126,6 +130,7 @@ async function main() {
|
||||
executionOnly?: boolean;
|
||||
record?: boolean;
|
||||
backendValidation?: string;
|
||||
reasoning?: string;
|
||||
},
|
||||
) => {
|
||||
await handleRun({
|
||||
@@ -140,6 +145,7 @@ async function main() {
|
||||
executionOnly: options.executionOnly ?? false,
|
||||
record: options.record ?? false,
|
||||
backendValidation: options.backendValidation,
|
||||
reasoning: options.reasoning,
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -190,6 +196,7 @@ async function handleRun(input: {
|
||||
executionOnly: boolean;
|
||||
record: boolean;
|
||||
backendValidation?: string;
|
||||
reasoning?: string;
|
||||
}) {
|
||||
if (input.record && input.caseIds.length > 0) {
|
||||
throw new Error(
|
||||
@@ -217,6 +224,13 @@ async function handleRun(input: {
|
||||
"--backend-validation currently supports only flow and script modes",
|
||||
);
|
||||
}
|
||||
if (input.reasoning) {
|
||||
if (input.mode === "cli") {
|
||||
throw new Error("--reasoning only applies to frontend modes");
|
||||
}
|
||||
// The frontend runtime runs in a child process, which inherits it.
|
||||
process.env.WMILL_AI_EVAL_REASONING = input.reasoning;
|
||||
}
|
||||
if (input.mode !== "cli") {
|
||||
await assertWindmillBackendReachable(resolveWindmillBackendSettings());
|
||||
}
|
||||
@@ -257,6 +271,10 @@ async function handleRun(input: {
|
||||
backendValidation,
|
||||
});
|
||||
|
||||
if (input.reasoning && result.runModel) {
|
||||
result.runModel = `${result.runModel}@${input.reasoning}`;
|
||||
}
|
||||
|
||||
const resolvedOutputPath =
|
||||
models.length === 1
|
||||
? resolveRunOutputPath(input.mode, input.outputPath)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Anthropic from "@anthropic-ai/sdk";
|
||||
import type { EvalMode, JudgeResult } from "./types";
|
||||
|
||||
export const DEFAULT_JUDGE_MODEL = "claude-sonnet-4-6";
|
||||
export const DEFAULT_JUDGE_MODEL = "claude-sonnet-5-5";
|
||||
|
||||
const JUDGE_TOOL_NAME = "submit_judgement";
|
||||
|
||||
@@ -29,6 +29,7 @@ export async function judgeOutput(input: {
|
||||
|
||||
const system = [
|
||||
"You evaluate benchmark outputs for Windmill AI generation.",
|
||||
`Always answer by calling the ${JUDGE_TOOL_NAME} tool.`,
|
||||
"Deterministic checks already run separately. Focus on whether the final output satisfies the user request.",
|
||||
"If expected state is provided, treat it as a valid example and reward semantically equivalent outputs.",
|
||||
"If a checklist is provided, treat it as the explicit acceptance criteria for this case.",
|
||||
@@ -69,8 +70,8 @@ export async function judgeOutput(input: {
|
||||
try {
|
||||
const response = await client.messages.create({
|
||||
model,
|
||||
max_tokens: 1024,
|
||||
temperature: 0,
|
||||
// The judge thinks by default, and thinking shares this budget with the verdict.
|
||||
max_tokens: 16000,
|
||||
system,
|
||||
messages: [{ role: "user", content: user }],
|
||||
tools: [
|
||||
@@ -93,11 +94,8 @@ export async function judgeOutput(input: {
|
||||
},
|
||||
},
|
||||
],
|
||||
tool_choice: {
|
||||
type: "tool",
|
||||
name: JUDGE_TOOL_NAME,
|
||||
disable_parallel_tool_use: true,
|
||||
},
|
||||
// Current models refuse a forced tool_choice ("tool"/"any").
|
||||
tool_choice: { type: "auto", disable_parallel_tool_use: true },
|
||||
});
|
||||
|
||||
const toolUseBlock = response.content.find(
|
||||
|
||||
@@ -34,6 +34,10 @@ describe("resolveEvalModel", () => {
|
||||
|
||||
it("supports DeepSeek aliases for frontend evals", () => {
|
||||
expect(resolveEvalModel("flow", "deepseek").frontend).toEqual({
|
||||
provider: "deepseek",
|
||||
model: "deepseek-flash",
|
||||
});
|
||||
expect(resolveEvalModel("flow", "deepseek-v4-flash").frontend).toEqual({
|
||||
provider: "deepseek",
|
||||
model: "deepseek-v4-flash",
|
||||
});
|
||||
|
||||
+81
-1
@@ -78,6 +78,32 @@ export const EVAL_MODELS: EvalModelSpec[] = [
|
||||
model: "opus",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "sonnet-5.5",
|
||||
label: "Claude Sonnet 5.5",
|
||||
aliases: ["sonnet-5.5", "claude-sonnet-5.5", "claude-sonnet-5-5"],
|
||||
frontend: {
|
||||
provider: "anthropic",
|
||||
model: "claude-sonnet-5-5",
|
||||
},
|
||||
cli: {
|
||||
provider: "anthropic",
|
||||
model: "claude-sonnet-5-5",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "opus-5.5",
|
||||
label: "Claude Opus 5.5",
|
||||
aliases: ["opus-5.5", "claude-opus-5.5", "claude-opus-5-5"],
|
||||
frontend: {
|
||||
provider: "anthropic",
|
||||
model: "claude-opus-5-5",
|
||||
},
|
||||
cli: {
|
||||
provider: "anthropic",
|
||||
model: "claude-opus-5-5",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "4o",
|
||||
label: "GPT-4o",
|
||||
@@ -96,6 +122,51 @@ export const EVAL_MODELS: EvalModelSpec[] = [
|
||||
model: "gpt-5.5",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gpt-5.6-sol",
|
||||
label: "GPT-5.6 Sol",
|
||||
aliases: ["gpt-5.6-sol"],
|
||||
frontend: {
|
||||
provider: "openai",
|
||||
model: "gpt-5.6-sol",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gpt-6-astra",
|
||||
label: "GPT-6 Astra",
|
||||
aliases: ["gpt-6-astra", "gpt-6"],
|
||||
frontend: {
|
||||
provider: "openai",
|
||||
model: "gpt-6-astra",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gpt-6-sol",
|
||||
label: "GPT-6 Sol",
|
||||
aliases: ["gpt-6-sol"],
|
||||
frontend: {
|
||||
provider: "openai",
|
||||
model: "gpt-6-sol",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gpt-6-luna",
|
||||
label: "GPT-6 Luna",
|
||||
aliases: ["gpt-6-luna"],
|
||||
frontend: {
|
||||
provider: "openai",
|
||||
model: "gpt-6-luna",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gemini-3.8-flash",
|
||||
label: "Gemini 3.8 Flash",
|
||||
aliases: ["gemini-3.8-flash"],
|
||||
frontend: {
|
||||
provider: "googleai",
|
||||
model: "gemini-3.8-flash",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gemini-3-flash-preview",
|
||||
label: "Gemini 3 Flash Preview",
|
||||
@@ -121,12 +192,21 @@ export const EVAL_MODELS: EvalModelSpec[] = [
|
||||
{
|
||||
id: "deepseek-v4-flash",
|
||||
label: "DeepSeek V4 Flash",
|
||||
aliases: ["deepseek", "deepseek-v4", "deepseek-v4-flash"],
|
||||
aliases: ["deepseek-v4", "deepseek-v4-flash"],
|
||||
frontend: {
|
||||
provider: "deepseek",
|
||||
model: "deepseek-v4-flash",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "deepseek-flash",
|
||||
label: "DeepSeek V4.1 Flash",
|
||||
aliases: ["deepseek", "deepseek-flash", "deepseek-v4.1-flash"],
|
||||
frontend: {
|
||||
provider: "deepseek",
|
||||
model: "deepseek-flash",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "deepseek-v4-pro",
|
||||
label: "DeepSeek V4 Pro",
|
||||
|
||||
@@ -0,0 +1,983 @@
|
||||
{
|
||||
"app": "confluence",
|
||||
"display_name": "Confluence",
|
||||
"description": "Atlassian's team workspace: pages, blog posts and spaces.",
|
||||
"docs_url": "https://developer.atlassian.com/cloud/confluence/rest/v2/intro/",
|
||||
"curated": true,
|
||||
"meta": {
|
||||
"api_docs": "https://developer.atlassian.com/cloud/confluence/rest/v2/intro/",
|
||||
"shipped_surfaces": [
|
||||
"pages",
|
||||
"blogposts",
|
||||
"spaces",
|
||||
"search",
|
||||
"users"
|
||||
],
|
||||
"validation": {
|
||||
"status": "live_validated",
|
||||
"method": "smoke test via `bun --env-file=.env.test -e` (+ `bun test` with the windmill-client fake for the trigger) against a free Confluence Cloud site (a test site) with an API token. All 13 actions + the trigger returned 2xx: full page lifecycle (create -> get -> update -> list -> delete), blog post create/update/delete, list_spaces, search_content (CQL), get_current_user, and the new_or_updated_page polling trigger.",
|
||||
"sources": {
|
||||
"pages": "HIGH \u2014 Pipedream actions + official v2 OpenAPI; create/get/update/list/delete all run live (2xx)",
|
||||
"blogposts": "HIGH \u2014 Pipedream actions + official v2 OpenAPI; create/update/delete all run live (2xx)",
|
||||
"spaces": "HIGH \u2014 Pipedream list-spaces + official v2 OpenAPI; list_spaces run live (2xx)",
|
||||
"search": "HIGH \u2014 Pipedream search-content + official v1 REST; search_content (CQL) run live (2xx)",
|
||||
"users": "HIGH \u2014 v1 /wiki/rest/api/user/current run live (2xx) with Basic auth",
|
||||
"trigger": "HIGH \u2014 new_or_updated_page e2e-tested under bun test with the auto-fake; first run sets the watermark, a subsequent run emits a freshly created page"
|
||||
},
|
||||
"low_confidence_fields": []
|
||||
},
|
||||
"pagination": {
|
||||
"pattern": "cursor",
|
||||
"request_params": {
|
||||
"cursor": "cursor",
|
||||
"limit": "limit"
|
||||
},
|
||||
"response_fields": {
|
||||
"next": "_links.next"
|
||||
},
|
||||
"default_limit": 25,
|
||||
"notes": "v2 endpoints return an opaque cursor inside the `_links.next` relative URL (?cursor=...) and also in the Link response header. The v1 /search endpoint paginates the same way."
|
||||
},
|
||||
"gotchas": [
|
||||
"Two API versions are in play: pages / blog posts / spaces use the v2 REST API (/wiki/api/v2); CQL search and current-user use the v1 REST API (/wiki/rest/api).",
|
||||
"Auth is HTTP Basic with the Atlassian account email as the username and an API token (NOT the account password) as the password.",
|
||||
"v2 query params are kebab-case (body-format, created-date, get-draft, root-level); v2 JSON request-body fields are camelCase (spaceId, parentId).",
|
||||
"Updating a page or blog post requires version.number = currentVersion + 1; the update_* scripts fetch the current version and increment it automatically.",
|
||||
"spaceId / parentId in v2 request bodies are numeric IDs passed as strings, not space keys.",
|
||||
"DELETE returns 204 with an empty body and trashes by default; pass purge=true to permanently remove an item that is already in the trash."
|
||||
],
|
||||
"enums": {
|
||||
"page_status": [
|
||||
"current",
|
||||
"archived",
|
||||
"deleted",
|
||||
"trashed",
|
||||
"draft",
|
||||
"historical"
|
||||
],
|
||||
"body_representation": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"body_format_read": [
|
||||
"storage",
|
||||
"atlas_doc_format",
|
||||
"view",
|
||||
"export_view",
|
||||
"anonymous_export_view",
|
||||
"styled_view",
|
||||
"editor"
|
||||
],
|
||||
"space_type": [
|
||||
"global",
|
||||
"collaboration",
|
||||
"knowledge_base",
|
||||
"personal"
|
||||
],
|
||||
"page_sort": [
|
||||
"id",
|
||||
"-id",
|
||||
"created-date",
|
||||
"-created-date",
|
||||
"modified-date",
|
||||
"-modified-date",
|
||||
"title",
|
||||
"-title"
|
||||
]
|
||||
},
|
||||
"errors": {
|
||||
"401": "Authentication failed \u2014 verify the email + API token pair and that the token has not been revoked.",
|
||||
"404": "Resource not found, or the authenticating account lacks permission to view it (Confluence often returns 404 rather than 403 for content the user cannot access)."
|
||||
}
|
||||
},
|
||||
"resource_type": {
|
||||
"name": "confluence",
|
||||
"description": "Confluence Cloud credentials. Authenticate with an Atlassian account email and an API token (HTTP Basic auth) against your site base URL.",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"apiToken": {
|
||||
"description": "API token for your Atlassian account. Create one at https://id.atlassian.com/manage-profile/security/api-tokens",
|
||||
"type": "string"
|
||||
},
|
||||
"baseUrl": {
|
||||
"description": "Base URL of your Confluence Cloud site, e.g. https://your-domain.atlassian.net (no trailing slash, no /wiki).",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "Email of the Atlassian account used to authenticate.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email",
|
||||
"apiToken",
|
||||
"baseUrl"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"scripts": [
|
||||
{
|
||||
"version_id": 28765,
|
||||
"app": "confluence",
|
||||
"summary": "Create Blog Post",
|
||||
"description": "Create a new blog post in a space.",
|
||||
"terms": "confluence create blog post create a new blog post in a space.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Create Blog Post\n * Create a new blog post in a space. The body is supplied as a content string in the chosen representation (default \"storage\", i.e. Confluence storage HTML).\n */\nexport async function main(\n auth: RT.Confluence,\n space_id: string,\n title: string,\n body: string,\n status: \"current\" | \"draft\" | undefined,\n representation: \"storage\" | \"atlas_doc_format\" | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/blogposts`)\n\n const payload = {\n spaceId: space_id,\n status: status ?? \"current\",\n title,\n body: {\n representation: representation ?? \"storage\",\n value: body,\n },\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"representation": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"space_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"current",
|
||||
"draft"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"space_id",
|
||||
"title",
|
||||
"body"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28766,
|
||||
"app": "confluence",
|
||||
"summary": "Create Page",
|
||||
"description": "Create a new page in a space.",
|
||||
"terms": "confluence create page create a new page in a space.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Create Page\n * Create a new page in a space. The body is supplied as a content string in the chosen representation (default \"storage\", i.e. Confluence storage HTML).\n */\nexport async function main(\n auth: RT.Confluence,\n space_id: string,\n title: string,\n body: string,\n status: \"current\" | \"draft\" | undefined,\n parent_id: string | undefined,\n representation: \"storage\" | \"atlas_doc_format\" | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/pages`)\n\n const payload = {\n spaceId: space_id,\n status: status ?? \"current\",\n title,\n parentId: parent_id,\n body: {\n representation: representation ?? \"storage\",\n value: body,\n },\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"parent_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"representation": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"space_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"current",
|
||||
"draft"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"space_id",
|
||||
"title",
|
||||
"body"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28767,
|
||||
"app": "confluence",
|
||||
"summary": "Delete Blog Post",
|
||||
"description": "Delete a blog post by its ID (moved to trash by default; purge to permanently remove).",
|
||||
"terms": "confluence delete blog post delete a blog post by its id (moved to trash by default; purge to permanently remove).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Delete Blog Post\n * Delete a blog post by its ID. By default the post is moved to the trash; set purge to permanently remove an already-trashed post.\n */\nexport async function main(\n auth: RT.Confluence,\n blog_post_id: string,\n purge: boolean | undefined,\n draft: boolean | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/blogposts/${blog_post_id}`)\n if (purge !== undefined) url.searchParams.append(\"purge\", String(purge))\n if (draft !== undefined) url.searchParams.append(\"draft\", String(draft))\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n if (response.status === 204) return { success: true }\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"blog_post_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"draft": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
},
|
||||
"purge": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"blog_post_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28768,
|
||||
"app": "confluence",
|
||||
"summary": "Delete Page",
|
||||
"description": "Delete a page by its ID (moved to trash by default; purge to permanently remove).",
|
||||
"terms": "confluence delete page delete a page by its id (moved to trash by default; purge to permanently remove).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Delete Page\n * Delete a page by its ID. By default the page is moved to the trash; set purge to permanently remove an already-trashed page.\n */\nexport async function main(\n auth: RT.Confluence,\n page_id: string,\n purge: boolean | undefined,\n draft: boolean | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/pages/${page_id}`)\n if (purge !== undefined) url.searchParams.append(\"purge\", String(purge))\n if (draft !== undefined) url.searchParams.append(\"draft\", String(draft))\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n if (response.status === 204) return { success: true }\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"draft": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
},
|
||||
"page_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"purge": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"page_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28769,
|
||||
"app": "confluence",
|
||||
"summary": "Get Current User",
|
||||
"description": "Returns the profile of the user that the API token authenticates as.",
|
||||
"terms": "confluence get current user returns the profile of the user that the api token authenticates as.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Get Current User\n * Returns the profile of the user that the API token authenticates as (account ID, display name, email).\n */\nexport async function main(auth: RT.Confluence) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/rest/api/user/current`)\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28770,
|
||||
"app": "confluence",
|
||||
"summary": "Get Page by ID",
|
||||
"description": "Retrieve a single page by its ID, optionally returning its body in a specific format or a previous version.",
|
||||
"terms": "confluence get page by id retrieve a single page by its id, optionally returning its body in a specific format or a previous version.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Get Page by ID\n * Retrieve a single page by its ID, optionally returning its body in a specific format or a previous version.\n */\nexport async function main(\n auth: RT.Confluence,\n page_id: string,\n body_format:\n | \"storage\"\n | \"atlas_doc_format\"\n | \"view\"\n | \"export_view\"\n | \"anonymous_export_view\"\n | \"styled_view\"\n | \"editor\"\n | undefined,\n version: number | undefined,\n get_draft: boolean | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/pages/${page_id}`)\n if (body_format !== undefined)\n url.searchParams.append(\"body-format\", body_format)\n if (version !== undefined) url.searchParams.append(\"version\", String(version))\n if (get_draft !== undefined)\n url.searchParams.append(\"get-draft\", String(get_draft))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body_format": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format",
|
||||
"view",
|
||||
"export_view",
|
||||
"anonymous_export_view",
|
||||
"styled_view",
|
||||
"editor"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"get_draft": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
},
|
||||
"page_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"page_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28771,
|
||||
"app": "confluence",
|
||||
"summary": "Get Pages in Space",
|
||||
"description": "Retrieve a paginated list of pages within a given space.",
|
||||
"terms": "confluence get pages in space retrieve a paginated list of pages within a given space.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Get Pages in Space\n * Retrieve a paginated list of pages within a given space.\n */\nexport async function main(\n auth: RT.Confluence,\n space_id: string,\n status: (\"current\" | \"archived\" | \"deleted\" | \"trashed\")[] | undefined,\n title: string | undefined,\n sort:\n | \"id\"\n | \"-id\"\n | \"created-date\"\n | \"-created-date\"\n | \"modified-date\"\n | \"-modified-date\"\n | \"title\"\n | \"-title\"\n | undefined,\n body_format: \"storage\" | \"atlas_doc_format\" | undefined,\n cursor: string | undefined,\n limit: number | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/spaces/${space_id}/pages`)\n for (const s of status ?? []) url.searchParams.append(\"status\", s)\n if (title !== undefined && title !== \"\")\n url.searchParams.append(\"title\", title)\n if (sort !== undefined) url.searchParams.append(\"sort\", sort)\n if (body_format !== undefined)\n url.searchParams.append(\"body-format\", body_format)\n if (cursor !== undefined && cursor !== \"\")\n url.searchParams.append(\"cursor\", cursor)\n if (limit !== undefined) url.searchParams.append(\"limit\", String(limit))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body_format": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"cursor": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"sort": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"id",
|
||||
"-id",
|
||||
"created-date",
|
||||
"-created-date",
|
||||
"modified-date",
|
||||
"-modified-date",
|
||||
"title",
|
||||
"-title"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"space_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"items": {
|
||||
"enum": [
|
||||
"current",
|
||||
"archived",
|
||||
"deleted",
|
||||
"trashed"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"originalType": "enum[]",
|
||||
"type": "array"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"space_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28772,
|
||||
"app": "confluence",
|
||||
"summary": "List Blog Posts",
|
||||
"description": "Retrieve a paginated list of blog posts on the site, with optional filtering and sorting.",
|
||||
"terms": "confluence list blog posts retrieve a paginated list of blog posts on the site, with optional filtering and sorting.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * List Blog Posts\n * Retrieve a paginated list of blog posts on the site, with optional filtering and sorting.\n */\nexport async function main(\n auth: RT.Confluence,\n status: (\"current\" | \"archived\" | \"deleted\" | \"trashed\")[] | undefined,\n title: string | undefined,\n sort:\n | \"id\"\n | \"-id\"\n | \"created-date\"\n | \"-created-date\"\n | \"modified-date\"\n | \"-modified-date\"\n | undefined,\n body_format: \"storage\" | \"atlas_doc_format\" | undefined,\n cursor: string | undefined,\n limit: number | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/blogposts`)\n for (const s of status ?? []) url.searchParams.append(\"status\", s)\n if (title !== undefined && title !== \"\")\n url.searchParams.append(\"title\", title)\n if (sort !== undefined) url.searchParams.append(\"sort\", sort)\n if (body_format !== undefined)\n url.searchParams.append(\"body-format\", body_format)\n if (cursor !== undefined && cursor !== \"\")\n url.searchParams.append(\"cursor\", cursor)\n if (limit !== undefined) url.searchParams.append(\"limit\", String(limit))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body_format": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"cursor": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"sort": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"id",
|
||||
"-id",
|
||||
"created-date",
|
||||
"-created-date",
|
||||
"modified-date",
|
||||
"-modified-date"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"items": {
|
||||
"enum": [
|
||||
"current",
|
||||
"archived",
|
||||
"deleted",
|
||||
"trashed"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"originalType": "enum[]",
|
||||
"type": "array"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28773,
|
||||
"app": "confluence",
|
||||
"summary": "List Pages",
|
||||
"description": "Retrieve a paginated list of pages on the site, with optional filtering and sorting.",
|
||||
"terms": "confluence list pages retrieve a paginated list of pages on the site, with optional filtering and sorting.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * List Pages\n * Retrieve a paginated list of pages on the site, with optional filtering and sorting.\n */\nexport async function main(\n auth: RT.Confluence,\n status: (\"current\" | \"archived\" | \"deleted\" | \"trashed\")[] | undefined,\n title: string | undefined,\n sort:\n | \"id\"\n | \"-id\"\n | \"created-date\"\n | \"-created-date\"\n | \"modified-date\"\n | \"-modified-date\"\n | \"title\"\n | \"-title\"\n | undefined,\n body_format: \"storage\" | \"atlas_doc_format\" | undefined,\n cursor: string | undefined,\n limit: number | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/pages`)\n for (const s of status ?? []) url.searchParams.append(\"status\", s)\n if (title !== undefined && title !== \"\")\n url.searchParams.append(\"title\", title)\n if (sort !== undefined) url.searchParams.append(\"sort\", sort)\n if (body_format !== undefined)\n url.searchParams.append(\"body-format\", body_format)\n if (cursor !== undefined && cursor !== \"\")\n url.searchParams.append(\"cursor\", cursor)\n if (limit !== undefined) url.searchParams.append(\"limit\", String(limit))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body_format": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"cursor": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"sort": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"id",
|
||||
"-id",
|
||||
"created-date",
|
||||
"-created-date",
|
||||
"modified-date",
|
||||
"-modified-date",
|
||||
"title",
|
||||
"-title"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"items": {
|
||||
"enum": [
|
||||
"current",
|
||||
"archived",
|
||||
"deleted",
|
||||
"trashed"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"originalType": "enum[]",
|
||||
"type": "array"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28774,
|
||||
"app": "confluence",
|
||||
"summary": "List Spaces",
|
||||
"description": "Retrieve a paginated list of spaces on the site, with optional filtering by key, type, and status.",
|
||||
"terms": "confluence list spaces retrieve a paginated list of spaces on the site, with optional filtering by key, type, and status.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * List Spaces\n * Retrieve a paginated list of spaces on the site, with optional filtering by key, type, and status.\n */\nexport async function main(\n auth: RT.Confluence,\n keys: string[] | undefined,\n type: \"global\" | \"collaboration\" | \"knowledge_base\" | \"personal\" | undefined,\n status: \"current\" | \"archived\" | undefined,\n sort: \"id\" | \"-id\" | \"key\" | \"-key\" | \"name\" | \"-name\" | undefined,\n cursor: string | undefined,\n limit: number | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/api/v2/spaces`)\n if (keys !== undefined && keys.length > 0)\n url.searchParams.append(\"keys\", keys.join(\",\"))\n if (type !== undefined) url.searchParams.append(\"type\", type)\n if (status !== undefined) url.searchParams.append(\"status\", status)\n if (sort !== undefined) url.searchParams.append(\"sort\", sort)\n if (cursor !== undefined && cursor !== \"\")\n url.searchParams.append(\"cursor\", cursor)\n if (limit !== undefined) url.searchParams.append(\"limit\", String(limit))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"cursor": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"keys": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"originalType": "string[]",
|
||||
"type": "array"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"sort": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"id",
|
||||
"-id",
|
||||
"key",
|
||||
"-key",
|
||||
"name",
|
||||
"-name"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"current",
|
||||
"archived"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"global",
|
||||
"collaboration",
|
||||
"knowledge_base",
|
||||
"personal"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28775,
|
||||
"app": "confluence",
|
||||
"summary": "Search Content (CQL)",
|
||||
"description": "Search for content using the Confluence Query Language (CQL).",
|
||||
"terms": "confluence search content (cql) search for content using the confluence query language (cql).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Search Content (CQL)\n * Search for content using the Confluence Query Language (CQL), e.g. `type=page AND space=ENG AND text ~ \"release notes\"`.\n */\nexport async function main(\n auth: RT.Confluence,\n cql: string,\n cursor: string | undefined,\n limit: number | undefined,\n expand: string | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const url = new URL(`${base}/wiki/rest/api/search`)\n url.searchParams.append(\"cql\", cql)\n if (cursor !== undefined && cursor !== \"\")\n url.searchParams.append(\"cursor\", cursor)\n if (limit !== undefined) url.searchParams.append(\"limit\", String(limit))\n if (expand !== undefined && expand !== \"\")\n url.searchParams.append(\"expand\", expand)\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"cql": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"cursor": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"expand": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"cql"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28776,
|
||||
"app": "confluence",
|
||||
"summary": "Update Blog Post",
|
||||
"description": "Update a blog post's title, body, or status. The current version is fetched automatically and incremented.",
|
||||
"terms": "confluence update blog post update a blog post's title, body, or status. the current version is fetched automatically and incremented.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Update Blog Post\n * Update a blog post's title, body, or status. The current version is fetched automatically and incremented; omitted fields keep their existing values.\n */\nexport async function main(\n auth: RT.Confluence,\n blog_post_id: string,\n title: string | undefined,\n body: string | undefined,\n status: \"current\" | \"draft\" | \"archived\" | undefined,\n representation: \"storage\" | \"atlas_doc_format\" | undefined,\n version_message: string | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const repr = representation ?? \"storage\"\n const authHeader = \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`)\n\n // Fetch the current blog post to obtain the version number and any fields not being changed.\n const currentRes = await fetch(\n `${base}/wiki/api/v2/blogposts/${blog_post_id}?body-format=${repr}`,\n {\n method: \"GET\",\n headers: { Authorization: authHeader, Accept: \"application/json\" },\n }\n )\n if (!currentRes.ok) {\n throw new Error(`${currentRes.status} ${await currentRes.text()}`)\n }\n const current = (await currentRes.json()) as {\n title: string\n status: string\n version: { number: number }\n body?: { [key: string]: { value?: string } }\n }\n\n const payload = {\n id: blog_post_id,\n status: status ?? current.status,\n title: title ?? current.title,\n body: {\n representation: repr,\n value: body ?? current.body?.[repr]?.value ?? \"\",\n },\n version: {\n number: current.version.number + 1,\n message: version_message,\n },\n }\n\n const response = await fetch(\n `${base}/wiki/api/v2/blogposts/${blog_post_id}`,\n {\n method: \"PUT\",\n headers: {\n Authorization: authHeader,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(payload),\n }\n )\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"blog_post_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"representation": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"current",
|
||||
"draft",
|
||||
"archived"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"version_message": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"blog_post_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28777,
|
||||
"app": "confluence",
|
||||
"summary": "Update Page",
|
||||
"description": "Update a page's title, body, or status. The current version is fetched automatically and incremented.",
|
||||
"terms": "confluence update page update a page's title, body, or status. the current version is fetched automatically and incremented.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\n/**\n * Update Page\n * Update a page's title, body, or status. The current version is fetched automatically and incremented; omitted fields keep their existing values.\n */\nexport async function main(\n auth: RT.Confluence,\n page_id: string,\n title: string | undefined,\n body: string | undefined,\n status: \"current\" | \"draft\" | \"archived\" | undefined,\n representation: \"storage\" | \"atlas_doc_format\" | undefined,\n version_message: string | undefined\n) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const repr = representation ?? \"storage\"\n const authHeader = \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`)\n\n // Fetch the current page to obtain the version number and any fields not being changed.\n const currentRes = await fetch(\n `${base}/wiki/api/v2/pages/${page_id}?body-format=${repr}`,\n {\n method: \"GET\",\n headers: { Authorization: authHeader, Accept: \"application/json\" },\n }\n )\n if (!currentRes.ok) {\n throw new Error(`${currentRes.status} ${await currentRes.text()}`)\n }\n const current = (await currentRes.json()) as {\n title: string\n status: string\n version: { number: number }\n body?: { [key: string]: { value?: string } }\n }\n\n const payload = {\n id: page_id,\n status: status ?? current.status,\n title: title ?? current.title,\n body: {\n representation: repr,\n value: body ?? current.body?.[repr]?.value ?? \"\",\n },\n version: {\n number: current.version.number + 1,\n message: version_message,\n },\n }\n\n const response = await fetch(`${base}/wiki/api/v2/pages/${page_id}`, {\n method: \"PUT\",\n headers: {\n Authorization: authHeader,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"page_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"representation": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"storage",
|
||||
"atlas_doc_format"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"current",
|
||||
"draft",
|
||||
"archived"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"version_message": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"page_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28778,
|
||||
"app": "confluence",
|
||||
"summary": "New or Updated Page",
|
||||
"description": "Emits pages created or updated since the last poll, tracked via Windmill state. Optionally restrict to a single space.",
|
||||
"terms": "confluence new or updated page emits pages created or updated since the last poll, tracked via windmill state. optionally restrict to a single space.",
|
||||
"kind": "trigger",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nimport * as wmill from \"windmill-client\"\n\n/**\n * New or Updated Page\n * Emits pages created or updated since the last poll, tracked via Windmill state. Optionally restrict to a single space.\n */\nexport async function main(auth: RT.Confluence, space_id: string | undefined) {\n const base = auth.baseUrl.replace(/\\/$/, \"\")\n const lastChecked: number = (await wmill.getState()) ?? 0\n\n const path = space_id\n ? `/wiki/api/v2/spaces/${space_id}/pages`\n : `/wiki/api/v2/pages`\n const url = new URL(`${base}${path}`)\n url.searchParams.append(\"sort\", \"-modified-date\")\n url.searchParams.append(\"limit\", \"50\")\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: \"Basic \" + btoa(`${auth.email}:${auth.apiToken}`),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n const data = (await response.json()) as {\n results?: { version?: { createdAt?: string } }[]\n }\n const pages = data.results ?? []\n\n const ts = (p: { version?: { createdAt?: string } }) =>\n p.version?.createdAt ? Date.parse(p.version.createdAt) : 0\n const maxTs = pages.reduce((m, p) => Math.max(m, ts(p)), 0)\n\n // First run: set the watermark to the newest page seen and don't emit a backlog.\n if (!lastChecked) {\n if (maxTs) await wmill.setState(maxTs)\n return []\n }\n\n const fresh = pages\n .filter((p) => ts(p) > lastChecked)\n .sort((a, b) => ts(a) - ts(b))\n\n if (maxTs > lastChecked) {\n await wmill.setState(maxTs)\n }\n\n return fresh\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-confluence",
|
||||
"type": "object"
|
||||
},
|
||||
"space_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,672 @@
|
||||
{
|
||||
"app": "servicenow",
|
||||
"display_name": "ServiceNow",
|
||||
"description": "IT service management: incidents, records and attachments.",
|
||||
"docs_url": "https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_RESTAPI.html",
|
||||
"curated": true,
|
||||
"meta": {
|
||||
"api_docs": "https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_RESTAPI.html",
|
||||
"shipped_surfaces": [
|
||||
"table",
|
||||
"stats",
|
||||
"attachment",
|
||||
"import_set",
|
||||
"user"
|
||||
],
|
||||
"validation": {
|
||||
"status": "live_validated",
|
||||
"method": "All surfaces run live against a ServiceNow PDI (a developer instance, Zurich) on 2026-06-04 with Basic auth: Table CRUD (create / get / update PATCH+PUT / delete on incident, delete verified by 404), list_records (encoded query + fields + pagination), aggregate (Stats group-by count), get_current_user, list_tables, and the table/fields/group_by dynselect resolvers. Attachment upload (binary /file) -> list -> download -> delete round-trip with the downloaded bytes byte-for-byte matching the upload. insert_import_set + get_import_set_result against imp_user (transform status surfaced). Trigger e2e-tested via bun test with the windmill-client fake. All created records cleaned up.",
|
||||
"sources": {
|
||||
"table_crud": "HIGH - run live (create/get/update PATCH & PUT/delete on incident)",
|
||||
"aggregate_stats": "HIGH - run live (/api/now/stats group-by count on incident)",
|
||||
"attachment": "HIGH - run live (binary /file upload -> list -> download -> delete round-trip; downloaded bytes verified equal)",
|
||||
"import_set": "HIGH - run live (POST /api/now/import/imp_user + GET result; top-level {import_set,staging_table,result} envelope, transform status surfaced)",
|
||||
"user": "HIGH - run live (/api/now/ui/user/current_user -> admin sys_user)",
|
||||
"trigger": "HIGH - e2e-tested live via bun test (first run sets watermark + no backlog, second run emits the new record)",
|
||||
"dropdowns": "HIGH - run live (sys_db_object table list; sys_dictionary super_class inheritance walk confirmed - incident resolves 106 fields incl. task-inherited short_description/priority/state)"
|
||||
},
|
||||
"low_confidence_fields": [
|
||||
"trigger datetime comparison assumes the integration user's timezone is UTC/GMT (validated with the PDI admin user, whose TZ is UTC); a non-UTC integration user could shift the sys_updated_on> watermark boundary by the offset"
|
||||
]
|
||||
},
|
||||
"pagination": {
|
||||
"pattern": "offset",
|
||||
"request_params": {
|
||||
"offset": "sysparm_offset",
|
||||
"limit": "sysparm_limit"
|
||||
},
|
||||
"response_fields": {
|
||||
"next": "Link (response header, rel=\"next\")",
|
||||
"total": "X-Total-Count (response header)"
|
||||
},
|
||||
"default_limit": 10000,
|
||||
"notes": "Table API pages with sysparm_offset + sysparm_limit. Each page response carries a Link header (RFC 5988) with rel=\"first\"/\"prev\"/\"next\"/\"last\" full URLs, plus X-Total-Count with the total match count. Add sysparm_no_count=true to skip the count on huge tables. The list_records action exposes limit+offset and returns one page; iterate offset until fewer than limit rows return."
|
||||
},
|
||||
"gotchas": [
|
||||
"Base path is {instance_url}/api/now; instance_url lives on the resource. Auth is Basic (username:password) OR Bearer token \u2014 scripts send Bearer when auth.token is set, otherwise Basic. Both hit identical paths.",
|
||||
"Table/Attachment/Stats reads/writes return a {\"result\": ...} envelope: list/stats -> result is an array, get/create/update -> result is an object. EXCEPTION: the Import Set API (POST /api/now/import/{staging}, GET .../{sys_id}) returns a TOP-LEVEL object {import_set, staging_table, result:[...]} with NO outer 'result' wrapper. Scripts return the raw JSON (envelope intact).",
|
||||
"Datetimes are 'YYYY-MM-DD HH:MM:SS' in UTC with NO trailing 'Z'. Used directly in encoded queries, e.g. sys_updated_on>2026-01-01 00:00:00.",
|
||||
"sysparm_query is an ENCODED query string: '^' = AND clause separator, '^OR' = OR, operators =, !=, LIKE, >=, <=, IN, STARTSWITH, ORDERBY, ORDERBYDESC. A literal '^' inside a value must be doubled to '^^'. Example: active=true^priority=1^ORDERBYDESCsys_updated_on.",
|
||||
"System fields sys_id, sys_created_on, sys_updated_on, sys_created_by are auto-managed; do not send them on create/update.",
|
||||
"DELETE (record and attachment) returns 204 No Content with an empty body \u2014 guard with `if (response.status === 204) return { success: true }` before response.json().",
|
||||
"Update: PATCH partial-updates; PUT replaces the whole record (clears omitted fields). update_record sends PUT when replace=true, PATCH otherwise.",
|
||||
"Attachment upload uses POST /api/now/attachment/file with table_name, table_sys_id, file_name in the QUERY STRING and the raw file bytes as the body (Content-Type = the file's MIME type), NOT multipart. Returns 201. Download is GET /api/now/attachment/{sys_id}/file (raw bytes).",
|
||||
"Import Set transform runs SYNCHRONOUSLY. A result[].status of 'error' is an application-level failure even on HTTP 200/201 \u2014 inspect result[].status / error_message, don't trust the HTTP code alone.",
|
||||
"Table-name DROPDOWN comes from GET /api/now/table/sys_db_object?sysparm_fields=name,label (use the 'name' as the value). A table's COLUMNS come from sys_dictionary \u2014 but sys_dictionary stores each field once, under the table where it is DEFINED, so name=<table> returns ONLY that table's own fields. Extended tables (incident/change_request/sc_task all extend task) inherit columns like short_description/priority/assigned_to/state that live on the parent. To get the full column set, walk sys_db_object.super_class up the hierarchy (GET sys_db_object?sysparm_query=name=<t>&sysparm_fields=super_class.name, repeat) and query GET sys_dictionary?sysparm_query=nameIN<table>,<ancestors>^elementISNOTEMPTY&sysparm_fields=element,column_label (elementISNOTEMPTY drops the collection/meta row).",
|
||||
"Table API enforces row- and field-level ACLs: a 200 can omit fields the user can't read; inaccessible tables/ops return 403."
|
||||
],
|
||||
"enums": {
|
||||
"sysparm_display_value": [
|
||||
"true",
|
||||
"false",
|
||||
"all"
|
||||
],
|
||||
"sysparm_view": [
|
||||
"desktop",
|
||||
"mobile",
|
||||
"both"
|
||||
],
|
||||
"import_set_result_status": [
|
||||
"inserted",
|
||||
"updated",
|
||||
"ignored",
|
||||
"error"
|
||||
],
|
||||
"incident_state_note": "incident.state and priority/urgency/impact are instance-configurable; defaults: state 1=New,2=In Progress,3=On Hold,6=Resolved,7=Closed,8=Canceled; priority/urgency/impact 1-5. On task/incident, priority is CALCULATED from impact+urgency (a priority value sent on create/update is recomputed/ignored) \u2014 set impact+urgency instead. Never hardcode for generic actions."
|
||||
},
|
||||
"errors": {
|
||||
"401": "Authentication failed or OAuth token expired/invalid. Check username/password or refresh the bearer token.",
|
||||
"403": "ACL denies the table or operation, or the user lacks the required role (e.g. rest_api_explorer / table ACL). Response: {\"error\":{\"message\":\"User Not Authorized\"}}.",
|
||||
"404": "No Record found (bad sys_id) or Invalid table (bad/inaccessible table name, common Import Set staging-table error).",
|
||||
"400": "Bad encoded query, invalid field name, or malformed request body."
|
||||
}
|
||||
},
|
||||
"resource_type": {
|
||||
"name": "servicenow",
|
||||
"description": "ServiceNow instance credential. Set instance_url to your instance base (https://<instance>.service-now.com). Authenticate with EITHER Basic auth (username + password) OR an OAuth 2.0 bearer token \u2014 scripts send a Bearer header when token is set, otherwise Basic auth.",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"order": [
|
||||
"instance_url",
|
||||
"username",
|
||||
"password",
|
||||
"token"
|
||||
],
|
||||
"properties": {
|
||||
"instance_url": {
|
||||
"default": "",
|
||||
"description": "Instance base URL, e.g. https://dev12345.service-now.com (no trailing slash). Every REST call is made against this host.",
|
||||
"placeholder": "https://dev12345.service-now.com",
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"default": "",
|
||||
"description": "Basic auth password. Leave blank when using an OAuth bearer token.",
|
||||
"password": true,
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"default": "",
|
||||
"description": "OAuth 2.0 access token (Bearer). Populated automatically when you connect via Windmill's ServiceNow OAuth flow (admin configures the instance + client id/secret in instance settings), or paste one manually. Provide this instead of username/password to authenticate with OAuth.",
|
||||
"password": true,
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"default": "",
|
||||
"description": "Basic auth username. Leave blank when using an OAuth bearer token.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"instance_url"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"scripts": [
|
||||
{
|
||||
"version_id": 28665,
|
||||
"app": "servicenow",
|
||||
"summary": "Aggregate Records",
|
||||
"description": "Count records on a table (Aggregate API), optionally filtered by an encoded query and grouped by a field.",
|
||||
"terms": "servicenow aggregate records count records on a table (aggregate api), optionally filtered by an encoded query and grouped by a field.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\nexport type DynSelect_group_by = string\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n// Walk the table's inheritance chain (sys_db_object.super_class) so the group-by\n// options include fields inherited from parent tables, not just fields defined\n// on the chosen table (sys_dictionary stores each field under its defining\n// table \u2014 e.g. incident extends task, so priority/state live on task).\n// Best-effort: any failure falls back to just the table itself.\nasync function tableChain(auth: RT.Servicenow, table: string) {\n const chain: string[] = []\n let current: string | undefined = table\n while (current && !chain.includes(current)) {\n chain.push(current)\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_query=name=${current}&sysparm_fields=super_class.name&sysparm_limit=1`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) break\n const { result } = (await response.json()) as {\n result: { \"super_class.name\"?: string }[]\n }\n current = result[0]?.[\"super_class.name\"] || undefined\n }\n return chain\n}\n\n// Dependent select: the chosen table's columns (sys_dictionary) to group by,\n// including columns inherited from parent tables.\nexport async function group_by(auth: RT.Servicenow, table: DynSelect_table) {\n if (!table) return []\n const chain = await tableChain(auth, table)\n const url = new URL(`${auth.instance_url}/api/now/table/sys_dictionary`)\n url.searchParams.append(\n \"sysparm_query\",\n `nameIN${chain.join(\",\")}^elementISNOTEMPTY`\n )\n url.searchParams.append(\"sysparm_fields\", \"element,column_label\")\n url.searchParams.append(\"sysparm_limit\", \"10000\")\n\n const response = await fetch(url, {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { element: string; column_label: string }[]\n }\n return result\n .filter((f) => f.element)\n .map((f) => ({\n value: f.element,\n label: `${f.column_label} (${f.element})`,\n }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * Aggregate Records\n * Count records on a table (Aggregate API), optionally filtered by an encoded query and grouped by a field. Returns ServiceNow's stats envelope with the count per group.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n query: string | undefined,\n group_by: DynSelect_group_by | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/stats/${table}`)\n url.searchParams.append(\"sysparm_count\", \"true\")\n if (query !== undefined && query !== \"\") {\n url.searchParams.append(\"sysparm_query\", query)\n }\n if (group_by !== undefined && group_by !== \"\") {\n url.searchParams.append(\"sysparm_group_by\", group_by)\n }\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"group_by": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-group_by",
|
||||
"type": "object"
|
||||
},
|
||||
"query": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28666,
|
||||
"app": "servicenow",
|
||||
"summary": "Create Record",
|
||||
"description": "Insert a record into any table (incident, change_request, sys_user, cmdb_ci, or a custom table). Pick the table from a live dropdown and pass a column/value map.",
|
||||
"terms": "servicenow create record insert a record into any table (incident, change_request, sys_user, cmdb_ci, or a custom table). pick the table from a live dropdown and pass a column/value map.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * Create Record\n * Insert a record into any table (incident, change_request, sys_user, cmdb_ci, or a custom table). Pick the table from a live dropdown and pass a column/value map.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n body: { [key: string]: any }\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/${table}`)\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: authHeader(auth),\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table",
|
||||
"body"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28667,
|
||||
"app": "servicenow",
|
||||
"summary": "Delete Attachment",
|
||||
"description": "Delete an attachment by sys_id. Returns {success, sys_id} (ServiceNow replies 204 No Content).",
|
||||
"terms": "servicenow delete attachment delete an attachment by sys_id. returns {success, sys_id} (servicenow replies 204 no content).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * Delete Attachment\n * Delete an attachment by its sys_id. ServiceNow returns 204 No Content on success, so this returns {success, sys_id}.\n */\nexport async function main(auth: RT.Servicenow, attachment_sys_id: string) {\n const response = await fetch(\n `${auth.instance_url}/api/now/attachment/${attachment_sys_id}`,\n {\n method: \"DELETE\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return { success: true, sys_id: attachment_sys_id }\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"attachment_sys_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"attachment_sys_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28668,
|
||||
"app": "servicenow",
|
||||
"summary": "Delete Record",
|
||||
"description": "Delete a record by sys_id. Returns {success, sys_id} (ServiceNow replies 204 No Content).",
|
||||
"terms": "servicenow delete record delete a record by sys_id. returns {success, sys_id} (servicenow replies 204 no content).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * Delete Record\n * Delete a record by sys_id. ServiceNow returns 204 No Content on success, so this returns {success, sys_id}.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n sys_id: string\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/${table}/${sys_id}`)\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return { success: true, sys_id }\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"sys_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table",
|
||||
"sys_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28670,
|
||||
"app": "servicenow",
|
||||
"summary": "Get Current User",
|
||||
"description": "Return the sys_user record of the authenticated user.",
|
||||
"terms": "servicenow get current user return the sys_user record of the authenticated user.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * Get Current User\n * Return the sys_user record of the authenticated user (resolves current_user, then fetches the full record from sys_user).\n */\nexport async function main(auth: RT.Servicenow) {\n const currentResponse = await fetch(\n `${auth.instance_url}/api/now/ui/user/current_user`,\n {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!currentResponse.ok) {\n throw new Error(`${currentResponse.status} ${await currentResponse.text()}`)\n }\n const { result } = (await currentResponse.json()) as {\n result: { user_sys_id: string }\n }\n\n const userResponse = await fetch(\n `${auth.instance_url}/api/now/table/sys_user/${result.user_sys_id}`,\n {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!userResponse.ok) {\n throw new Error(`${userResponse.status} ${await userResponse.text()}`)\n }\n\n return await userResponse.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28671,
|
||||
"app": "servicenow",
|
||||
"summary": "Get Import Set Result",
|
||||
"description": "Fetch the outcome of a prior import row by the staging-row sys_id returned by Insert Into Import Set (result.sys_id).",
|
||||
"terms": "servicenow get import set result fetch the outcome of a prior import row by the staging-row sys_id returned by insert into import set (result.sys_id).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * Get Import Set Result\n * Fetch the outcome of a prior import row by the staging-row sys_id returned by Insert Into Import Set (result.sys_id), returning the transform result (target table, status, target record link).\n */\nexport async function main(\n auth: RT.Servicenow,\n staging_table: string,\n sys_id: string\n) {\n const url = new URL(\n `${auth.instance_url}/api/now/import/${staging_table}/${sys_id}`\n )\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"staging_table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"sys_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"staging_table",
|
||||
"sys_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28672,
|
||||
"app": "servicenow",
|
||||
"summary": "Get Record",
|
||||
"description": "Retrieve a single record by sys_id. Optionally restrict the returned columns, and choose raw vs. display values.",
|
||||
"terms": "servicenow get record retrieve a single record by sys_id. optionally restrict the returned columns, and choose raw vs. display values.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\nexport type DynMultiselect_fields = string[]\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n// Walk the table's inheritance chain (sys_db_object.super_class) so the column\n// list includes fields inherited from parent tables, not just fields defined on\n// the chosen table (sys_dictionary stores each field under its defining table \u2014\n// e.g. incident extends task, so short_description/priority live on task).\n// Best-effort: any failure falls back to just the table itself.\nasync function tableChain(auth: RT.Servicenow, table: string) {\n const chain: string[] = []\n let current: string | undefined = table\n while (current && !chain.includes(current)) {\n chain.push(current)\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_query=name=${current}&sysparm_fields=super_class.name&sysparm_limit=1`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) break\n const { result } = (await response.json()) as {\n result: { \"super_class.name\"?: string }[]\n }\n current = result[0]?.[\"super_class.name\"] || undefined\n }\n return chain\n}\n\n// Dependent multi-select: the chosen table's columns (sys_dictionary), including\n// columns inherited from parent tables.\nexport async function fields(auth: RT.Servicenow, table: DynSelect_table) {\n if (!table) return []\n const chain = await tableChain(auth, table)\n const url = new URL(`${auth.instance_url}/api/now/table/sys_dictionary`)\n url.searchParams.append(\n \"sysparm_query\",\n `nameIN${chain.join(\",\")}^elementISNOTEMPTY`\n )\n url.searchParams.append(\"sysparm_fields\", \"element,column_label\")\n url.searchParams.append(\"sysparm_limit\", \"10000\")\n\n const response = await fetch(url, {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { element: string; column_label: string }[]\n }\n return result\n .filter((f) => f.element)\n .map((f) => ({\n value: f.element,\n label: `${f.column_label} (${f.element})`,\n }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * Get Record\n * Retrieve a single record by sys_id. Optionally restrict the returned columns, and choose raw vs. display values.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n sys_id: string,\n fields: DynMultiselect_fields | undefined,\n display_value: \"false\" | \"true\" | \"all\" | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/${table}/${sys_id}`)\n if (fields && fields.length > 0) {\n url.searchParams.append(\"sysparm_fields\", fields.join(\",\"))\n }\n if (display_value !== undefined && display_value !== \"\") {\n url.searchParams.append(\"sysparm_display_value\", display_value)\n }\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"display_value": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"false",
|
||||
"true",
|
||||
"all"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynmultiselect-fields",
|
||||
"type": "object"
|
||||
},
|
||||
"sys_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table",
|
||||
"sys_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28673,
|
||||
"app": "servicenow",
|
||||
"summary": "Insert Into Import Set",
|
||||
"description": "Push a row into an import set staging table; the transform map runs synchronously. Inspect result[].status.",
|
||||
"terms": "servicenow insert into import set push a row into an import set staging table; the transform map runs synchronously. inspect result[].status.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * Insert Into Import Set\n * Push a row into an import set staging table; the associated transform map runs synchronously. Inspect result[].status (inserted/updated/ignored/error) \u2014 a transform error returns 200/201 with status \"error\".\n */\nexport async function main(\n auth: RT.Servicenow,\n staging_table: string,\n body: { [key: string]: any }\n) {\n const url = new URL(`${auth.instance_url}/api/now/import/${staging_table}`)\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: authHeader(auth),\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"staging_table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"staging_table",
|
||||
"body"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28674,
|
||||
"app": "servicenow",
|
||||
"summary": "List Attachments",
|
||||
"description": "List attachment metadata, optionally filtered with an encoded query (e.g. table_name=incident^table_sys_id=<id>).",
|
||||
"terms": "servicenow list attachments list attachment metadata, optionally filtered with an encoded query (e.g. table_name=incident^table_sys_id=<id>).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * List Attachments\n * List attachment metadata (sys_id, file_name, content_type, size, download_link). Filter with an encoded query, e.g. table_name=incident^table_sys_id=<record sys_id>.\n */\nexport async function main(\n auth: RT.Servicenow,\n query: string | undefined,\n limit: number | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/attachment`)\n if (query !== undefined && query !== \"\") {\n url.searchParams.append(\"sysparm_query\", query)\n }\n url.searchParams.append(\"sysparm_limit\", String(limit ?? 100))\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"query": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28675,
|
||||
"app": "servicenow",
|
||||
"summary": "List Records",
|
||||
"description": "Query records from any table with an encoded query (sysparm_query), pagination, column selection, and raw/display values.",
|
||||
"terms": "servicenow list records query records from any table with an encoded query (sysparm_query), pagination, column selection, and raw/display values.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\nexport type DynMultiselect_fields = string[]\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n// Walk the table's inheritance chain (sys_db_object.super_class) so the column\n// list includes fields inherited from parent tables, not just fields defined on\n// the chosen table (sys_dictionary stores each field under its defining table \u2014\n// e.g. incident extends task, so short_description/priority live on task).\n// Best-effort: any failure falls back to just the table itself.\nasync function tableChain(auth: RT.Servicenow, table: string) {\n const chain: string[] = []\n let current: string | undefined = table\n while (current && !chain.includes(current)) {\n chain.push(current)\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_query=name=${current}&sysparm_fields=super_class.name&sysparm_limit=1`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) break\n const { result } = (await response.json()) as {\n result: { \"super_class.name\"?: string }[]\n }\n current = result[0]?.[\"super_class.name\"] || undefined\n }\n return chain\n}\n\n// Dependent multi-select: the chosen table's columns (sys_dictionary), including\n// columns inherited from parent tables.\nexport async function fields(auth: RT.Servicenow, table: DynSelect_table) {\n if (!table) return []\n const chain = await tableChain(auth, table)\n const url = new URL(`${auth.instance_url}/api/now/table/sys_dictionary`)\n url.searchParams.append(\n \"sysparm_query\",\n `nameIN${chain.join(\",\")}^elementISNOTEMPTY`\n )\n url.searchParams.append(\"sysparm_fields\", \"element,column_label\")\n url.searchParams.append(\"sysparm_limit\", \"10000\")\n\n const response = await fetch(url, {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { element: string; column_label: string }[]\n }\n return result\n .filter((f) => f.element)\n .map((f) => ({\n value: f.element,\n label: `${f.column_label} (${f.element})`,\n }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * List Records\n * Query records from any table with an encoded query (sysparm_query), pagination, column selection, and raw/display values. Returns one page; iterate offset until fewer than limit rows return.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n query: string | undefined,\n limit: number | undefined,\n offset: number | undefined,\n fields: DynMultiselect_fields | undefined,\n display_value: \"false\" | \"true\" | \"all\" | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/${table}`)\n if (query !== undefined && query !== \"\") {\n url.searchParams.append(\"sysparm_query\", query)\n }\n url.searchParams.append(\"sysparm_limit\", String(limit ?? 100))\n if (offset !== undefined) {\n url.searchParams.append(\"sysparm_offset\", String(offset))\n }\n if (fields && fields.length > 0) {\n url.searchParams.append(\"sysparm_fields\", fields.join(\",\"))\n }\n if (display_value !== undefined && display_value !== \"\") {\n url.searchParams.append(\"sysparm_display_value\", display_value)\n }\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"display_value": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"false",
|
||||
"true",
|
||||
"all"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynmultiselect-fields",
|
||||
"type": "object"
|
||||
},
|
||||
"limit": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"offset": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "number"
|
||||
},
|
||||
"query": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28676,
|
||||
"app": "servicenow",
|
||||
"summary": "List Tables",
|
||||
"description": "List the instance tables (sys_db_object) with API name, label, and sys_id. Optionally filter by a name/label substring.",
|
||||
"terms": "servicenow list tables list the instance tables (sys_db_object) with api name, label, and sys_id. optionally filter by a name/label substring.",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n/**\n * List Tables\n * List the instance's tables (sys_db_object) with their API name, label, and sys_id. Optionally filter by a substring matched against name or label.\n */\nexport async function main(\n auth: RT.Servicenow,\n name_filter: string | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/sys_db_object`)\n url.searchParams.append(\"sysparm_fields\", \"name,label,sys_id\")\n if (name_filter !== undefined && name_filter !== \"\") {\n url.searchParams.append(\n \"sysparm_query\",\n `nameLIKE${name_filter}^ORlabelLIKE${name_filter}`\n )\n }\n url.searchParams.append(\"sysparm_limit\", \"10000\")\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"name_filter": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28677,
|
||||
"app": "servicenow",
|
||||
"summary": "Update Record",
|
||||
"description": "Update a record by sys_id with a column/value map. PATCH (partial) by default; set replace=true to PUT (full overwrite).",
|
||||
"terms": "servicenow update record update a record by sys_id with a column/value map. patch (partial) by default; set replace=true to put (full overwrite).",
|
||||
"kind": "script",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nexport type DynSelect_table = string\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * Update Record\n * Update a record by sys_id with a column/value map. PATCH (partial update) by default; set replace=true to PUT (full overwrite that clears omitted fields).\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n sys_id: string,\n body: { [key: string]: any },\n replace: boolean | undefined\n) {\n const url = new URL(`${auth.instance_url}/api/now/table/${table}/${sys_id}`)\n\n const response = await fetch(url, {\n method: replace ? \"PUT\" : \"PATCH\",\n headers: {\n Authorization: authHeader(auth),\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(body),\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n return await response.json()\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"body": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
},
|
||||
"replace": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sys_id": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table",
|
||||
"sys_id",
|
||||
"body"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_id": 28679,
|
||||
"app": "servicenow",
|
||||
"summary": "New or Updated Record",
|
||||
"description": "Emits records whose timestamp is newer than the last poll, tracked via Windmill state. Defaults to sys_updated_on.",
|
||||
"terms": "servicenow new or updated record emits records whose timestamp is newer than the last poll, tracked via windmill state. defaults to sys_updated_on.",
|
||||
"kind": "trigger",
|
||||
"language": "bunnative",
|
||||
"content": "//native\n\nimport * as wmill from \"windmill-client\"\n\nexport type DynSelect_table = string\n\nfunction authHeader(auth: RT.Servicenow) {\n return auth.token\n ? `Bearer ${auth.token}`\n : `Basic ${btoa(`${auth.username}:${auth.password}`)}`\n}\n\n// Dropdown of the instance's tables (sys_db_object).\nexport async function table(auth: RT.Servicenow) {\n const response = await fetch(\n `${auth.instance_url}/api/now/table/sys_db_object?sysparm_fields=name,label&sysparm_limit=10000`,\n {\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n }\n )\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n const { result } = (await response.json()) as {\n result: { name: string; label: string }[]\n }\n return result\n .filter((t) => t.name)\n .map((t) => ({ value: t.name, label: `${t.label} (${t.name})` }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\n/**\n * New or Updated Record\n * Emits records from a table whose timestamp is newer than the last poll, tracked via Windmill state. Defaults to sys_updated_on (set timestamp_field to sys_created_on for new-only). Optionally narrow with an extra encoded query.\n */\nexport async function main(\n auth: RT.Servicenow,\n table: DynSelect_table,\n timestamp_field: \"sys_updated_on\" | \"sys_created_on\" | undefined,\n query: string | undefined\n) {\n const tsField =\n timestamp_field && timestamp_field !== \"\"\n ? timestamp_field\n : \"sys_updated_on\"\n const lastChecked: string | undefined = await wmill.getState()\n\n // ServiceNow datetimes are 'YYYY-MM-DD HH:MM:SS' in UTC (no trailing Z).\n const nowUtc = new Date().toISOString().slice(0, 19).replace(\"T\", \" \")\n\n // First run: set the watermark to now and don't emit a backlog.\n if (!lastChecked) {\n await wmill.setState(nowUtc)\n return []\n }\n\n let encoded = `${tsField}>${lastChecked}`\n if (query !== undefined && query !== \"\") {\n encoded += `^${query}`\n }\n encoded += `^ORDERBY${tsField}`\n\n const url = new URL(`${auth.instance_url}/api/now/table/${table}`)\n url.searchParams.append(\"sysparm_query\", encoded)\n url.searchParams.append(\"sysparm_limit\", \"200\")\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: authHeader(auth),\n Accept: \"application/json\",\n },\n })\n\n if (!response.ok) {\n throw new Error(`${response.status} ${await response.text()}`)\n }\n\n const { result } = (await response.json()) as {\n result: Array<{ [key: string]: any }>\n }\n\n if (result.length > 0) {\n await wmill.setState(result[result.length - 1][tsField])\n }\n\n return result\n}\n",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"auth": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "resource-servicenow",
|
||||
"type": "object"
|
||||
},
|
||||
"query": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"originalType": "string",
|
||||
"type": "string"
|
||||
},
|
||||
"table": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"format": "dynselect-table",
|
||||
"type": "object"
|
||||
},
|
||||
"timestamp_field": {
|
||||
"default": null,
|
||||
"description": "",
|
||||
"enum": [
|
||||
"sys_updated_on",
|
||||
"sys_created_on"
|
||||
],
|
||||
"originalType": "enum",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auth",
|
||||
"table"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -17,8 +17,12 @@ import type { BenchmarkArtifactFile, CliTrace, ModeRunner } from "../core/types"
|
||||
|
||||
const IGNORE_WORKSPACE_FILES = new Set([
|
||||
".claude",
|
||||
".agents",
|
||||
"AGENTS.md",
|
||||
"AGENTS.wmill.md",
|
||||
"CLAUDE.md",
|
||||
// the guidance has the agent create one for every new folder
|
||||
"folder.meta.yaml",
|
||||
"rt.d.ts",
|
||||
".wmill-benchmark-bin",
|
||||
".wmill-benchmark-wmill-invocations.log",
|
||||
@@ -89,7 +93,7 @@ export function createCliModeRunner(
|
||||
const run = await runPromptAndCapture(
|
||||
renderedPrompt,
|
||||
workspaceDir,
|
||||
context.evalCase?.runtime?.maxTurns ?? 6,
|
||||
context.evalCase?.runtime?.maxTurns ?? 12,
|
||||
modelConfig
|
||||
);
|
||||
const workspaceFiles = await readDirectoryFiles(workspaceDir, { ignore: IGNORE_WORKSPACE_FILES });
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"test:frontend-graph": "cd ../frontend && node_modules/.bin/vitest run --project server --config ../ai_evals/adapters/frontend/vitest.unit.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.25",
|
||||
"@anthropic-ai/sdk": "^0.39.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.284",
|
||||
"@anthropic-ai/sdk": "^0.129.0",
|
||||
"commander": "^14.0.3",
|
||||
"openai": "^6.9.1",
|
||||
"yaml": "^2.8.3"
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COALESCE((operator_settings->>'builder_flows')::boolean, false) AS \"flows!\",\n COALESCE((operator_settings->>'manage_schedules')::boolean, true) AS \"schedules!\",\n COALESCE((operator_settings->>'manage_triggers')::boolean, true) AS \"triggers!\"\n FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "flows!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "schedules!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "triggers!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "094a07eaa5714ec739786717f110ea539e84c33de23476e042a4a1a7cb529dac"
|
||||
}
|
||||
+6
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n WITH job_info AS (\n SELECT id, kind::text AS kind, parent_job\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2\n )\n SELECT\n q.id AS \"id!\",\n s.flow_status,\n q.suspend AS \"suspend!\",\n j.runnable_path AS script_path,\n j.permissioned_as_email AS email,\n (ji.kind IN ('flow', 'flowpreview', 'singlestepflow')) AS \"is_flow_level!\",\n (ji.kind NOT IN ('flow', 'flowpreview', 'singlestepflow') AND q.id = ji.id) AS \"is_wac!\"\n FROM job_info ji\n JOIN v2_job_queue q ON q.id = CASE\n WHEN ji.kind IN ('flow', 'flowpreview', 'singlestepflow') THEN ji.id\n ELSE COALESCE(ji.parent_job, ji.id)\n END\n JOIN v2_job j ON j.id = q.id\n LEFT JOIN v2_job_status s ON s.id = q.id\n FOR UPDATE OF q\n ",
|
||||
"query": "\n WITH job_info AS (\n SELECT id, kind::text AS kind, parent_job\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2\n )\n SELECT\n q.id AS \"id!\",\n q.suspend AS \"suspend!\",\n j.runnable_path AS script_path,\n j.permissioned_as_email AS email,\n (ji.kind IN ('flow', 'flowpreview', 'singlestepflow')) AS \"is_flow_level!\",\n (ji.kind NOT IN ('flow', 'flowpreview', 'singlestepflow') AND q.id = ji.id) AS \"is_wac!\"\n FROM job_info ji\n JOIN v2_job_queue q ON q.id = CASE\n WHEN ji.kind IN ('flow', 'flowpreview', 'singlestepflow') THEN ji.id\n ELSE COALESCE(ji.parent_job, ji.id)\n END\n JOIN v2_job j ON j.id = q.id\n FOR UPDATE OF q\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,31 +10,26 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "flow_status",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "suspend!",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 2,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 3,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 4,
|
||||
"name": "is_flow_level!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 5,
|
||||
"name": "is_wac!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -47,7 +42,6 @@
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
@@ -55,5 +49,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "11b5d006acf040b2461b9539863b1e511c23e1a5f9be97da066a6808a49f2092"
|
||||
"hash": "0a18205b64e68529b7f90bc1c5db2cbea9ee5eaa962970b65a92597fd5e62707"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE descendants AS (\n SELECT id FROM v2_job WHERE parent_job = $1 AND workspace_id = $2\n UNION ALL\n SELECT j.id FROM v2_job j JOIN descendants d ON j.parent_job = d.id\n WHERE j.workspace_id = $2\n ) SELECT d.id AS \"id!\" FROM descendants d JOIN v2_job_queue q ON q.id = d.id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "0c2bf6925de6dd4d9f8d47d7fdd9e7801129570106b5acd49c0f708398606771"
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n schedule.path, t.jobs FROM schedule,\n LATERAL(SELECT ARRAY(\n SELECT json_build_object('id', id, 'success', status = 'success', 'duration_ms', duration_ms)\n FROM v2_job_completed c JOIN v2_job j USING (id)\n WHERE trigger_kind = 'schedule'\n AND trigger = schedule.path\n AND c.workspace_id = $1\n AND j.workspace_id = $1\n AND parent_job IS NULL AND runnable_path = schedule.script_path\n AND status <> 'skipped'\n ORDER BY completed_at DESC\n LIMIT 20\n ) AS jobs) t\n WHERE workspace_id = $1 AND NOT starts_with(schedule.path, $4)\n ORDER BY edited_at DESC\n LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "jobs",
|
||||
"type_info": "JsonArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "0f141ca6a58901dee1ddcf25694d76a4e5702c0324a27ef3a5740ef5d98d8946"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE up AS (\n SELECT id, parent_job, kind, runnable_id, runnable_path, 0 AS depth\n FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT p.id, p.parent_job, p.kind, p.runnable_id, p.runnable_path, u.depth + 1\n FROM v2_job p JOIN up u ON p.id = u.parent_job\n WHERE p.workspace_id = $2 AND u.kind <> 'flow' AND u.depth < 100\n )\n SELECT EXISTS (SELECT 1 FROM up WHERE kind = 'flow'\n AND runnable_id = (SELECT f.versions[array_upper(f.versions, 1)]\n FROM flow f WHERE f.path = up.runnable_path AND f.workspace_id = $2))\n AND NOT EXISTS (SELECT 1 FROM up WHERE kind <> 'flow' AND (\n EXISTS (SELECT 1 FROM v2_job_completed c WHERE c.id = up.id\n AND jsonb_typeof(c.flow_status->'restarted_from') = 'object')\n OR EXISTS (SELECT 1 FROM v2_job_status st WHERE st.id = up.id\n AND jsonb_typeof(st.flow_status->'restarted_from') = 'object')))\n AS \"current!\"",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "current!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "141725e44ccd3f9cc0b5eb070ea3de94b89a4940901e11c48c5f2014ff28086a"
|
||||
}
|
||||
+9
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email, permissioned_as,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip, labels\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10, $11,\n $12, $13, $14, $15,\n $16, $17, $18,\n $19, $20,\n $21, $22, $23, $24,\n $25, $26, $27, $28, $29, $30\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email, permissioned_as,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip, labels\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10, $11,\n $12, $13, $14, $15,\n $16, $17, $18,\n $19, $20,\n $21, $22, $23, $24,\n $25, $26, $27, $28, $29, $30\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels,\n late_run_streak\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -167,6 +167,11 @@
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -236,8 +241,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2effe885aafaa0c5fc1f969b8c6ffbd38c53fb586251ba780b75a007e5a47ed6"
|
||||
"hash": "1c51aab269efb558640ea502e63ebed4c3d8b025947becd1c614757e7e29a5d8"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT restart_unless_cancelled FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "restart_unless_cancelled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1d27895aa42ccbb542479b19baefd62790205b529ab0d8af36f18c470e8bb838"
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT 'schedule' AS \"kind!\", COUNT(*)::BIGINT AS \"count!\" FROM schedule\n UNION ALL SELECT 'http', COUNT(*)::BIGINT FROM http_trigger\n UNION ALL SELECT 'websocket', COUNT(*)::BIGINT FROM websocket_trigger\n UNION ALL SELECT 'kafka', COUNT(*)::BIGINT FROM kafka_trigger\n UNION ALL SELECT 'nats', COUNT(*)::BIGINT FROM nats_trigger\n UNION ALL SELECT 'postgres', COUNT(*)::BIGINT FROM postgres_trigger\n UNION ALL SELECT 'mqtt', COUNT(*)::BIGINT FROM mqtt_trigger\n UNION ALL SELECT 'sqs', COUNT(*)::BIGINT FROM sqs_trigger\n UNION ALL SELECT 'gcp', COUNT(*)::BIGINT FROM gcp_trigger\n UNION ALL SELECT 'azure', COUNT(*)::BIGINT FROM azure_trigger\n UNION ALL SELECT 'amqp', COUNT(*)::BIGINT FROM amqp_trigger\n UNION ALL SELECT 'email', COUNT(*)::BIGINT FROM email_trigger\n -- Grouped, not a single 'native' key: these fire as nextcloud/google/github,\n -- so a lone key would not line up with the `trigger`/`fired` series.\n UNION ALL SELECT service_name::text, COUNT(*)::BIGINT FROM native_trigger GROUP BY service_name\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "count!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1d346a14ad5586af347b8e7ac413500a39efa20e4915ffa56fd40537597db36e"
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE lineage AS (\n SELECT id, parent_job, kind, runnable_path, runnable_id, trigger_kind, trigger,\n 0 AS depth\n FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT p.id, p.parent_job, p.kind, p.runnable_path, p.runnable_id, p.trigger_kind,\n p.trigger, l.depth + 1\n FROM v2_job p JOIN lineage l ON p.id = l.parent_job\n WHERE p.workspace_id = $2 AND l.depth < 100\n )\n SELECT id AS \"id!\", parent_job, kind AS \"kind!: JobKind\", runnable_path,\n trigger_kind::text AS trigger_kind,\n -- A restart takes its flow version from the request, so a trusted flow path\n -- can carry another flow's code: the version must belong to that path.\n CASE kind\n WHEN 'flow' THEN EXISTS (SELECT 1 FROM flow_version fv\n WHERE fv.id = runnable_id AND fv.path = runnable_path AND fv.workspace_id = $2)\n WHEN 'script' THEN EXISTS (SELECT 1 FROM script s\n WHERE s.hash = runnable_id AND s.path = runnable_path AND s.workspace_id = $2)\n -- An app editor preview also runs an app script, at an app path it does not\n -- have to own; only deployed-app runs are stamped with their app.\n WHEN 'appscript' THEN COALESCE(trigger_kind = 'app'\n AND starts_with(runnable_path, trigger || '/'), false)\n ELSE true\n END AS \"origin_verified!\"\n FROM lineage ORDER BY depth",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "parent_job",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "kind!: JobKind",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlestepflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript",
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "runnable_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "trigger_kind",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "origin_verified!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "201c4d014dd71da5a38ba832f836ca71b0fe6dbee8645449e97517aeb4ef9655"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM script WHERE workspace_id = $1 AND path = $2 AND hash = $3)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "298e0e30afdc973c517a9b4ea99f5dd1616a62c6ecbaaea312c686038d21e6fd"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT c.id AS \"id!\", u.job_id AS \"job_id!\", u.created_seq AS \"user_seq!\"\n FROM unnest($1::uuid[]) AS c(id)\n CROSS JOIN LATERAL (\n SELECT job_id, created_seq\n FROM flow_conversation_message\n WHERE conversation_id = c.id AND message_type = 'user'\n ORDER BY created_seq DESC\n LIMIT 1\n ) u\n WHERE u.job_id IS NOT NULL\n AND EXISTS (SELECT 1 FROM v2_job_queue q WHERE q.id = u.job_id)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "job_id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "user_seq!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2bf039a2880555c5e6eec9a29c4a380aa832d72d2aaf5b109ee9554f1e9da6a4"
|
||||
}
|
||||
+8
-2
@@ -1,12 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM script WHERE workspace_id = $1 AND path = ANY($2) RETURNING path",
|
||||
"query": "DELETE FROM script WHERE workspace_id = $1 AND path = ANY($2) RETURNING path, hash",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "hash",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -16,8 +21,9 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "819c4d78f10979a83529155c3f101f42f6958742bc68ab384944fa3c5cba094c"
|
||||
"hash": "2d73fe76e45becbcae7a9d4384bbb2f9739cab232b8f3e357e27dc32bb35c3a8"
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n schedule.path, t.jobs, schedule.late_run_streak, schedule.missed_occurrences, schedule.last_missed_at FROM schedule,\n LATERAL(SELECT ARRAY(\n SELECT json_build_object('id', id, 'success', status = 'success', 'duration_ms', duration_ms)\n FROM v2_job_completed c JOIN v2_job j USING (id)\n WHERE trigger_kind = 'schedule'\n AND trigger = schedule.path\n AND c.workspace_id = $1\n AND j.workspace_id = $1\n AND parent_job IS NULL AND runnable_path = schedule.script_path\n AND status <> 'skipped'\n ORDER BY completed_at DESC\n LIMIT 20\n ) AS jobs) t\n WHERE workspace_id = $1 AND NOT starts_with(schedule.path, $4)\n ORDER BY edited_at DESC\n LIMIT $2 OFFSET $3",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "jobs",
|
||||
"type_info": "JsonArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "missed_occurrences",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "last_missed_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "39793465641124401a732744777bc3553985b757b48962e9976b5da5b812bd05"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM flow WHERE workspace_id = $1 AND path = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3dccc8e745f4a0973541088f66172e74af6828c1ab52cf7a6fd10b305deade85"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET started_at = now() WHERE id = $1 RETURNING started_at",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "started_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "451f303f4a24d848ba4ccdc2441ca7cb85bdfc66110b6534260f8642f91f006a"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE schedule SET late_run_streak = 0\n WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "466b2c68074202146da10d4b0757128fa332a52e3b451aac307234d02a51de8f"
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json<HashMap<String, Box<RawValue>>>\",\n j.tag AS \"tag!\", j.priority, j.kind AS \"kind!: JobKind\"\n FROM v2_job j\n WHERE j.id = $1 and j.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "args: sqlx::types::Json<HashMap<String, Box<RawValue>>>",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "tag!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "priority",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "kind!: JobKind",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlestepflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript",
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4a257470121ee6bffd9c68e88aabb7563c0ba31233854c8922f9449da7b43e9e"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT late_run_streak FROM schedule WHERE workspace_id = $1 AND path = $2 FOR SHARE",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4a6fb702794ce250a8217034ae9b0b719f61fe0cb8a7a2c76be4b7422a7138a0"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM script WHERE workspace_id = $1 AND path = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "4bc47050c74a02ab3169c3165898b2af07e995de71564d867b171f97f719fbde"
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT c.id IS NOT NULL AS \"completed!\",\n c.result AS \"result: sqlx::types::Json<Box<RawValue>>\",\n c.status = 'success' AS \"success\",\n EXISTS(SELECT 1 FROM v2_job_queue WHERE id = $1) AS \"queued!\"\n FROM (SELECT 1) one\n LEFT JOIN v2_job_completed c ON c.id = $1 AND c.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "completed!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "result: sqlx::types::Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "success",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "queued!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "52f9582423689f098a337f88341239a136ee5132bc97f4b06cc1ee5beaa621e5"
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT bool_and(operator) FROM (\n SELECT operator FROM usr WHERE email = $1 AND is_service_account IS false\n UNION ALL\n SELECT operator FROM workspace_invite WHERE email = $1\n ) t",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "bool_and",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5312b8db714139a94d7ff1c0794af063c36ac17e9d331cd9980b91b28d713c72"
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET canceled_by = $1, canceled_reason = $2, scheduled_for = now(), suspend = 0 WHERE id = $3 AND workspace_id = $4 AND canceled_by IS NULL RETURNING id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "611dd43eb1b629db860c1b477444cb3dadcbf8058a24883bc22f143762c5ea3d"
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE schedule SET late_run_streak = late_run_streak + 1,\n missed_occurrences = CASE WHEN late_run_streak = 0 THEN $3\n ELSE missed_occurrences + $3 END,\n last_missed_at = $4\n WHERE workspace_id = $1 AND path = $2\n RETURNING late_run_streak, missed_occurrences",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "missed_occurrences",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Int4",
|
||||
"Timestamptz"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "640482b37fe2aa63a5082250170406bcbe888ddece7dec3330778591573031fb"
|
||||
}
|
||||
+9
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", schema AS \"schema: String\", schema_validation AS \"schema_validation: bool\", codebase LIKE '%.tar' as use_tar, codebase LIKE '%.esm%' as is_esm, modules AS \"modules: serde_json::Value\" FROM script WHERE hash = $1 LIMIT 1",
|
||||
"query": "SELECT content AS \"content!: String\",\n lock AS \"lock: String\", language AS \"language: Option<ScriptLang>\", envs AS \"envs: Vec<String>\", schema AS \"schema: String\", schema_validation AS \"schema_validation: bool\", codebase LIKE '%.tar' as use_tar, codebase LIKE '%.esm%' as is_esm, modules AS \"modules: serde_json::Value\", deleted FROM script WHERE hash = $1 ORDER BY deleted LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -80,6 +80,11 @@
|
||||
"ordinal": 8,
|
||||
"name": "modules: serde_json::Value",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "deleted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -96,8 +101,9 @@
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b44afcf1b9c047ac525638f0952c2cb01d65b1b46693331ac157dfca0dab6824"
|
||||
"hash": "65e33a933ed8568c82f9b4e121b9ba4280123fe22a959d4d3d978ca767aeed29"
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, name, enabled, pwd FROM datatable_role",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "pwd",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "71ee2cb6661cca1fa4d8874a7f6d368347c59f36fd87df6dc7996152ccb84af0"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, status = 'success' AS \"success!\", result AS \"result: Json<Box<RawValue>>\"\n FROM v2_job_completed WHERE workspace_id = $1 AND id = ANY($2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "success!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "result: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"UuidArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "789c3f6d29f46fcb17a22fe97405a1754ccb8ddeb08c23795dfe14e9f332e9dc"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE chain(id, parent_job) AS (\n SELECT id, parent_job FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT j.id, j.parent_job FROM v2_job j\n JOIN chain c ON j.id = c.parent_job AND j.workspace_id = $2\n )\n SELECT\n CASE WHEN a.agent THEN regexp_replace(j.runnable_path, '\\.chat$', '')\n ELSE j.runnable_path END AS runnable_path,\n CASE\n WHEN a.agent THEN 'agents'\n WHEN j.kind IN ('script', 'script_hub', 'unassigned_script') THEN 'scripts'\n WHEN j.kind IN ('flow', 'unassigned_flow') THEN 'flows'\n WHEN j.kind IN ('singlestepflow', 'unassigned_singlestepflow') THEN\n CASE WHEN COALESCE(\n (SELECT m->'value'->>'type'\n FROM jsonb_array_elements(j.raw_flow->'modules') m\n WHERE m->>'id' IN ('a', 'main')\n LIMIT 1),\n 'script'\n ) = 'flow' THEN 'flows' ELSE 'scripts' END\n END AS scope_kind,\n CASE WHEN j.trigger_kind = 'app' THEN j.trigger END AS launched_by_app\n FROM v2_job j JOIN chain c ON c.id = j.id,\n LATERAL (SELECT j.kind = 'flowpreview'\n AND j.raw_flow->'modules'->1 IS NULL\n AND j.raw_flow->'modules'->0->>'id' = '__wm_agent_root' AS agent) a\n WHERE j.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "runnable_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "scope_kind",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "launched_by_app",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "7904ac35748b1de2c7ff764f22787436d0fe74f99f298e5c399233f0d247f2ec"
|
||||
}
|
||||
+9
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n edited_at = now(),\n permissioned_as = $26,\n labels = COALESCE($27, labels)\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n edited_at = now(),\n permissioned_as = $26,\n labels = COALESCE($27, labels),\n late_run_streak = 0,\n missed_occurrences = 0,\n last_missed_at = NULL\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels,\n late_run_streak\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -167,6 +167,11 @@
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -233,8 +238,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4d2c91937d87d385290b918af0238825cdd7784d0d611cd0069b15d11039ac56"
|
||||
"hash": "793810c58d9e872da52d478db61c0a20126a207397cb9ed2dc18cd0b2baa57fa"
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT ws.workspace_id AS \"workspace_id!\", dt.key AS \"datatable!\"\n FROM workspace_settings ws\n JOIN workspace w ON w.id = ws.workspace_id AND w.deleted = false\n CROSS JOIN LATERAL jsonb_each(COALESCE(ws.datatable->'datatables', '{}'::jsonb)) dt\n WHERE ws.workspace_id <> $1\n AND dt.value->'database'->>'resource_type' = 'instance'\n AND dt.value->'database'->>'resource_path' = $2\n ORDER BY ws.workspace_id, dt.key\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "datatable!",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "79799b5a2e499df6c28e286c42b9ad2db940c2455ab19cc95e5198baf96d5629"
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET suspend = $1 WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "84ad6f24aa9ccdc6f6e705b7de1bb707637a9bb1475711f9f9f3f2a6e28cb169"
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE chain(id, parent_job) AS (\n SELECT id, parent_job FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT j.id, j.parent_job FROM v2_job j\n JOIN chain c ON j.id = c.parent_job AND j.workspace_id = $2\n )\n SELECT j.runnable_path,\n CASE\n WHEN j.kind IN ('script', 'script_hub', 'unassigned_script') THEN 'scripts'\n WHEN j.kind IN ('flow', 'unassigned_flow') THEN 'flows'\n WHEN j.kind IN ('singlestepflow', 'unassigned_singlestepflow') THEN\n CASE WHEN COALESCE(\n (SELECT m->'value'->>'type'\n FROM jsonb_array_elements(j.raw_flow->'modules') m\n WHERE m->>'id' IN ('a', 'main')\n LIMIT 1),\n 'script'\n ) = 'flow' THEN 'flows' ELSE 'scripts' END\n END AS scope_kind,\n CASE WHEN j.trigger_kind = 'app' THEN j.trigger END AS launched_by_app\n FROM v2_job j JOIN chain c ON c.id = j.id\n WHERE j.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "runnable_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "scope_kind",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "launched_by_app",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "84fcddaf5bc61d607a6e6e5e31de7436b203a3baa7ef0509cb8e6c52270ae3a9"
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO datatable_role (id, name, enabled, pwd) VALUES ($1, $2, $3, $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "86af9d51a158ea5cb6161461ecddf2a63695f8cbf8af648da5a0a77a5b9d02ba"
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT 'schedule' AS \"kind!\", COUNT(*)::BIGINT AS \"count!\", 0::BIGINT AS \"suspended!\" FROM schedule\n UNION ALL SELECT 'http', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM http_trigger\n UNION ALL SELECT 'websocket', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM websocket_trigger\n UNION ALL SELECT 'kafka', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM kafka_trigger\n UNION ALL SELECT 'nats', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM nats_trigger\n UNION ALL SELECT 'postgres', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM postgres_trigger\n UNION ALL SELECT 'mqtt', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM mqtt_trigger\n UNION ALL SELECT 'sqs', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM sqs_trigger\n UNION ALL SELECT 'gcp', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM gcp_trigger\n UNION ALL SELECT 'azure', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM azure_trigger\n UNION ALL SELECT 'amqp', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM amqp_trigger\n UNION ALL SELECT 'email', COUNT(*)::BIGINT, COUNT(*) FILTER (WHERE mode = 'suspended')::BIGINT FROM email_trigger\n -- Grouped, not a single 'native' key: these fire as nextcloud/google/github,\n -- so a lone key would not line up with the `trigger`/`fired` series.\n UNION ALL SELECT service_name::text, COUNT(*)::BIGINT, 0::BIGINT FROM native_trigger GROUP BY service_name\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "kind!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "count!",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "suspended!",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "872c06b3098cf59797bcaa90d6da5c6acbf5468c190fa72b6b6707d950267752"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET suspend = GREATEST(suspend - 1, 0) WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a35164456ade8e79cb8f5418c8fe82c45be45409f881292f0d4a3316362ba1f4"
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT jsonb_object_keys(value->'databases') FROM global_settings\n WHERE name = 'custom_instance_pg_databases'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "jsonb_object_keys",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "b9842d2d8abf382bd82d8fa1de012373638be391f884f81dc387ffc465badac6"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT NOT starts_with(COALESCE(label, ''), 'impersonation:') FROM token\n WHERE token_hash = $1 AND (expiration IS NULL OR expiration > now())",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "be004608fc1de826803f266dfce96e09e634977478443b6372bca5e3dd0ce5ff"
|
||||
}
|
||||
+9
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels\n ",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2,\n late_run_streak = 0,\n missed_occurrences = 0,\n last_missed_at = NULL\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip,\n labels,\n late_run_streak\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -167,6 +167,11 @@
|
||||
"ordinal": 32,
|
||||
"name": "labels",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"name": "late_run_streak",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -210,8 +215,9 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "642ca096c1c151fc01445593c8d2ed879a9a7be6ff044a9ce1b581ba725665fe"
|
||||
"hash": "ca54e732d521416cb2692c8c4406c63922f0eebcba2b2cefc4290c4baee5c071"
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH active_users as (SELECT distinct username as email FROM audit_partitioned WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh') AND username NOT IN (SELECT email FROM usr WHERE is_service_account)),\n active_authors as (SELECT distinct t.email FROM usr t LEFT JOIN workspace_settings ws ON ws.workspace_id = t.workspace_id WHERE NOT (t.operator AND NOT COALESCE((ws.operator_settings->>'builder_flows')::boolean, false)) AND t.email IN (SELECT email FROM active_users)),\n active_authors_agg as (SELECT array_agg(email) as authors FROM active_authors),\n active_ops_agg as (SELECT array_agg(email) as operators from active_users WHERE email NOT IN (SELECT email FROM active_authors))\n SELECT active_authors_agg.authors, active_ops_agg.operators, array_length(active_authors_agg.authors, 1) as author_count, array_length(active_ops_agg.operators, 1) as operator_count FROM active_authors_agg, active_ops_agg",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "authors",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "operators",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "author_count",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "operator_count",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cbc521b505c3953dc624ae6cefe6999e6d0c732da83ebc74ef7678cacd22ad6b"
|
||||
}
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11, native_mode = $12, ip = COALESCE($13, ip) WHERE worker = $6",
|
||||
"query": "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1, custom_tags = $2,\n occupancy_rate = $3, memory_usage = $4, wm_memory_usage = $5, vcpus = COALESCE($7, vcpus),\n memory = COALESCE($8, memory), occupancy_rate_15s = $9, occupancy_rate_5m = $10, occupancy_rate_30m = $11, native_mode = $12, ip = COALESCE($13, ip),\n current_job_id = COALESCE($14, current_job_id), current_job_workspace_id = COALESCE($15, current_job_workspace_id) WHERE worker = $6",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,10 +17,12 @@
|
||||
"Float4",
|
||||
"Float4",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Uuid",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0c18351237816fe0c56e23801fcb8e70dbffcf08ed121e55c871f727c4ddf626"
|
||||
"hash": "d267e03115ee74ab7ca7edd8a010e424819133ce38dab7300a5fca4c6f54536d"
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT dt.key AS \"datatable!\"\n FROM workspace_settings ws\n CROSS JOIN LATERAL jsonb_each(COALESCE(ws.datatable->'datatables', '{}'::jsonb)) dt\n WHERE ws.workspace_id = $1\n AND dt.key <> $2\n AND NOT dt.value ? 'permissions'\n AND dt.value->'database'->>'resource_type' = 'instance'\n AND dt.value->'database'->>'resource_path' = $3\n ORDER BY dt.key",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "datatable!",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d48ca62c86b1af7a9dd2450c1c28dc45020a2a553d8874c49f9eafedea5a9d40"
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH active_users as (SELECT distinct username as email FROM audit_partitioned WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login' OR operation = 'users.token.refresh')),\n active_authors as (SELECT distinct email FROM usr WHERE usr.operator IS false AND email IN (SELECT email FROM active_users)),\n active_authors_agg as (SELECT array_agg(email) as authors FROM active_authors),\n active_ops_agg as (SELECT array_agg(email) as operators from active_users WHERE email NOT IN (SELECT email FROM active_authors))\n SELECT active_authors_agg.authors, active_ops_agg.operators, array_length(active_authors_agg.authors, 1) as author_count, array_length(active_ops_agg.operators, 1) as operator_count FROM active_authors_agg, active_ops_agg",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "authors",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "operators",
|
||||
"type_info": "VarcharArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "author_count",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "operator_count",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "dc37f9ad685a2f2bd781be8678e0548a860815f30e866362344434f23aa7c83c"
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT q.id AS \"id!\", j.created_by, j.permissioned_as, j.permissioned_as_email, j.trigger, j.trigger_kind AS \"trigger_kind: TriggerKindLabel\", j.preprocessed, j.args AS \"args: sqlx::types::Json<HashMap<String, Box<RawValue>>>\" FROM v2_job_queue q JOIN v2_job j USING (id) JOIN script s ON s.workspace_id = j.workspace_id AND s.hash = j.runnable_id WHERE j.workspace_id = $1 AND j.runnable_path = $2 AND j.kind = 'script' AND j.flow_step_id IS NULL AND j.runnable_id != $3 AND q.canceled_by IS NULL AND s.restart_unless_cancelled",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "permissioned_as_email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "trigger",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "trigger_kind: TriggerKindLabel",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_trigger_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"webhook",
|
||||
"http",
|
||||
"websocket",
|
||||
"kafka",
|
||||
"email",
|
||||
"nats",
|
||||
"schedule",
|
||||
"app",
|
||||
"ui",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"gcp",
|
||||
"mqtt",
|
||||
"nextcloud",
|
||||
"google",
|
||||
"ci_test",
|
||||
"github",
|
||||
"azure",
|
||||
"asset",
|
||||
"freshness",
|
||||
"amqp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "preprocessed",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "args: sqlx::types::Json<HashMap<String, Box<RawValue>>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "df8586283178b0e684b9a0e359efb16e38fd68a5fe2b01cd77f74a725bbf37a3"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS (SELECT 1 FROM v2_job_queue WHERE id = $1 AND workspace_id = $2 AND running)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "ee769c78ec0a782de18911cdcb655aad93cf23321cd0f42f7ffa5348bfbea26e"
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "WITH RECURSIVE lineage AS (\n SELECT id, parent_job, kind, runnable_path, runnable_id, trigger_kind, trigger,\n permissioned_as, args, 0 AS depth\n FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT p.id, p.parent_job, p.kind, p.runnable_path, p.runnable_id, p.trigger_kind,\n p.trigger, p.permissioned_as, p.args, l.depth + 1\n FROM v2_job p JOIN lineage l ON p.id = l.parent_job\n WHERE p.workspace_id = $2 AND l.depth < 100\n )\n SELECT id AS \"id!\", parent_job, kind AS \"kind!: JobKind\", runnable_path, runnable_id,\n trigger_kind::text AS trigger_kind, permissioned_as AS \"permissioned_as!\",\n -- A restart takes its flow version from the request, so a trusted flow path\n -- can carry another flow's code: the version must belong to that path.\n CASE kind\n WHEN 'flow' THEN EXISTS (SELECT 1 FROM flow_version fv\n WHERE fv.id = runnable_id AND fv.path = runnable_path AND fv.workspace_id = $2)\n WHEN 'script' THEN EXISTS (SELECT 1 FROM script s\n WHERE s.hash = runnable_id AND s.path = runnable_path AND s.workspace_id = $2\n AND NOT s.deleted)\n ELSE true\n END AS \"origin_verified!\",\n -- What `run/f` and `run/p` resolve the path to now; for scripts the predicate of\n -- `get_latest_deployed_script_hash`.\n COALESCE(CASE kind\n WHEN 'flow' THEN runnable_id = (SELECT f.versions[array_upper(f.versions, 1)]\n FROM flow f WHERE f.path = runnable_path AND f.workspace_id = $2)\n WHEN 'script' THEN runnable_id = (SELECT s.hash FROM script s\n WHERE s.path = runnable_path AND s.workspace_id = $2 AND NOT s.deleted\n AND s.lock IS NOT NULL AND s.lock_error_logs IS NULL\n ORDER BY s.created_at DESC LIMIT 1)\n END, false) AS \"current_version!\",\n EXISTS (SELECT 1 FROM v2_job_status st WHERE st.id = lineage.id\n AND jsonb_typeof(st.flow_status->'restarted_from') = 'object')\n OR EXISTS (SELECT 1 FROM v2_job_completed c WHERE c.id = lineage.id\n AND jsonb_typeof(c.flow_status->'restarted_from') = 'object')\n AS \"restarted!\",\n COALESCE(\n (SELECT st.flow_status->'restarted_from'->>'flow_job_id' FROM v2_job_status st\n WHERE st.id = lineage.id),\n (SELECT c.flow_status->'restarted_from'->>'flow_job_id' FROM v2_job_completed c\n WHERE c.id = lineage.id)\n ) AS restarted_from,\n -- Only deployed-app runs are stamped with their app; an app editor preview\n -- runs app code at an app path it does not have to own.\n COALESCE(trigger_kind = 'app' AND starts_with(runnable_path, trigger || '/'), false)\n AS \"app_stamped!\",\n -- A preview's modules come from its args, which its parent may have taken from\n -- the caller.\n COALESCE(jsonb_typeof(args->'_MODULES') = 'object', false) AS \"args_modules!\"\n FROM lineage ORDER BY depth",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id!",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "parent_job",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "kind!: JobKind",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "job_kind",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"script",
|
||||
"preview",
|
||||
"flow",
|
||||
"dependencies",
|
||||
"flowpreview",
|
||||
"script_hub",
|
||||
"identity",
|
||||
"flowdependencies",
|
||||
"http",
|
||||
"graphql",
|
||||
"postgresql",
|
||||
"noop",
|
||||
"appdependencies",
|
||||
"deploymentcallback",
|
||||
"singlestepflow",
|
||||
"flowscript",
|
||||
"flownode",
|
||||
"appscript",
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "runnable_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "runnable_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "trigger_kind",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "permissioned_as!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "origin_verified!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "current_version!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "restarted!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "restarted_from",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "app_stamped!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "args_modules!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "ee92100fcd681515ba81f23970e64af7f33464879bef4d1eff682126dffeef64"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT value AS \"value: sqlx::types::Json<serde_json::Value>\"\n FROM resource WHERE workspace_id = $1 AND path = $2 AND resource_type = 'ai_agent'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "value: sqlx::types::Json<serde_json::Value>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "f3a740394038ddfced97254d8dcd4de49f63fb6dc4c8aab126dbe7cd611a862c"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_queue SET worker = $1, running = true, started_at = now()\n WHERE id = $2 AND tag = ANY($3)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Uuid",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f5c87b6fc1decd6dc53590fe4d16f59430530b8b7428e42d1777927e63395ff8"
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json<HashMap<String, Box<RawValue>>>\",\n j.tag AS \"tag!\", j.priority\n FROM v2_job j\n WHERE j.id = $1 and j.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "args: sqlx::types::Json<HashMap<String, Box<RawValue>>>",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "tag!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "priority",
|
||||
"type_info": "Int2"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "f7753f8f7b40aebf66de607203f97681d06ac04f9d547b4c49c032d9e933a29e"
|
||||
}
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path",
|
||||
"query": "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING hash",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
"name": "hash",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -19,5 +19,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0e7d95f4913e5775651971d741a3b5c1ef5dfe079be5325abe2866d39a7fe5fb"
|
||||
"hash": "fa85a22c77844bb2c538b802b0984d4d69aecdd8a65db02e9495b0d6011c5c1f"
|
||||
}
|
||||
Generated
+91
-79
@@ -14792,7 +14792,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -14876,11 +14876,12 @@ dependencies = [
|
||||
"windmill-worker-volumes",
|
||||
"windows-service",
|
||||
"windows-sys 0.52.0",
|
||||
"winresource",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-ai"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -14914,7 +14915,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-alerting"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -14927,7 +14928,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15067,7 +15068,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-agent-workers"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15090,7 +15091,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-assets"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15107,7 +15108,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-auth"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15133,7 +15134,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
@@ -15143,7 +15144,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-configs"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15160,7 +15161,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-debug"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"base64 0.22.1",
|
||||
@@ -15182,7 +15183,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-embeddings"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15205,7 +15206,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flow-conversations"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15221,7 +15222,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flows"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15243,7 +15244,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-groups"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15265,7 +15266,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-inputs"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15279,7 +15280,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-integration-tests"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -15315,7 +15316,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-jobs"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15340,7 +15341,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-npm-proxy"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15368,7 +15369,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-openapi"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15390,7 +15391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-schedule"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15410,7 +15411,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-scripts"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15452,7 +15453,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-settings"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15481,7 +15482,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-sse"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -15493,7 +15494,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-users"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.8.9",
|
||||
@@ -15517,7 +15518,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workers"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15531,7 +15532,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workspaces"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"axum 0.8.9",
|
||||
"chrono",
|
||||
@@ -15551,6 +15552,7 @@ dependencies = [
|
||||
"strum",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -15569,7 +15571,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
@@ -15583,7 +15585,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.8.9",
|
||||
@@ -15602,7 +15604,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"aho-corasick",
|
||||
@@ -15660,6 +15662,7 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"pkcs1",
|
||||
"postgres-native-tls 0.5.3",
|
||||
"postgres-protocol",
|
||||
"prometheus",
|
||||
"quick_cache",
|
||||
"rand 0.9.5",
|
||||
@@ -15714,7 +15717,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-dep-map"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
@@ -15734,7 +15737,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -15751,7 +15754,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"astral-tokio-tar",
|
||||
@@ -15778,7 +15781,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-jseval"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -15795,7 +15798,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -15811,7 +15814,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-mcp"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -15832,7 +15835,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-native-triggers"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -15863,7 +15866,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-oauth"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -15888,7 +15891,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-object-store"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -15924,7 +15927,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-operator"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -15942,7 +15945,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -15951,7 +15954,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15963,7 +15966,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -15975,7 +15978,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -15987,7 +15990,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -15999,7 +16002,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16011,7 +16014,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -16022,7 +16025,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16033,7 +16036,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16045,7 +16048,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -16056,7 +16059,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16078,7 +16081,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-r"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16090,7 +16093,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16104,7 +16107,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -16121,7 +16124,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16134,7 +16137,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -16146,7 +16149,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16164,7 +16167,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde-wasm-bindgen",
|
||||
@@ -16180,7 +16183,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wac"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -16196,7 +16199,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16210,7 +16213,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16249,7 +16252,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-runtime-nativets"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"const_format",
|
||||
@@ -16289,7 +16292,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"wasm-bindgen",
|
||||
@@ -16300,7 +16303,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-store"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16335,7 +16338,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-test-utils"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16359,7 +16362,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16392,7 +16395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-amqp"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16419,7 +16422,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-azure"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16452,7 +16455,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-email"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16472,7 +16475,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-gcp"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16506,7 +16509,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-http"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16543,7 +16546,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-kafka"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16566,7 +16569,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-mqtt"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16590,7 +16593,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-nats"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -16614,7 +16617,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-postgres"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16649,7 +16652,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-sqs"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16677,7 +16680,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-websocket"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16702,7 +16705,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.13.2",
|
||||
@@ -16721,7 +16724,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-once-cell",
|
||||
@@ -16839,7 +16842,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker-volumes"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
@@ -17380,6 +17383,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.19"
|
||||
|
||||
+7
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -88,7 +88,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal", "./parsers/windmill-parser-wasm"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -329,6 +329,9 @@ strum.workspace = true
|
||||
windows-service = "0.7"
|
||||
windows-sys = { version = "0.52", features = ["Win32_System_Services", "Win32_System_Console", "Win32_Foundation"] }
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winresource = { version = "0.1.31", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemallocator = { optional = true, workspace = true }
|
||||
tikv-jemalloc-sys = { optional = true, workspace = true }
|
||||
@@ -519,7 +522,7 @@ serde_urlencoded = "^0"
|
||||
astral-tokio-tar = "^0.6.2"
|
||||
tempfile = "^3"
|
||||
x509-parser = "^0.16"
|
||||
tokio-util = { version = "=0.7.17", features = ["io"] }
|
||||
tokio-util = { version = "=0.7.17", features = ["io", "rt"] }
|
||||
json-pointer = "^0"
|
||||
itertools = "^0.14.0"
|
||||
regex = "^1"
|
||||
@@ -626,6 +629,7 @@ wasm-bindgen-test = "^0"
|
||||
convert_case = "0.6.0"
|
||||
getrandom = "0.2"
|
||||
tokio-postgres = {version = "^0.7", features = ["array-impls", "with-serde_json-1", "with-chrono-0_4", "with-uuid-1", "with-bit-vec-0_6"]}
|
||||
postgres-protocol = "0.6"
|
||||
rust-postgres = { package = "tokio-postgres", git = "https://github.com/MaterializeInc/rust-postgres", rev = "78c1222577bb091d69bc22b1bc7ad01c14675abe"}
|
||||
rust-postgres-native-tls = { package = "postgres-native-tls", git = "https://github.com/MaterializeInc/rust-postgres", features = ["runtime"], rev = "78c1222577bb091d69bc22b1bc7ad01c14675abe" }
|
||||
bit-vec = "=0.6.3"
|
||||
|
||||
@@ -2,4 +2,19 @@
|
||||
fn main() {
|
||||
// trigger recompilation when a new migration is added
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
|
||||
// Embed a VERSIONINFO resource so the exe's file properties carry the release
|
||||
// version (from Cargo.toml), which Windows worker update scripts read.
|
||||
// `cfg(windows)` is the host, matching the Windows-only build-dependency, so
|
||||
// cross builds to Windows get no version resource.
|
||||
#[cfg(windows)]
|
||||
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
|
||||
winresource::WindowsResource::new()
|
||||
.set("ProductName", "Windmill")
|
||||
.set("FileDescription", "Windmill")
|
||||
.set("CompanyName", "Windmill Labs, Inc.")
|
||||
.set("LegalCopyright", "Copyright Windmill Labs, Inc.")
|
||||
.compile()
|
||||
.expect("failed to embed the Windows version resource");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
421cf2a8b4f98b421e93c0fc7c1c378314a66e50
|
||||
ff4d04f17721d84fb9a1f655af50ff7e4e7dcbc7
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Roles on the external cluster are live logins there; dropping the column would forget them.
|
||||
LOCK TABLE datatable_role;
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM datatable_role WHERE cluster <> 'instance') THEN
|
||||
RAISE EXCEPTION 'datatable_role holds roles on the external instance cluster. Delete them in instance settings first.';
|
||||
END IF;
|
||||
-- Before this, only data tables on Windmill's own cluster could be under roles, and a role
|
||||
-- block left with just `admin` survives deleting every external role.
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM workspace_settings ws,
|
||||
jsonb_each(CASE WHEN jsonb_typeof(ws.datatable->'datatables') = 'object'
|
||||
THEN ws.datatable->'datatables' ELSE '{}'::jsonb END) dt
|
||||
WHERE dt.value->'database'->>'resource_type' = 'external_instance'
|
||||
AND dt.value ? 'permissions'
|
||||
) THEN
|
||||
RAISE EXCEPTION 'external instance data tables are still under roles. Turn their roles off first.';
|
||||
END IF;
|
||||
END $$;
|
||||
ALTER TABLE datatable_role DROP CONSTRAINT datatable_role_cluster_name_key;
|
||||
ALTER TABLE datatable_role ADD CONSTRAINT datatable_role_name_key UNIQUE (name);
|
||||
ALTER TABLE datatable_role DROP COLUMN cluster;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- A data table role is a Postgres login on one cluster: Windmill's own ('instance'), or the external
|
||||
-- instance cluster ('external_instance'). Role names are the cluster's own key, so they are unique
|
||||
-- per cluster rather than across the instance.
|
||||
ALTER TABLE datatable_role
|
||||
ADD COLUMN cluster VARCHAR(20) NOT NULL DEFAULT 'instance'
|
||||
CHECK (cluster IN ('instance', 'external_instance'));
|
||||
ALTER TABLE datatable_role DROP CONSTRAINT datatable_role_name_key;
|
||||
ALTER TABLE datatable_role ADD CONSTRAINT datatable_role_cluster_name_key UNIQUE (cluster, name);
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE schedule DROP COLUMN last_missed_at;
|
||||
ALTER TABLE schedule DROP COLUMN missed_occurrences;
|
||||
ALTER TABLE schedule DROP COLUMN late_run_streak;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- A late run finished (or, for flows, started) after the next occurrence was due, so that
|
||||
-- occurrence was missed. late_run_streak counts late runs in a row and is reset by a run on
|
||||
-- time; missed_occurrences is what the latest streak missed and last_missed_at when, both
|
||||
-- kept after the streak ends so a recent one stays visible. An edit or toggle clears all.
|
||||
ALTER TABLE schedule ADD COLUMN late_run_streak INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE schedule ADD COLUMN missed_occurrences INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE schedule ADD COLUMN last_missed_at TIMESTAMPTZ;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS workspace_fork_creation;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- A fork being created in the background, and how that ended. The fork's own workspace row only
|
||||
-- exists once the copy commits, so this is where a failure is reported to whoever polls for it.
|
||||
-- `heartbeat_at` is refreshed while the copy runs: an unfinished row that stops being refreshed
|
||||
-- belongs to a server that went away mid-copy. `step` names the part of the copy it is in.
|
||||
-- `creation_id` names one attempt: the row is reused when a failed or abandoned one is retried, and
|
||||
-- whoever polls reads only the attempt it started.
|
||||
CREATE TABLE workspace_fork_creation (
|
||||
fork_workspace_id VARCHAR(50) PRIMARY KEY,
|
||||
parent_workspace_id VARCHAR(50) NOT NULL,
|
||||
creation_id UUID NOT NULL,
|
||||
created_by VARCHAR(255) NOT NULL,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
heartbeat_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
step TEXT,
|
||||
finished_at TIMESTAMPTZ,
|
||||
error TEXT
|
||||
);
|
||||
|
||||
GRANT ALL ON workspace_fork_creation TO windmill_user;
|
||||
GRANT ALL ON workspace_fork_creation TO windmill_admin;
|
||||
+86
-28
@@ -18,7 +18,32 @@
|
||||
"slack": {
|
||||
"auth_url": "https://slack.com/oauth/v2/authorize",
|
||||
"token_url": "https://slack.com/api/oauth.v2.access",
|
||||
"scopes": ["chat:write", "chat:write.public", "channels:join", "files:write"]
|
||||
"scopes": [
|
||||
"chat:write",
|
||||
"chat:write.public",
|
||||
"channels:join",
|
||||
"files:write",
|
||||
"users:read",
|
||||
"users:read.email",
|
||||
"channels:read",
|
||||
"channels:history"
|
||||
],
|
||||
"user_scopes": [
|
||||
"channels:history",
|
||||
"groups:history",
|
||||
"im:history",
|
||||
"mpim:history",
|
||||
"search:read",
|
||||
"users:read",
|
||||
"chat:write",
|
||||
"users:read.email",
|
||||
"channels:read",
|
||||
"groups:read",
|
||||
"im:read",
|
||||
"mpim:read",
|
||||
"files:read"
|
||||
],
|
||||
"token_response_path": "authed_user"
|
||||
},
|
||||
"supabase_wizard": {
|
||||
"auth_url": "https://api.supabase.com/v1/oauth/authorize",
|
||||
@@ -30,8 +55,8 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/spreadsheets"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/spreadsheets",
|
||||
"https://www.googleapis.com/auth/spreadsheets.readonly"
|
||||
{ "value": "https://www.googleapis.com/auth/spreadsheets", "label": "Read, edit, create and delete spreadsheets" },
|
||||
{ "value": "https://www.googleapis.com/auth/spreadsheets.readonly", "label": "Read spreadsheets" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -43,9 +68,9 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/drive"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/drive.readonly",
|
||||
"https://www.googleapis.com/auth/drive"
|
||||
{ "value": "https://www.googleapis.com/auth/drive.file", "label": "Only files created or opened with this connection" },
|
||||
{ "value": "https://www.googleapis.com/auth/drive.readonly", "label": "Read all Drive files" },
|
||||
{ "value": "https://www.googleapis.com/auth/drive", "label": "Read, edit, create and delete all Drive files" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -57,11 +82,11 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/gmail.send"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/gmail.send",
|
||||
"https://www.googleapis.com/auth/gmail.readonly",
|
||||
"https://www.googleapis.com/auth/gmail.compose",
|
||||
"https://www.googleapis.com/auth/gmail.modify",
|
||||
"https://www.googleapis.com/auth/gmail.labels"
|
||||
{ "value": "https://www.googleapis.com/auth/gmail.send", "label": "Send mail only" },
|
||||
{ "value": "https://www.googleapis.com/auth/gmail.readonly", "label": "Read and search mail" },
|
||||
{ "value": "https://www.googleapis.com/auth/gmail.compose", "label": "Draft and send mail" },
|
||||
{ "value": "https://www.googleapis.com/auth/gmail.modify", "label": "Read, search, label, draft and send mail" },
|
||||
{ "value": "https://www.googleapis.com/auth/gmail.labels", "label": "Create, edit and delete labels" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -73,10 +98,10 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/calendar.events"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/calendar.events",
|
||||
"https://www.googleapis.com/auth/calendar.events.readonly",
|
||||
"https://www.googleapis.com/auth/calendar.readonly",
|
||||
"https://www.googleapis.com/auth/calendar"
|
||||
{ "value": "https://www.googleapis.com/auth/calendar.events", "label": "Read and edit events on all calendars" },
|
||||
{ "value": "https://www.googleapis.com/auth/calendar.events.readonly", "label": "Read events on all calendars" },
|
||||
{ "value": "https://www.googleapis.com/auth/calendar.readonly", "label": "Read all calendars and events" },
|
||||
{ "value": "https://www.googleapis.com/auth/calendar", "label": "Read, edit, share and delete all calendars" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -88,10 +113,43 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/forms"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/forms",
|
||||
"https://www.googleapis.com/auth/forms.body",
|
||||
"https://www.googleapis.com/auth/forms.body.readonly",
|
||||
"https://www.googleapis.com/auth/forms.responses.readonly"
|
||||
{ "value": "https://www.googleapis.com/auth/forms", "label": "Read and manage forms in Drive" },
|
||||
{ "value": "https://www.googleapis.com/auth/forms.body", "label": "Read, edit, create and delete forms" },
|
||||
{ "value": "https://www.googleapis.com/auth/forms.body.readonly", "label": "Read forms" },
|
||||
{ "value": "https://www.googleapis.com/auth/forms.responses.readonly", "label": "Read form responses" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"gdocs": {
|
||||
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/documents"],
|
||||
"scope_options": [
|
||||
{ "value": "https://www.googleapis.com/auth/documents", "label": "Read, edit, create and delete documents" },
|
||||
{ "value": "https://www.googleapis.com/auth/documents.readonly", "label": "Read documents" },
|
||||
{ "value": "https://www.googleapis.com/auth/drive.file", "label": "Only files created or opened with this connection" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
"prompt": "consent"
|
||||
}
|
||||
},
|
||||
"gchat": {
|
||||
"auth_url": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/chat.spaces.readonly",
|
||||
"https://www.googleapis.com/auth/chat.messages.create"
|
||||
],
|
||||
"scope_options": [
|
||||
{ "value": "https://www.googleapis.com/auth/chat.spaces.readonly", "label": "Read spaces" },
|
||||
{ "value": "https://www.googleapis.com/auth/chat.messages", "label": "Read, send, edit and delete messages" },
|
||||
{ "value": "https://www.googleapis.com/auth/chat.messages.readonly", "label": "Read messages" },
|
||||
{ "value": "https://www.googleapis.com/auth/chat.messages.create", "label": "Send messages only" },
|
||||
{ "value": "https://www.googleapis.com/auth/chat.memberships.readonly", "label": "Read space members" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -103,8 +161,8 @@
|
||||
"token_url": "https://oauth2.googleapis.com/token",
|
||||
"scopes": ["https://www.googleapis.com/auth/cloud-platform"],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/cloud-platform",
|
||||
"https://www.googleapis.com/auth/cloud-platform.read-only"
|
||||
{ "value": "https://www.googleapis.com/auth/cloud-platform", "label": "Read and manage all Google Cloud resources" },
|
||||
{ "value": "https://www.googleapis.com/auth/cloud-platform.read-only", "label": "Read Google Cloud resources" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
@@ -121,13 +179,13 @@
|
||||
"https://www.googleapis.com/auth/admin.directory.orgunit"
|
||||
],
|
||||
"scope_options": [
|
||||
"https://www.googleapis.com/auth/admin.directory.user",
|
||||
"https://www.googleapis.com/auth/admin.directory.user.readonly",
|
||||
"https://www.googleapis.com/auth/admin.directory.group",
|
||||
"https://www.googleapis.com/auth/admin.directory.group.readonly",
|
||||
"https://www.googleapis.com/auth/admin.directory.orgunit",
|
||||
"https://www.googleapis.com/auth/admin.directory.orgunit.readonly",
|
||||
"https://www.googleapis.com/auth/admin.directory.user.security"
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.user", "label": "Read and manage users" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.user.readonly", "label": "Read users" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.group", "label": "Read and manage groups" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.group.readonly", "label": "Read groups" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.orgunit", "label": "Read and manage organizational units" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.orgunit.readonly", "label": "Read organizational units" },
|
||||
{ "value": "https://www.googleapis.com/auth/admin.directory.user.security", "label": "Manage user security settings" }
|
||||
],
|
||||
"extra_params": {
|
||||
"access_type": "offline",
|
||||
|
||||
+24
-24
@@ -6216,7 +6216,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
@@ -6302,7 +6302,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6314,7 +6314,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"serde",
|
||||
@@ -6323,7 +6323,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6335,7 +6335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -6347,7 +6347,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -6359,7 +6359,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6371,7 +6371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -6383,7 +6383,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -6394,7 +6394,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -6405,7 +6405,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -6417,7 +6417,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -6428,7 +6428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -6450,7 +6450,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-r"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -6462,7 +6462,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6476,7 +6476,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case",
|
||||
@@ -6493,7 +6493,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6506,7 +6506,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -6518,7 +6518,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6536,7 +6536,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts-asset"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde-wasm-bindgen",
|
||||
@@ -6552,7 +6552,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wac"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -6568,7 +6568,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wasm"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.17",
|
||||
@@ -6600,7 +6600,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -6614,7 +6614,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
|
||||
@@ -12,7 +12,7 @@ resolver = "2"
|
||||
members = ["."]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.819.0"
|
||||
version = "1.820.0"
|
||||
edition = "2021"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
|
||||
|
||||
@@ -1232,6 +1232,11 @@ Windmill Community Edition {GIT_VERSION}
|
||||
default_base_internal_url.clone()
|
||||
};
|
||||
|
||||
// Deletions are delivered by `notify_event` to running processes only, so one that
|
||||
// was down when a version was deleted would keep its code on disk: start from an
|
||||
// empty script cache, refilled from the database.
|
||||
windmill_common::cache::script::clear();
|
||||
|
||||
initial_load(
|
||||
&conn,
|
||||
killpill_tx.clone(),
|
||||
@@ -1884,6 +1889,12 @@ async fn process_notify_event(
|
||||
);
|
||||
windmill_common::workspaces::PUBLIC_APP_RATE_LIMIT_CACHE.remove(payload);
|
||||
}
|
||||
windmill_common::SCRIPT_VERSION_DELETED_CHANNEL => {
|
||||
match serde_json::from_str(payload) {
|
||||
Ok(deleted) => windmill_api_scripts::scripts::evict_deleted_script_versions(deleted),
|
||||
Err(e) => tracing::error!("Invalid script version deletion payload {payload}: {e}"),
|
||||
}
|
||||
}
|
||||
"notify_runnable_version_change" => {
|
||||
tracing::info!("Runnable version change detected: {}", payload);
|
||||
match payload.split(':').collect::<Vec<&str>>().as_slice() {
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
use axum::{
|
||||
extract::State,
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
use sqlx::{Pool, Postgres};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tokio::sync::Notify;
|
||||
use windmill_common::{flows::FlowValue, jobs::JobPayload};
|
||||
use windmill_test_utils::{
|
||||
completed_job, in_test_worker, listen_for_completed_jobs, ApiServer, RunJob, StreamFind,
|
||||
};
|
||||
|
||||
async fn model(Json(body): Json<Value>) -> ([(&'static str, &'static str); 1], String) {
|
||||
let finished = body["messages"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|m| m["role"] == "tool");
|
||||
let delta = if finished {
|
||||
json!({"role":"assistant", "content":"done"})
|
||||
} else {
|
||||
json!({"role":"assistant", "tool_calls": (0..3).map(|i| json!({
|
||||
"index":i, "id":format!("call_{i}"), "type":"function",
|
||||
"function":{"name":format!("tool_{i}"), "arguments":"{}"}
|
||||
})).collect::<Vec<_>>()})
|
||||
};
|
||||
let event = json!({"choices":[{"index":0, "delta":delta, "finish_reason":null}]});
|
||||
let end = json!({"choices":[{"index":0, "delta":{}, "finish_reason":if finished {"stop"} else {"tool_calls"}}]});
|
||||
(
|
||||
[("content-type", "text/event-stream")],
|
||||
format!("data: {event}\n\ndata: {end}\n\ndata: [DONE]\n\n"),
|
||||
)
|
||||
}
|
||||
|
||||
async fn run_batch(
|
||||
db: Pool<Postgres>,
|
||||
parallel: bool,
|
||||
limited: bool,
|
||||
unsupported: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
std::env::set_var("ALLOW_PRIVATE_AI_BASE_URLS", "true");
|
||||
let server = ApiServer::start(db.clone()).await?;
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
|
||||
let base = format!("http://{}", listener.local_addr()?);
|
||||
let gate = Arc::new(Notify::new());
|
||||
let router = Router::new()
|
||||
.route("/v1/chat/completions", post(model))
|
||||
.route(
|
||||
"/first",
|
||||
get(|State(gate): State<Arc<Notify>>| async move {
|
||||
gate.notified().await;
|
||||
"ok"
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/second",
|
||||
get(|State(gate): State<Arc<Notify>>| async move {
|
||||
gate.notify_one();
|
||||
"ok"
|
||||
}),
|
||||
)
|
||||
.with_state(gate);
|
||||
let stub = tokio::spawn(async move {
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
});
|
||||
let tools: Vec<Value> = (0..3).map(|i| {
|
||||
let wait = if parallel && !limited && i < 2 {
|
||||
format!("await fetch('{base}/{}');", if i == 0 {"first"} else {"second"})
|
||||
} else { String::new() };
|
||||
let finish = if i == 2 { "throw new Error('expected tool failure');".to_string() }
|
||||
else { format!("return {i};") };
|
||||
json!({"id":format!("t{i}"),"summary":format!("tool_{i}"),"value":{
|
||||
"type":"rawscript", "language":"bun", "input_transforms":{},
|
||||
"tag": if unsupported && i != 1 { "unserved-tool-tag" } else { "bun" },
|
||||
"concurrent_limit": if limited { Some(1) } else { None },
|
||||
"custom_concurrency_key": if limited { Some("agent-tool-test") } else { None },
|
||||
"content":format!("export async function main() {{ {wait} await Bun.sleep({}); {finish} }}", if i == 0 {200} else {0})
|
||||
}})
|
||||
}).collect();
|
||||
let flow: FlowValue = serde_json::from_value(
|
||||
json!({"modules":[{"id":"agent","timeout":if unsupported {Some(3)} else {None},"value":{
|
||||
"type":"aiagent", "tools":tools, "input_transforms":{
|
||||
"provider":{"type":"static","value":{"kind":"customai","model":"queue-test","resource":{"base_url":format!("{base}/v1")}}},
|
||||
"user_message":{"type":"static","value":"run the tools"},
|
||||
"max_iterations":{"type":"static","value":3}
|
||||
}
|
||||
}}]}),
|
||||
)?;
|
||||
let id = RunJob::from(JobPayload::RawFlow {
|
||||
value: flow,
|
||||
path: Some("u/test/agent_queue".into()),
|
||||
restarted_from: None,
|
||||
})
|
||||
.push(&db)
|
||||
.await;
|
||||
let notifications = listen_for_completed_jobs(&db).await;
|
||||
let wait = async {
|
||||
if parallel {
|
||||
in_test_worker(&db, notifications.find(&id), server.addr.port()).await;
|
||||
} else {
|
||||
notifications.find(&id).await;
|
||||
}
|
||||
};
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(45),
|
||||
in_test_worker(&db, wait, server.addr.port()),
|
||||
)
|
||||
.await?;
|
||||
let result = completed_job(id, &db).await;
|
||||
if unsupported {
|
||||
assert!(
|
||||
!result.success,
|
||||
"agent must time out waiting for unsupported tools"
|
||||
);
|
||||
let children: Vec<(String, bool, bool)> = sqlx::query_as(
|
||||
"SELECT j.runnable_path, c.status = 'success', c.started_at IS NULL
|
||||
FROM v2_job j JOIN v2_job_completed c USING(id)
|
||||
WHERE j.parent_job IN (SELECT id FROM v2_job WHERE parent_job = $1)
|
||||
ORDER BY j.runnable_path",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
assert_eq!(children.len(), 3);
|
||||
assert_eq!(
|
||||
children.iter().map(|c| (c.1, c.2)).collect::<Vec<_>>(),
|
||||
vec![(false, true), (true, false), (false, true)],
|
||||
"neither the first-tool reservation nor fallback may execute unsupported tags"
|
||||
);
|
||||
stub.abort();
|
||||
server.close().await?;
|
||||
return Ok(());
|
||||
}
|
||||
assert!(result.success, "{:?}", result.result);
|
||||
let result = result.json_result().expect("agent result");
|
||||
let messages: Vec<_> = result["messages"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|m| m["role"] == "tool")
|
||||
.collect();
|
||||
assert_eq!(messages.len(), 3);
|
||||
for (index, message) in messages.iter().enumerate() {
|
||||
assert_eq!(message["tool_call_id"], format!("call_{index}"));
|
||||
}
|
||||
assert_eq!(messages[0]["content"], "0");
|
||||
assert_eq!(messages[1]["content"], "1");
|
||||
assert!(messages[2]["content"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("expected tool failure"));
|
||||
let (parent_id, parent_worker): (uuid::Uuid, String) = sqlx::query_as(
|
||||
"SELECT j.id, c.worker FROM v2_job j JOIN v2_job_completed c USING(id) WHERE j.parent_job = $1"
|
||||
).bind(id).fetch_one(&db).await?;
|
||||
let children: Vec<(String, String, bool)> = sqlx::query_as(
|
||||
"SELECT j.runnable_path, c.worker, c.status = 'success' FROM v2_job j JOIN v2_job_completed c USING(id) WHERE j.parent_job = $1 ORDER BY j.runnable_path"
|
||||
).bind(parent_id).fetch_all(&db).await?;
|
||||
assert_eq!(children.len(), 3);
|
||||
assert_eq!(
|
||||
children[0].1, parent_worker,
|
||||
"first tool must stay on the parent worker"
|
||||
);
|
||||
assert_eq!(
|
||||
children.iter().map(|c| c.2).collect::<Vec<_>>(),
|
||||
vec![true, true, false]
|
||||
);
|
||||
if limited {
|
||||
let overlapping: i64 = sqlx::query_scalar(
|
||||
"SELECT count(*) FROM v2_job j1 JOIN v2_job_completed c1 ON c1.id = j1.id
|
||||
JOIN v2_job j2 ON j2.parent_job = j1.parent_job AND j2.id > j1.id
|
||||
JOIN v2_job_completed c2 ON c2.id = j2.id
|
||||
WHERE j1.parent_job = $1 AND c1.started_at < c2.completed_at AND c2.started_at < c1.completed_at"
|
||||
).bind(parent_id).fetch_one(&db).await?;
|
||||
assert_eq!(
|
||||
overlapping, 0,
|
||||
"the reserved first job must count toward the shared limit"
|
||||
);
|
||||
} else if parallel {
|
||||
assert_ne!(
|
||||
children[1].1, parent_worker,
|
||||
"second tool must unblock the first from another worker"
|
||||
);
|
||||
} else {
|
||||
assert!(children.iter().all(|child| child.1 == parent_worker));
|
||||
}
|
||||
stub.abort();
|
||||
server.close().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[serial_test::serial]
|
||||
async fn parent_drains_tools_without_another_worker(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
run_batch(db, false, false, false).await
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[serial_test::serial]
|
||||
async fn parent_keeps_first_tool_while_another_worker_runs_siblings(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
run_batch(db, true, false, false).await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "private"))]
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[serial_test::serial]
|
||||
async fn reserved_tool_obeys_shared_concurrency_limit(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
run_batch(db, true, true, false).await
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
#[serial_test::serial]
|
||||
async fn parent_leaves_unsupported_first_and_remaining_tools_queued(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
run_batch(db, false, false, true).await
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//! A caller's `_MODULES` never reaches a job: the worker builds that arg in as module code,
|
||||
//! so on a deployed runnable it would run caller code as the runnable (and its
|
||||
//! `on_behalf_of`). `push` drops it from `args` and `extra` alike and only sets it from a
|
||||
//! preview's own `RawCode::modules`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde_json::{json, value::RawValue};
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::{
|
||||
jobs::{JobPayload, RawCode},
|
||||
runnable_settings::{ConcurrencySettings, DebouncingSettings},
|
||||
scripts::{ScriptHash, ScriptLang, ScriptModule},
|
||||
};
|
||||
use windmill_queue::{PushArgs, PushIsolationLevel};
|
||||
|
||||
fn modules(v: &str) -> serde_json::Value {
|
||||
json!({ "helper.ts": { "content": format!("export const v = \"{v}\""), "language": "bun" } })
|
||||
}
|
||||
|
||||
/// Pushes `payload` with a caller `_MODULES` both in `args` and in `extra` (where webhook
|
||||
/// query and headers land), and returns the stored `_MODULES`.
|
||||
async fn stored_modules(db: &Pool<Postgres>, payload: JobPayload) -> Option<serde_json::Value> {
|
||||
let caller: Box<RawValue> = serde_json::value::to_raw_value(&modules("caller")).unwrap();
|
||||
let args = HashMap::from([("_MODULES".to_string(), caller.clone())]);
|
||||
let extra = HashMap::from([("_MODULES".to_string(), caller)]);
|
||||
let (id, tx) = windmill_queue::push(
|
||||
db,
|
||||
PushIsolationLevel::IsolatedRoot(db.clone()),
|
||||
"test-workspace",
|
||||
payload,
|
||||
PushArgs { args: &args, extra: Some(extra) },
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("push must succeed");
|
||||
tx.commit().await.unwrap();
|
||||
sqlx::query_scalar::<_, Option<serde_json::Value>>(
|
||||
"SELECT args->'_MODULES' FROM v2_job WHERE id = $1",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn caller_modules_never_reach_a_job(db: Pool<Postgres>) {
|
||||
let deployed = JobPayload::ScriptHash {
|
||||
hash: ScriptHash(123412),
|
||||
path: "f/system/hello".to_string(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
language: ScriptLang::Bun,
|
||||
priority: None,
|
||||
apply_preprocessor: false,
|
||||
concurrency_settings: ConcurrencySettings::default(),
|
||||
debouncing_settings: DebouncingSettings::default(),
|
||||
labels: None,
|
||||
};
|
||||
assert_eq!(stored_modules(&db, deployed).await, None);
|
||||
|
||||
let preview = |modules: Option<HashMap<String, ScriptModule>>| {
|
||||
JobPayload::Code(RawCode {
|
||||
hash: None,
|
||||
content: "import { v } from \"./helper\"; export function main() { return v }"
|
||||
.to_string(),
|
||||
path: None,
|
||||
language: ScriptLang::Bun,
|
||||
lock: None,
|
||||
concurrency_settings: ConcurrencySettings::default().into(),
|
||||
debouncing_settings: DebouncingSettings::default(),
|
||||
cache_ttl: None,
|
||||
cache_ignore_s3_path: None,
|
||||
dedicated_worker: None,
|
||||
modules,
|
||||
tag: None,
|
||||
})
|
||||
};
|
||||
assert_eq!(stored_modules(&db, preview(None)).await, None);
|
||||
let own = serde_json::from_value(modules("preview")).unwrap();
|
||||
assert_eq!(
|
||||
stored_modules(&db, preview(Some(own))).await,
|
||||
Some(modules("preview"))
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//! Turning Windmill's own Postgres off as a data table substrate takes the data tables already on
|
||||
//! it out of use, not only new ones: resolution is the one path every job, API call and trigger
|
||||
//! reaches a data table through.
|
||||
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::workspaces::get_datatable_resource_from_db_unchecked;
|
||||
|
||||
#[sqlx::test]
|
||||
async fn test_instance_datatable_refused_once_instance_pg_disabled(
|
||||
db: Pool<Postgres>,
|
||||
) -> anyhow::Result<()> {
|
||||
sqlx::query("INSERT INTO workspace (id, name, owner) VALUES ('dtoff-ws', 'DTOFF', 'dtoff')")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
sqlx::query(
|
||||
"INSERT INTO workspace_settings (workspace_id, datatable) VALUES ('dtoff-ws', \
|
||||
'{\"datatables\": {\"main\": {\"database\": {\"resource_type\": \"instance\", \
|
||||
\"resource_path\": \"dt_dtoff\"}}}}')",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let resolved = get_datatable_resource_from_db_unchecked(&db, "dtoff-ws", "main").await?;
|
||||
assert_eq!(resolved["dbname"], "dt_dtoff");
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO global_settings (name, value) VALUES ('instance_pg_disabled', 'true')",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let refused = get_datatable_resource_from_db_unchecked(&db, "dtoff-ws", "main")
|
||||
.await
|
||||
.expect_err("an instance data table must not resolve while Windmill's database is off");
|
||||
assert!(
|
||||
refused.to_string().contains("disabled on this instance"),
|
||||
"unexpected error: {refused}"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//! Script data is cached by hash, and the hash leaves out the workspace, so forks and clones
|
||||
//! share it: a deleted copy in one workspace must not stop the live copy in another.
|
||||
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::{cache, scripts::ScriptHash, worker::Connection};
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn a_deleted_copy_does_not_shadow_a_live_one(db: Pool<Postgres>) {
|
||||
const SHARED: i64 = 0x5de1_e7ed_0002;
|
||||
sqlx::query("INSERT INTO workspace (id, name, owner) VALUES ('fork', 'fork', 'test-user')")
|
||||
.execute(&db)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"INSERT INTO script (workspace_id, hash, path, summary, description, content,
|
||||
created_by, language, lock, archived, deleted)
|
||||
VALUES ('fork', $1, 'f/infra/tool', '', '', '', 'test-user', 'bash', '', true, true),
|
||||
('test-workspace', $1, 'f/infra/tool', '', '', 'echo shared', 'test-user',
|
||||
'bash', '', false, false)",
|
||||
)
|
||||
.bind(SHARED)
|
||||
.execute(&db)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let conn = Connection::Sql(db.clone());
|
||||
let (data, _) = cache::script::fetch(&conn, ScriptHash(SHARED))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(data.code, "echo shared");
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("base"))]
|
||||
async fn a_large_deletion_is_split_across_events(db: Pool<Postgres>) {
|
||||
let deleted = windmill_common::DeletedScriptVersions::new(
|
||||
"test-workspace",
|
||||
(0..501).map(|h| (format!("f/infra/s{}", h % 2), h)),
|
||||
);
|
||||
let mut conn = db.acquire().await.unwrap();
|
||||
deleted.notify(&mut conn).await.unwrap();
|
||||
|
||||
let events: Vec<String> = sqlx::query_scalar(
|
||||
"SELECT payload FROM notify_event WHERE channel = $1 ORDER BY id",
|
||||
)
|
||||
.bind(windmill_common::SCRIPT_VERSION_DELETED_CHANNEL)
|
||||
.fetch_all(&db)
|
||||
.await
|
||||
.unwrap();
|
||||
let events: Vec<windmill_common::DeletedScriptVersions> =
|
||||
events.iter().map(|e| serde_json::from_str(e).unwrap()).collect();
|
||||
assert_eq!(events.len(), 2);
|
||||
let hashes: Vec<i64> = events.iter().flat_map(|e| e.hashes.clone()).collect();
|
||||
let paths: Vec<String> = events.iter().flat_map(|e| e.paths.clone()).collect();
|
||||
assert_eq!(hashes, deleted.hashes);
|
||||
assert_eq!(paths, deleted.paths);
|
||||
}
|
||||
+31
@@ -30,3 +30,34 @@ INSERT INTO v2_job (id, workspace_id, kind, runnable_path, trigger_kind, trigger
|
||||
VALUES
|
||||
('3bb0c0de-0000-4000-8000-000000000007', 'test-workspace', 'appscript', 'f/t/app/comp', 'app', 'f/t/app', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-000000000008', 'test-workspace', 'appscript', 'f/t/app/comp', NULL, NULL, 'test-user', 'u/test-user', 'test@windmill.dev');
|
||||
|
||||
-- Inline steps of the deployed flow f/t/agent run as previews (no flow node, or
|
||||
-- DISABLE_FLOW_SCRIPT): a direct step, a step in a loop body, and a step whose path
|
||||
-- leaves the flow's namespace.
|
||||
INSERT INTO v2_job (id, workspace_id, kind, runnable_path, parent_job, created_by, permissioned_as, permissioned_as_email)
|
||||
VALUES
|
||||
('3bb0c0de-0000-4000-8000-000000000009', 'test-workspace', 'preview', 'f/t/agent/a', '3bb0c0de-0000-4000-8000-000000000001', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-00000000000a', 'test-workspace', 'flowpreview', 'f/t/agent/loop-0', '3bb0c0de-0000-4000-8000-000000000001', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-00000000000b', 'test-workspace', 'preview', 'f/t/agent/loop-0/a', '3bb0c0de-0000-4000-8000-00000000000a', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-00000000000c', 'test-workspace', 'preview', 'f/t/agentx', '3bb0c0de-0000-4000-8000-000000000001', 'test-user', 'u/test-user', 'test@windmill.dev');
|
||||
|
||||
-- Steps of the flow preview claiming f/t/agent: one nested in a branch, and one whose
|
||||
-- path names another item.
|
||||
INSERT INTO v2_job (id, workspace_id, kind, runnable_path, parent_job, created_by, permissioned_as, permissioned_as_email)
|
||||
VALUES
|
||||
('3bb0c0de-0000-4000-8000-00000000000d', 'test-workspace', 'flowpreview', 'f/t/agent/branchone-1', '3bb0c0de-0000-4000-8000-000000000003', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-00000000000e', 'test-workspace', 'preview', 'f/t/agent/branchone-1/a', '3bb0c0de-0000-4000-8000-00000000000d', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-00000000000f', 'test-workspace', 'preview', 'f/prod/deploy', '3bb0c0de-0000-4000-8000-000000000003', 'test-user', 'u/test-user', 'test@windmill.dev');
|
||||
|
||||
-- A deployed app without app_script entries runs its inline script as a stamped preview.
|
||||
INSERT INTO v2_job (id, workspace_id, kind, runnable_path, trigger_kind, trigger, created_by, permissioned_as, permissioned_as_email)
|
||||
VALUES
|
||||
('3bb0c0de-0000-4000-8000-000000000010', 'test-workspace', 'preview', 'f/t/app/comp', 'app', 'f/t/app', 'test-user', 'u/test-user', 'test@windmill.dev');
|
||||
|
||||
-- A step of the deployed flow whose modules came in its args, and a flow preview at the
|
||||
-- bare `f` whose step names another item.
|
||||
INSERT INTO v2_job (id, workspace_id, kind, runnable_path, parent_job, args, created_by, permissioned_as, permissioned_as_email)
|
||||
VALUES
|
||||
('3bb0c0de-0000-4000-8000-000000000011', 'test-workspace', 'preview', 'f/t/agent/m', '3bb0c0de-0000-4000-8000-000000000001', '{"_MODULES": {}}', 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-000000000012', 'test-workspace', 'flowpreview', 'f', NULL, NULL, 'test-user', 'u/test-user', 'test@windmill.dev'),
|
||||
('3bb0c0de-0000-4000-8000-000000000013', 'test-workspace', 'preview', 'f/prod/deploy', '3bb0c0de-0000-4000-8000-000000000012', NULL, 'test-user', 'u/test-user', 'test@windmill.dev');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user