Skip to content

Releases

acrylblends 1.1.0

Choose a tag to compare

@musichen musichen released this 30 Oct 03:35

1.1.0

A page follows its state over one WebSocket, so eight windows of a board no longer use up the browser's connections; a panel left open while the app restarts keeps working; and the five things the final macOS walk of the workshop guide found: the agent drives the page that asked, the app honours PORT and HOST, the appearance reaches every open window at once and the pages the agent builds, and the "Agent is driving" bar stays up for the whole run.

Added

  • watch(key, onValue) of /api/state/client.js. A cell of ctx.state is followed in a page with

    import { watch } from '/api/state/client.js'
    const stop = watch('board.cards', (cards) => render(cards))

    The callback gets the value now and after every change, as the JSON value the server holds (a string stays a string); stop() ends it. A third argument, (error) => ..., is told of a key that does not exist; without it the console gets one warning. One WebSocket (GET /api/state/ws) carries every key of the page. A WebSocket is not one of the six connections a browser allows to one host, and an event stream per key is: three keys in eight windows were 24 streams, and the windows past the sixth loaded only partly (found on macOS with 8 board windows).

  • The client retries a lost socket after a delay that grows from 250 ms to ten seconds, subscribes every key again and does not repeat a value the page already has. A server that is silent for 45 seconds counts as gone (it writes a ping every 15). A page that is hidden for good lets its connections go, because the back/forward cache would keep them open, and opens them again when it comes back.

  • Where the WebSocket cannot open (no ws package, a server that does not upgrade, a proxy that does not pass it) each key is followed by its event stream instead, with one console warning. GET /api/state/<key>/events?json=1 sends every value as JSON; without it a string is sent as it is, as before.

  • Who may open it. The upgrade needs a loopback peer on the app's own port and a loopback Host; the Origin, when the request has one, must be exactly the app's own origin and Sec-Fetch-Site absent or same-origin. A program on this machine that sends no Origin (a script, a test) is let in, as it is for the JSON and stream routes; a page of another origin gets 403, and so does a request that names another host (a name that only resolves to this machine, DNS rebinding), with or without an Origin.

  • ws is now a dependency of the acryl-state plugin (it was only the door's development dependency), so an app that follows state in production has it. It is loaded when the first socket is wanted.

  • The agent template's AGENTS.md tells the agent to follow state with watch and not with an EventSource.

  • Gates: verify:door section G holds all six connections of a page in real Chrome and checks that watch still delivers every change of a key; verify:desktop opens eight board windows that each follow three keys and checks that they all load and show each new count.

Fixed

  • A page left open while the app restarts keeps working. The panel's token belongs to one start of the app, and in development node --watch restarts the app at every change of a file, the guide's first edit included: a page that stayed open sent the old token, the socket closed with 4401 and the panel said "the panel was not accepted (reload the page and try again)". The server now says accepted once it has taken the token; when the page is refused (4401 on the socket, 403 on the stream) it asks for a new session and tries once more without the person seeing anything, and only a second refusal is shown. The terminal driver takes a fresh session when it opens too. The token stays per start (a token kept across starts would give a leaked one a longer life, and the retry is needed anyway). The door's page channel and the state socket never had a token and reconnect by themselves. Gate: verify:door section K restarts the app under an open page in real Chrome and checks that the same document asks the agent, is looked at through the door and follows a state key again, with no reload; with the retry switched off it fails with the message above.

Fixed (found by the macOS walk of 1.0.10)

  • The agent drives the page whose panel asked. With a browser tab left open from an earlier step and a desktop window, a question asked in the window's panel sent every door call to the tab: the window stayed where it was, the hidden tab moved, and the agent reported success. The panel now tells the door which page asked, and until the run ends the calls that name no window go to that page (a page that reloads or navigates during its run stays the one; an explicit --window still picks its own). The asking page comes before the page the door last sent somewhere and before the page in front. Gates: verify:door section H holds a second tab in front and asks from the panel's tab; unit tests in the door's channel and the panel's socket.
  • PORT and HOST are honoured. The agent and blank templates ignored PORT (PORT=3299 still listened on the app's own number 3991). The instance reads ACRYLBLENDS_PORT, then PORT, and ACRYLBLENDS_HOST, then HOST, for every template (the three templates that mapped them in main.ts no longer need to). docs/framework/guides/production.md has the four variables and a real-server example.
  • The appearance reaches every open window, and the pages the agent builds. The Settings page saved the choice to the browser's storage and only a reload applied it elsewhere. The template now serves /appearance.js: one script tag in a page's head applies the saved choice before the page paints, applies a change made in another window at once, and defines --bg, --fg, --muted, --surface, --border and --accent for the system's light and dark and for the person's choice. The home and settings pages use it, and the template's AGENTS.md tells the agent to put the tag in the pages it builds and colour them with the properties (the board the agent built used the system's colours and ignored the choice). Gate: verify:door section J changes the appearance in one tab of real Chrome and reads the other tab's background colour.
  • The "Agent is driving" bar stays up for the whole run. It showed only while one door call was in flight (a run is mostly the agent thinking: the walk saw it in none of 33 frames). It is now up from the start of a run asked for from that page's panel to its end, says "working on your request" between calls, and its Stop ends the run as well as the page's control (a new question allows the agent again). Gate: verify:door section I, with an agent tool that takes three seconds and calls nothing.

Changed

  • The names ws and client.js are routes under /api/state, so a state cell cannot be called either (ctx.state.cell('ws', 0) throws, saying so). No other key changes.

Upgrading

Run npx acrylblends upgrade in an app made before 1.1.0, then npm install. That takes the new acryl-state (and moves its ws dependency from devDependencies to dependencies, so npm install --omit=dev keeps it), the door, the agent panel and the instance (PORT and HOST). It does not touch your own files, so two things of this release need a copy from a fresh app (npx acrylblends init fresh --template agent): src/routes.ts (the /appearance.js route and the pages that use it) and AGENTS.md (what the agent is told about the appearance, watch, and the stale panel). The event stream routes are as they were: a page that opens its own EventSource still works, and still holds a connection for as long as it is open, so move it to watch.

acrylblends 1.0.10

Choose a tag to compare

@musichen musichen released this 29 Oct 21:55

1.0.10

A production install no longer carries vitest.

Fixed

  • acrylblends no longer declares vitest as a peer dependency. It was declared (optional) for the two conformance suites a provider of a port passes, acrylblends/db/conformance and acrylblends/kv/conformance; pnpm resolves an optional peer wherever the workspace has the package, so pnpm deploy --prod of an app pulled vitest, vite, rolldown, lightningcss and their native files into the production closure (about 30 MB, and files of the wrong platform for an archive built on another system). The suites now run on the vitest the provider's test hands them as a third argument, so the package imports no vitest at all (acrylblends/testing never did) and a suite can never see a second copy of vitest that is not the one running the test.

  • A new gate, pnpm run verify:vitest-peer, checks the packed package declares no vitest and no file of it imports vitest, that pnpm install --prod of a project that depends on it has no vitest, vite, rolldown or lightningcss in its closure even with vitest in the workspace beside it, and that a provider's project with its own vitest runs the kv conformance suite from acrylblends/kv/conformance under pnpm. It fails on 1.0.9.

  • window list names the path a window was opened on until its page reports its own address (it showed an empty path for a window still loading), and the timeout text adds "a page busy running a long script" to the causes it names.

Upgrading

Nothing to change for an app. A provider's test that imports describeKvConformance or describeDbConformance from the package now passes vitest as a third argument:

import * as vitest from 'vitest'
import { describeKvConformance } from 'acrylblends/kv/conformance'

describeKvConformance('my-provider', open, vitest)

Without it the call throws, saying what to pass. The copy that npx acrylblends add kv --suite (or db --suite) puts in an app, and the plugins' own suites in this repository, are unchanged: they import vitest from the app, and take two arguments.

acrylblends 1.0.9

Choose a tag to compare

@musichen musichen released this 29 Oct 21:02

1.0.9

Hardening after the first walks of 1.0.8's WebSocket channel, and a panel bug a CI run found.

Fixed

  • A message typed as the page loads is no longer dropped. The panel asks the server for the latest run when a page loads, and over its socket "there is none" takes a round trip; a message typed in that moment found the panel believing it was busy following a run, and was ignored (the text stayed in the box, nothing was asked). A panel is busy only once there is a run to show. A real-browser case delays that answer and fails on 1.0.8's client; it is how a CI run (Node 26, a slow runner) lost a message.
  • The "Working... m:ss" timer counts from the run's start, not from when the page loaded (a page that loads during a long run showed 0:05 on a run 25 minutes old).
  • A look right after a navigate or reload waits for the page to register again (up to two seconds) instead of failing with a timeout, and a read-only call that no page acknowledged gets one more try when a page registered while it waited.
  • The "no page answered" message names the real causes (a background tab, a page still loading, a page holding the browser's six connections with event streams of its own) so an agent no longer reloads and resizes windows to chase one that does not apply.
  • A new window never lands exactly on another, even on a small screen. 1.0.8's cascade wrapped to the screen's corner when the window did not fit, and on a 1024x768 screen that is where the first window already was (found by the new Electron gate on a Windows runner). The place is now the first free one: a step down and right, then from the corner, and on a screen no bigger than the window the step is kept (the window may run a little past the edge).
  • window list shows each window's place (1100x760@120,80), and the real-Electron gate asserts that a window opened beside another does not stack on it (1.0.8's cascade).

Upgrading

npx acrylblends@latest upgrade brings the door, the panel and the desktop shell.

acrylblends 1.0.8

Choose a tag to compare

@musichen musichen released this 29 Oct 20:12

1.0.8

Two windows of the desktop app, and the Settings the workshop's menu step needs.

Two live windows no longer stall

  • The cause. A browser lets a page hold six connections to one host, across all the windows of the app, and the door's channel, the panel's follow of a run and a board's own state were each an event stream held for as long as the page was open: two windows made six, and then every request of either window (the door's answers, Stop) queued forever while the server answered /health in a millisecond. 1.0.7's fix covered pages the browser kept after they were left, not two live ones.
  • The fix. The door's page channel and the panel's runs now travel over a WebSocket, which is not one of the six: a page opens it at once however many streams are open, and the answers go up the same socket (a request would queue). The event stream stays as the fallback (a server without the ws package, a proxy that cannot upgrade), and the page says so once in the console. A WebSocket has no CORS, so the upgrade is refused unless its Origin is exactly the app's own (a page on another port, a web site, a program that sends none are all refused, with tests); the panel's socket also needs its token as the first frame. ws is a development dependency of the door, so new apps have it. Checked in a real Chrome with six connections held (the door and the panel still answer; kept to the stream, the look fails in seconds with a reason) and in a real Electron with two windows that hold six streams between them.
  • Streams are the app's to count too. The agent template's AGENTS.md tells the in-app agent to keep to two event streams a page; the state client still opens one per cell (a ticket on the 1.1 list), and the panel's width over a page's fixed elements is another.

