Skip to content

Releases: hassancs91/PyRun

Release list

PyRun 2.0.0

Choose a tag to compare

@hassancs91 hassancs91 released this 23 Oct 08:28

Upgrade notes (from 1.x)

  • Selected secret injection is now exact. A user script in Selected mode
    receives only the secrets attached to it on the script form. Before 2.0 it also
    received every unowned workspace secret, which is the same set All gives, so
    the setting narrowed nothing. After upgrading, open each script that uses
    Selected, attach the secrets it reads (Auto-detect from code finds them) or
    switch it to All. Plugin scripts in Selected mode (the SDK default) get
    their own plugin's secrets and the secrets attached to them; a user's unowned
    secret no longer reaches them unless it is attached (the bundled example
    plugins only use their own secrets).
  • Datastore and plugin API tokens are stored hashed. Like Unified API
    tokens, they are kept only as a SHA-256 digest; the upgrade (migration 0065)
    hashes the existing ones. Tokens keep working unchanged; the list shows the
    first 8 and last 4 characters. A database copy or dump no longer contains
    usable tokens. Rolling back to 1.x after upgrading invalidates them (create
    new ones there).
  • Each script has its own working folder. A run's current directory is now
    /app/data/workdir/<script id>/ (the id is in the script's URL) instead of the
    folder every script shared. Files a run writes there are there for the script's
    next run, other scripts no longer see them, and the folder is deleted with the
    script (a backup restore keeps the folders of the scripts it restores; the files
    are not part of backups). Files scripts wrote before 2.0 stay in
    /app/data/workdir/; move what a script still reads into its own folder, or
    read it by absolute path.
  • Container process limit. docker-compose.coolify.yml now sets
    pids_limit: 2048 (processes and threads for the whole container). Scripts
    run as root in the image and the kernel exempts root from the per-run process
    limit (RLIMIT_NPROC), so that setting can't stop a fork bomb; Settings →
    Isolation now marks it "not enforced while scripts run as root". Raise the
    limit if your scripts start many processes (several headless browsers), and set
    one yourself on other deployments (docker run --pids-limit, or compose
    pids_limit).
  • New image name, and Coolify keeps your old compose file. New releases are
    published as hasanaboulhasan/pyrun; an install that still pulls
    hasanaboulhasan/pyrunner:latest stays on the last image published under that
    name, so change the image name when you update. On Coolify, Redeploy pulls
    the image named in the compose file the resource already has: a 1.x Docker
    Compose resource keeps the 1.x compose, without pids_limit: 2048,
    RATELIMIT_TRUSTED_PROXY_DEPTH=1 (login lockout and per-IP rate limits then
    see Traefik's address for every visitor) or the pass-through for
    DATABASE_URL, PYRUNNER_DATA_DB_URL and PYRUNNER_ALLOW_PRIVATE_NETWORK.
    Replace the resource's compose with the 2.0 docker-compose.coolify.yml
    (keep your environment values) before you redeploy. A Docker Image resource
    needs the new image name and RATELIMIT_TRUSTED_PROXY_DEPTH=1 added by hand.
    The Coolify install guide's Upgrading from 1.x section has the steps.
  • An upgraded instance keeps the name "PyRunner". 1.x saved its default
    instance name in the database, and the upgrade doesn't change a saved name, so
    the header, page titles and emails still say "PyRunner". Change it under
    Settings → General → Instance name if you want "PyRun".

Added

  • Workspace Brains — immutable revisions and deduplicated text blobs,
    a file browser with visibility and freshness badges, owner/admin edits,
    revision diffs, access grants, template creation, and ZIP or public GitHub
    import/export. Existing BrainOutside 1.0 content is preserved. Backup format
    1.13.0 includes opted-in brains and their full history; token grants must be
    recreated after restore.
  • Brain read and write tools — shared REST/MCP, signed script loopback,
    plugin BrainAPI (SDK 2.7) and grant-filtered agent tools. Captures produce
    validated proposals for owner/admin review, with diffs, conflict re-review,
    idempotent approval and cost caps. The confined reader assembles cited context;
    console requests run asynchronously. Optional vector search verifies results
    against canonical notes and the caller's current tier.
  • GitHub Brain sync — HTTP Git Data exports with explicit all-tier consent,
    a reviewed initial SHA and non-force updates. Pulls create reviewable proposals;
    optional polling never approves or exports. Credentials use workspace Secrets.
    Restore pauses polling and drops transient jobs while preserving history.
  • MCP and Connect — stateless Streamable HTTP at /mcp, shared API
    handlers, permission-filtered OpenAPI, and workspace connector instructions.
    Expiring read-only URL tokens are opt-in. Supports current per-request
    protocol metadata and older clients' initialization handshake; no sessions
    or streaming worker connections.
  • Unified API registry and REST — permission-scoped, hashed API tokens;
    one schema and handler per tool-call endpoint, GET for reads, a filtered
    /api/v1/_catalog, and /api/v1/_health. Existing datastore and plugin token
    scopes retain their reach. Brain endpoints share these handlers; REST names
    use dots (brain.get-note), MCP names hyphens (brain-get-note).
  • Vectors: embedding providers, vector stores and collections — Services
    gains two cards: Embedding Providers
    (OpenAI, Voyage, Jina, Z.AI, a local Ollama, or any OpenAI-compatible
    /v1/embeddings endpoint; Test records the vector dimension) and Vector
    Stores
    : the built-in Local store (one SQLite file per collection on the
    data volume, searched exactly with numpy), seeded and set as the default, and
    Qdrant servers, where each collection becomes one Qdrant collection named
    after its workspace. A new Vectors page per workspace creates collections,
    pins the provider's model and vector dimension to each one, runs real searches
    with JSON payload filters, browses points and deletes with a typed
    confirmation.
    • Scripts use the new pyrunner_vectors helper (standard library only,
      no keys in the run): get_collection("docs") then add_texts,
      add_vectors, search, search_vector, get, count, scroll,
      delete and embed. A plugin's collections are open to that plugin's
      scripts only; every other collection to any script in its workspace. Each
      run is braked at 2 000 embedding calls, 20 MB of embedded text and 200 000
      points written or deleted, so a runaway loop can't run up a provider bill.
    • Plugins get VectorAPI (plugin SDK 2.7): provision("docs", embedding_provider=..., dimension=..., metric=...) is idempotent and
      refuses to silently change a collection's model or dimension; get,
      list, delete and the same operations as the helper. Manifests declare
      provisions.vector_collections, and the plugin doctor checks it. The SDK
      Showcase example gains a Vectors card.
    • One filter grammar on both stores — equality, $in, ranges,
      $prefix, must / must_not — with strict types, array fields that match
      on any element, and scores that are always "higher is better", so a
      collection answers the same on Local and on Qdrant.
    • Native Qdrant access stays off unless the operator ticks Share
      credentials with runs
      on the connection; then a script's
      credentials() returns the URL, API key and physical collection name for
      qdrant-client.
    • Backup format 1.13.0 carries providers and connections (upserted by name,
      credentials encrypted as stored) and Local collections with their points
      inline, up to PYRUNNER_VECTOR_BACKUP_MAX_MB (default 50) each; Qdrant
      collections are carried as settings and reattach to the server on restore.
      Removing a plugin with remove data deletes the collections it owns.
  • Cron expression schedules — a sixth run mode that takes a standard
    5-field cron expression (0 9 * * 1-5 for weekday mornings, */15 9-17 * * *
    for every quarter hour during business hours), for anything the guided
    daily/weekly/monthly modes can't say. The form validates as you type and
    previews the next three runs; the plugin SDK gets ScheduleAPI.sync(..., mode="cron", cron="..."). Like the other clock-based modes the expression
    is written in the schedule's timezone and converted to UTC for the
    scheduler (day-of-week/month fields move with a midnight crossing, and the
    daily DST resync covers cron too), so 0 9 * * 1-5 in Asia/Tokyo really
    fires at 09:00 Tokyo time. Contributed by @cmcau (#5); timezone conversion
    added on merge. Backup format 1.8.0 carries the new field.
  • Yearly schedules — a run mode for "once a year on this date": pick a
    month, day, time and timezone. The scheduler stores the next real local
    occurrence (so the right DST offset applies for that date and a schedule
    set late in December can land in the next UTC year), and 29 February runs
    only in leap years — including when the timezone conversion pushes the UTC
    date onto 28 February or 1 March. Available to plugins via
    ScheduleAPI.sync(..., mode="yearly", month=, day=, time_str=, tz=) and
    carried by backups (format 1.8.0). Contributed by @djpfs (#16).
  • Account management from the command line — python manage.py user is
    the way back in when the console can't help: no email configured for reset
    links, a login lockout, or no admin who can sign in.
    user reset-password <email> sets a new password (prompted, or --generate
    to print a random one), signs out that account's sessions and clears its
    lockout. user create <email> [--admin], user promote, user demote,
    user unlock and user list cover the rest; the last admin can't be
    demoted. In Docker:
    `docker exec -it pyrunner python manage.py us...
Read more