Desktop

  • A Settings item. The agent template serves a /settings page (the appearance: system, light or dark, kept in the browser) and the desktop app's menu has a Settings item (Cmd+, on macOS, in the application menu; Ctrl+, in File elsewhere) that opens it in a window, or focuses the window that shows it. acryl menu click --path "File>Settings..." does the same; an app without a /settings page gets no item. The real-Electron gate presses it and changes the setting through the door.
  • window open cascades. A new window sits a step down and to the right of the one it was opened from, not exactly on top.

Small

  • The panel's disabled buttons look disabled (Send kept its dark style while a run went).
  • The preflight names what each agent needs (pi (DeepSeek key found), codex (be logged in: codex login)).
  • The panel's refusal of a driver says why: a terminal driver (pick it in the panel), an agent that is not on the PATH, pi without a key.

Upgrading

npx acrylblends@latest upgrade brings the door, the panel and the desktop shell; run npm install for ws. The agent template's src/routes.ts (the /settings page) and AGENTS.md are your own files: copy them from a fresh npm create acrylblends@latest my-app agent if you want them.

acrylblends 1.0.7

Choose a tag to compare

@musichen musichen released this 29 Oct 12:40

1.0.7

The fixes from the first walk of the workshop on Windows, and one for every machine: a deployed app keeps what the agent built.

Before the workshop

  • npx acrylblends@latest preflight checks a machine before the first command and needs no app folder: Node 22.19+, npm, git, a coding agent the panel would offer (claude, codex, or pi with a DeepSeek key), a free port in the apps' range (so a firewall or security tool that blocks loopback shows up here), and gh for the publish step; on Windows also the PowerShell version (5.1 needs the guide's PowerShell lines) and long paths. One line per check: PASS with what was found, FIX with the exact command or address, NOTE for what the first blocks do without; exit 1 while anything needs a fix. (An older Node gets the bin's own plain refusal before anything else.)

Deploy

  • Production serves the extensions the agent installed. With NODE_ENV=production the agent template left the extensions row out (1.0.1), so an app whose pages were extensions, such as the workshop's board, answered 404 for them once deployed. The row now starts again, read-only: the extension folders of plugins/ (committed with the app) are loaded once from where they are; install, update, remove, reload and approve are refused; /ask, /extensions* and /tasks* stay unmounted. The start prints extensions (read-only): board, or extensions: none found in plugins/, and a WARNING when .acrylblends/extensions.json records an extension whose folder plugins/ lacks. verify:production builds one in development and serves it in production. An app made before 1.0.7 keeps its own src/app.ts: copy the production switch from a fresh npm create acrylblends@latest my-app agent (or upgrade the extensions plugin and set readOnly: true for it in production).

The door

  • Git Bash on Windows. acryl act navigate --path /nope arrived as C:/Program Files/Git/nope (MSYS rewrites an argument that looks like a Unix path before a native program sees it), so the agent of every attendee whose shell is Git Bash (pi's is) could not navigate. The shim now exports MSYS_NO_PATHCONV and MSYS2_ARG_CONV_EXCL, and the client turns a --path under the Git folder back into the path typed, with a warning. A test runs the shim through a real sh (and, on Windows, shows that MSYS rewrites the same argument without the exports).
  • Two tabs of the app no longer make a look wait 30 s. A page that was left stayed alive in the browser's back/forward cache with its event stream open; a browser allows six connections per host, so after a few navigations the live page's own answers queued behind dead pages. A page now lets go of its stream when it is left and reopens it if it is restored. The door also chooses the page that answers the same way every time (the page it just sent somewhere, then the one in front, then the most recent); pages acknowledge a call, a read-only call (look, act wait) to a page that does not acknowledge in 3 s goes to the next page and puts the silent one last, and when none answers it says so and what to do, instead of waiting 30 s. verify:door drives two real tabs.
  • A field is a secret by what it is. Any text field whose label contained "card" was hidden from the agent, so it could not type the title of a card on a board. A password input, an autocomplete of a credential or a payment detail, a value in the shape of a key or token, and labels that say so in plain words (password, API key, card number, CVV, IBAN, ...) are hidden; "Card title" is not.

The panel

  • It sits beside the page: the page gets narrower by the panel's width while the panel is open, so the header and right-hand columns stay in view.
  • It renders the agent's answer as Markdown (headings, lists, code, bold, italic, links), safely: the answer is parsed to a tree and built with createElement and textContent, so markup, scripts and javascript: links stay text.
  • It says it is working, with the time elapsed, while a run is going: a coding agent started headless prints nothing for minutes.

Upgrading

npx acrylblends@latest upgrade brings the door, the panel and the extensions plugin. The agent template's src/app.ts and AGENTS.md are your own files; copy their production switch and the "Drive the app" paragraph from a fresh npm create acrylblends@latest my-app agent.

acrylblends 1.0.6

Choose a tag to compare

@musichen musichen released this 29 Oct 11:37

1.0.6

The desktop app's windows and menu are door verbs: an agent that lives in the app can open a window, drive it, resize it and press a menu item, and cannot end its own session by accident.

Windows and menu (desktop app)

  • acryl window list|open|focus|close|resize and acryl menu list|click. window open --path /settings opens a new window on a path of the app and answers its id; menu click --path "File>New Note" presses a menu item by the labels menu list prints. They are in the CLI, MCP and the in-app agent's tools like every verb. Settings are a path of the app (build them at /settings) or a menu item.
  • --window <id> on look and act drives the page of that window instead of the page used last. Each window tells the door which it is (an AcrylWindow/<id> suffix of its user agent), so it works for windows the agent opened itself and for the first one.
  • It works when the window is attached to npm run dev. The door lives in the server, which is another process then. The Electron main process connects to the door the way a page does (over /api/acryl-door/shell/*, which needs the run's secret, so no page can open it) and follows the server across restarts.
  • The session cannot be ended by accident. window close on the last window, and a menu item that ends the app (Quit, Close Window on the last window), are refused unless the call says --force; menu list marks them. Every call is in .acryl/audit/ui-control.jsonl with the window id. With no desktop app attached the verbs answer one line: no desktop window is attached; run npm run desktop.
  • A new gate, pnpm run verify:desktop, runs all of it in a real Electron (attached to a dev server, the app's own server, a dev restart, the guards); CI runs it on Linux under a virtual display and on Windows. Design: the Electron adapter.

The agent template

  • AGENTS.md tells the in-app agent how to drive the app it builds (look, act, a path the app does not serve answers 404 at /nope and the window shows a 404 page, the window and menu verbs, the --force caution) and to build settings as a page at /settings.

The package

  • A clean tarball. 1.0.5 was packed from a working tree that held 95 stray plugins/acryl-router/src/**/*.d.ts files (declarations beside their sources; inert, but every new app received them). 1.0.6 is packed from a fresh clone of the release commit, and verify:tarball now fails on a declaration file beside its source.
  • packages/cli/lib (build output) is no longer tracked in the repository; a fresh clone builds and packs the same tarball.

Upgrading

npx acrylblends@latest upgrade brings the door, the router and the desktop shell. An agent-template app's own AGENTS.md is its own file: copy the "Drive the app you build" paragraph from a fresh npm create acrylblends@latest my-app agent if you want the in-app agent to know the new verbs.

acrylblends 1.0.5

Choose a tag to compare

@musichen musichen released this 29 Oct 07:11

1.0.5

The first-run fixes from the workshop guide's walk on 1.0.4, and the app's own agent driving the page with a real model.

The agent template

  • A new app from the agent template has the agent panel. npm create acrylblends@latest my-app agent answers /api/agent-panel/session with no add step; add driver-pty and add driver-acp mount after the panel's row. (An agent-template app made earlier gets it with npx acrylblends add agent-panel.) The panel starts nothing in production.
  • test/builder.test.ts builds a plugin with a name and a path of its own (builder-probe-<random>), not /hello. A route a person adds at /hello (the workshop's first route) shadowed the plugin the test installs, and the template's own npm test failed on 1.0.4. The packed-install gate now adds /hello, /hi and /api to the agent template's routes and runs its tests.
  • The default step budget is 12 (it was 6: looking, pressing and looking again used the six before the answer).

The panel

  • The tool lines say what the tool is doing. A coding agent started in the folder reports its own log; the panel showed the driver's name once per event ("codex, codex, codex"). It now shows the command (codex: $ npm test) or the sentence, drops the agent's bookkeeping (hooks, token counts, the output of a command that succeeded), and shows a failure; the app's own agent shows the tool and its arguments.
  • The panel's script is added to every HTML page the app answers, including the pages of a plugin that loads at boot (it used the late-mount way and was missing after a restart). It uses ctx.router.wrap, like the door. A gate now checks the door's and the panel's scripts on a page of a plugin installed live, one loaded at boot, and one mounted as a row of blend.yaml ahead of both.

The door and the app's own agent

  • The door's verbs reach a real model with their schemas. The tools registry takes an optional JSON Schema per tool (parameters), the openai plugin sends it, and the door registers door_look and door_act with the schema MCP lists, so a model knows an act needs its op. With a DeepSeek key in the server's environment (ACRYLBLENDS_MODEL_URL, ACRYLBLENDS_MODEL, ACRYLBLENDS_MODEL_KEY) the panel's built-in agent looks at the open page and presses its buttons: no Codex or Claude login needed. Recorded in the evidence page.

Desktop

  • npm run desktop while npm run dev runs attaches to the running server instead of failing with "another process serves the door": the window opens on the dev server found through .acryl/door.json (only a live process on a loopback address that answers), prints a line saying so, and closing the window leaves that server running.

Rows, extensions and npm run dev

  • A row can import the framework by name. A plugin mounted as a row of blend.yaml (imported by src/app.ts before anything runs) that imports acrylblends, acrylblends/body-limit or acryl-router used to stop the app with Cannot find package 'acrylblends', even after npm install acrylblends. The preload every script uses (plugins/acryl-node-server/src/register.ts) now resolves those names the way the extensions do, for the app's own files. verify:door mounts such a row and fails on 1.0.4.
  • The agent template's AGENTS.md says what to build: a feature written while the app runs is an extension (plugins/<name>/, installed with .acryl/bin/acryl plugin add plugins/<name>); a row of blend.yaml only when the person asks for one.
  • A live install no longer fights npm run dev. The dev script's watcher saw the staged copy of an extension being replaced and restarted the app halfway through an install or a reload (a loop, with the door answering "no running app"). An unchanged extension reuses its staged copy, and nothing staged is deleted while the watcher runs (a start without it prunes). A new gate, verify:dev-install, installs, reloads and restarts under npm run dev.
  • A mistyped path no longer strands the window. act navigate --path /nope used to leave the window on a bare "404 Not Found" with no door script, and every later call answered no-window until a person opened a real page. In development the 404 of a page request is now a small HTML page (it names the path and links back) that carries the door's script, so look, act click and act navigate keep working; act navigate says what the app answered (404 at /nope, 200 at /p1). verify:door navigates to a missing path, looks, clicks the way back and navigates on.
  • The agent template's home page says the panel is on the page (it still told you to add it).

Upgrading

npx acrylblends@latest upgrade brings every plugin copied into an existing app (the door, the router, the panel, the extensions, the preload, the agent, the desktop shell). An agent-template app's own test/builder.test.ts, blend.yaml and src/app.ts are its own files: copy them from a fresh npm create acrylblends@latest my-app agent if a route of yours shadows the old test or you want the panel row.

acrylblends 1.0.4

Choose a tag to compare

@musichen musichen released this 29 Oct 06:05

1.0.4

An agent that builds a page can now look at it and click through it in the same run. For the workshop's "built from the inside, then driven" demo: the page an agent builds is seen by the door, the page refreshes when the app changes, and the panel keeps the run alive across those refreshes.

The door

  • act reload and act navigate --path /settings (CLI, MCP and the built-in chat: one verb table). They answer first, move the page, and then wait up to 15 s for the new page to connect to the door, so the next look sees it. A path must start with one / and name no other site; a path whose page does not load the door fails with a timeout that says so.
  • The open pages reload after a live plugin change (plugin add, plugin remove, plugin reload, except for a plugin that was already as it should be); the verb answers once they are back. reloadOnChange: false in the door row turns it off.
  • The door's page script is added to every HTML page in development, including the pages of a plugin that loaded at boot, so a page an agent writes can be looked at and driven with no tag. Production is untouched (it has no door).
  • The .acryl/bin/acryl command uses a real Node when the app runs in Electron (a node of 22.19 or newer on the PATH), so each call no longer prints a code-signing complaint on macOS.

The router

  • ctx.router.wrap(middleware) runs a middleware around every request, whenever the plugin that asks was mounted; a middleware in an ordinary mount wraps only the routes mounted after it. It goes away with the calling plugin.

The agent panel

  • A run belongs to the app, not to the page. A page that reloads, closes or moves no longer cancels the agent: the next page that loads shows the question and the events so far (GET /api/agent-panel/run) and follows the run to its end. Only Stop (POST /api/agent-panel/stop) cancels it. A run that ended before a page loaded is shown there, and does not reload it again.

Docs

  • The agent docs (AGENTS.md, the app guide) say that look and act are how you check your own UI work, and name act reload and act navigate. The workshop pages say to run the agent from a clean configuration and to word tasks as "do it now, without asking for approval". The recorded run is in Build, then check it through the door.

Upgrading

An app made by an earlier release gets all of it with npx acrylblends@latest upgrade (the door, the router and the panel are library plugins). An app keeps working without it.

acrylblends 1.0.3

Choose a tag to compare

@musichen musichen released this 29 Oct 01:50

1.0.3

Fixes from the workshop rehearsals, and a correctness fix that matters on the Node most Mac users now have.

Correctness

  • Node 26. A request body that arrived complete but whose connection closed before the app read it could be cut to its first chunk on Node 26 (Homebrew's current node): the adapter read the stream's buffer once, and Node 26 returns only the first chunk of a destroyed stream's buffer in one read(). It now reads until the stream says there is no more, which is the contract of Readable#read() on every version. Node 26 joins the CI matrix (the full check on Linux, and the framework packages on Linux, macOS and Windows).

Starting an app

  • A relative path in a blend.yaml row is read from the app's folder. config: { migrations: migrations } (and path: data/app.sqlite) on the acryl-db-sqlite row, and path on acryl-kv-json, used to stop the app with "no file base URL". The add db hint and the data guide show the row.
  • create-acrylblends takes a GitHub address pasted from the browser as a source or a registry (https://github.com/owner/repo, with .git or /tree/<ref>), like github:owner/repo.

Behind the release

  • Two flaky tests were fixed at the root: the node-server spec served a fixture that a ported test makes unreadable for one test while another worker read it, and the PTY gate read the audit log before the line that follows the process's death.
  • The packed-install gate now runs the refusal of an ACRYL starter through npm exec, and an app that mounts the database with a relative migrations path (the row and its registry entry written the way the hint says).

acrylblends 1.0.2

Choose a tag to compare

@musichen musichen released this 28 Oct 20:26

1.0.2

The release for people starting from nothing: every friction found by walking the published 1.0.0 as a newcomer on macOS and Linux, fixed, plus one more production default.

Security

The chat template is closed in production. Anyone who could reach a deployed chat app spent the owner's model key. With NODE_ENV=production every route but GET /health now answers 503 until you choose: CHAT_ACCESS_TOKEN (the browser asks for a login: any user name, the token as the password), or ACRYL_PRODUCTION_KEEP=chat to make it public on purpose, which prints a WARNING at start and limits one address to CHAT_RATE_PER_MINUTE messages a minute (default 20). Development is unchanged. An app made by 1.0.0 or 1.0.1 keeps its own src/routes.ts: copy src/access.ts, src/chat-app.ts and src/routes.ts from a fresh acrylblends init my-app --template chat, or put your own login in front. See What runs in production.

Start from the registry, from npm alone

  • npm create acrylblends@latest my-app -- --from <id> makes an app from a registry starter (npm needs the -- before the options). --from also takes github:owner/repo, @scope/name, a .tgz and a template folder; --registry github:owner/repo (or a folder) reads another registry. Nothing is installed or run, the manifest of the new app is its own (private, named after its folder, no licence of the starter's), and the secrets scan runs first.
  • Registry entries declare their runtime. The index now carries runtime: acrylblends | acryl. A starter for the ACRYL app (acryl.gtd and the other acryl.* Blueprints) is refused with one line: it is an ACRYL app starter, open it in the ACRYL app. create-acrylblends never writes disabled rows.
  • Three starters that work today: acrylblends.notes-desktop (the notes app with a desktop window), acrylblends.chat and acrylblends.agent, made from the templates by the release routine. acrylblends.github.io/blends shows the runtime as a badge and filters on it.

The newcomer path

  • acrylblends add agent-panel mounts itself. It adds the panel's row and its registration (and driver-pty and driver-acp after it), or refuses before copying anything, naming the rows the app lacks (agent, extensions) and where they come from (the agent template). A blank app no longer ends in a silent 404.
  • pi is offered in the panel when it has a DeepSeek key in the environment or its own stored login (~/.pi/agent/auth.json). The docs say that the panel's coding agents use the login of the person who runs the app.
  • node-pty: add driver-pty explains the npm 11 / pnpm warning about its install script (the prebuilt binaries work without it; the exact approve and rebuild commands are printed and documented).
  • todo prints the URL it serves (/todo), create-acrylblends prints its Next: block once, and init --desktop says what the desktop shell added and why.
  • npx acrylblends@latest upgrade is the documented form, with the reason: an app's own older copy shadows a bare npx acrylblends and has no upgrade verb (it prints its help and exits 0, or says "unknown command" and exits 2).
  • Node 22.19 or newer is checked first. The two commands and every new app (scripts/check-node.cjs, run before dev, start, test and check) stop an older Node with "Node 22.19+ needed, you have 18.x: install it from nodejs.org or with nvm".
  • Linux, Ubuntu 23.10 and later: npm run desktop no longer aborts at Chromium's sandbox. Where kernel.apparmor_restrict_unprivileged_userns=1, and only there, it starts Electron with --no-sandbox and prints one line saying why; the desktop page says how to keep the sandbox with an AppArmor profile.

Things to know

  • A registry starter is a whole app, about 2.6 MB with its plugin copies, so a registry grows by that much per entry.
  • acrylblends upgrade still does not edit an app's own src/ files (the second pass is planned).