Commit Graph
799 Commits
Author SHA1 Message Date
stroblme fae85f5ecb Flows and dashboards get ids: the store is addressed by id with a key index, the first start gives every document its key as id, fluksio store migrate --dry-run and ls; the rename routes are gone 2026-11-02 16:54:11 +01:00
stroblme de13c48c90 CI stacks come up on the runner again: mosquitto reads its config from /etc/mosquitto, where Ubuntu's AppArmor profile lets it, and Playwright reaches the app on localhost, a secure context for the clipboard spec
Docs / docs (push) Successful in 20s
Playwright Tests / test-playwright (1, 2) (push) Successful in 3m1s
Playwright Tests / test-playwright (2, 2) (push) Successful in 2m1s
pre-commit / pre-commit (push) Successful in 3m1s
Test Backend / test-backend (3.12) (push) Successful in 4m30s
Test Backend / test-backend (3.14) (push) Successful in 4m18s
Compose Smoke Test / test-compose (push) Successful in 28s
Playwright Tests / merge-reports (push) Successful in 1m17s
2026-11-02 07:22:32 +01:00
stroblme c675d2041c Ring face's unit sized inline relative to the reading, so pre-commit's arbitrary font size check passes 2026-11-02 07:03:39 +01:00
stroblme 66588c58aa Merge the v0.2.0 worker range fix
Docs / docs (push) Successful in 20s
Playwright Tests / test-playwright (1, 2) (push) Failing after 29s
Playwright Tests / test-playwright (2, 2) (push) Failing after 26s
pre-commit / pre-commit (push) Failing after 2m14s
Test Backend / test-backend (3.12) (push) Successful in 4m44s
Test Backend / test-backend (3.14) (push) Successful in 4m30s
Compose Smoke Test / test-compose (push) Failing after 22s
Playwright Tests / merge-reports (push) Failing after 2m19s
2026-11-01 13:47:19 +01:00
stroblme 7919cf5670 fluksio requires fluksio-worker 0.2.x: the 0.1 range left the 0.2.0 wheels unresolvable, and publish stopped on it
Publish / publish (push) Successful in 47s
v0.2.0
2026-11-01 13:47:01 +01:00
stroblme 78e1011089 Back to development
Docs / docs (push) Successful in 21s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m7s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m36s
Test Backend / test-backend (3.14) (push) Successful in 4m23s
Compose Smoke Test / test-compose (push) Failing after 26s
Playwright Tests / merge-reports (push) Failing after 2m20s
2026-11-01 13:21:13 +01:00
stroblme 5e3a2f9a62 Release v0.2.0
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m9s
Playwright Tests / test-playwright (2, 2) (push) Failing after 20s
Playwright Tests / merge-reports (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 41s
Test Backend / test-backend (3.12) (push) Canceled after 0s
Test Backend / test-backend (3.14) (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Publish / publish (push) Failing after 24s
2026-11-01 13:18:37 +01:00
stroblme 64ef7c84d6 Portal members: a request that loses the adoption race reads back the winner's user instead of being refused
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m16s
Playwright Tests / test-playwright (2, 2) (push) Failing after 19s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m51s
Test Backend / test-backend (3.14) (push) Successful in 4m32s
Compose Smoke Test / test-compose (push) Failing after 26s
Playwright Tests / merge-reports (push) Failing after 2m17s
2026-11-01 11:21:42 +01:00
stroblme df1071ce73 Message suggestions stay open when an empty field is clicked: the field's own focus no longer counts as outside the list 2026-11-01 11:17:18 +01:00
stroblme e6614ff378 Image builds with uv 0.13.6; follow-redirects 1.16.2 and seroval 1.6.12; getting started covers Docker on Raspberry Pi OS and Compose 2.24
Docs / docs (push) Successful in 24s
Playwright Tests / test-playwright (1, 2) (push) Failing after 2m28s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m44s
Test Backend / test-backend (3.14) (push) Successful in 4m34s
Compose Smoke Test / test-compose (push) Failing after 25s
Playwright Tests / merge-reports (push) Failing after 2m21s
2026-11-01 10:59:51 +01:00
stroblme d8eb6968f0 test_wren: load numpy itself, so the BLAS test passes alone 2026-11-01 10:59:45 +01:00
stroblme b41684a608 UI: 409 toasts read the message, chat sheet named per tab, 44px phone sidebar trigger, button grids shade as controls, unambiguous chart label key, optional settings typed and cleared to null 2026-11-01 10:59:43 +01:00
stroblme 7f40ea62de Duplicating a copy names it after the original; a failed duplicate or delete says why 2026-11-01 10:59:36 +01:00
stroblme 8f4c7d8ff1 Worker: wait for a node process's exit status before telling a signal from a failure 2026-11-01 10:59:35 +01:00
stroblme 7a7315043d Alerts: a delivery no channel took leaves the cooldown unspent 2026-11-01 10:59:28 +01:00
stroblme 83c5f2dfcf A run that starts clears its 'Waiting for a worker' reason 2026-11-01 10:59:21 +01:00
stroblme 48d4be6093 Starting a running flow restarts its nodes, so interval and cron Injects keep firing; a params-dict node is told settings arrive by name 2026-11-01 10:59:14 +01:00
Melvin Strobl e0f5ddc3a4 Devices may publish to their own out ports' messages (a level set on the device itself)
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Failing after 36s
Playwright Tests / test-playwright (2, 2) (push) Failing after 20s
pre-commit / pre-commit (push) Failing after 2m13s
Test Backend / test-backend (3.12) (push) Successful in 4m32s
Test Backend / test-backend (3.14) (push) Successful in 4m18s
Compose Smoke Test / test-compose (push) Failing after 26s
Playwright Tests / merge-reports (push) Failing after 2m16s
2026-10-29 21:58:38 +01:00
Melvin Strobl d2e8c01785 Getting started: own settings go in compose.override.yml, uv waits 120 s for a download 2026-10-29 21:35:50 +01:00
stroblme 75b3a93094 Fix work recovery and reduce engine and chart overhead
Docs / docs (push) Successful in 33s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m37s
Playwright Tests / test-playwright (2, 2) (push) Failing after 27s
pre-commit / pre-commit (push) Failing after 2m37s
Test Backend / test-backend (3.12) (push) Successful in 5m7s
Test Backend / test-backend (3.14) (push) Successful in 5m2s
Compose Smoke Test / test-compose (push) Failing after 28s
Playwright Tests / merge-reports (push) Failing after 2m29s
2026-10-29 14:24:17 +01:00
stroblme 20a0853f69 Entry chunk: signup and the form schemas leave it, build runs without warnings
Docs / docs (push) Successful in 20s
pre-commit / pre-commit (push) Failing after 2m16s
Test Backend / test-backend (3.12) (push) Successful in 4m49s
Test Backend / test-backend (3.14) (push) Successful in 4m33s
The entry is 798 kB (259 kB gzipped) instead of 927 kB (298 kB). Signup's
`export default` kept it unsplit, and login, recover and reset-password left a
bare `z.object(...)` statement in their route stubs (reset-password also read
its search through Zod), which held all of Zod and react-hook-form in the entry.
The schemas are factories now, reset-password validates its search by hand.

The shell routes keep their component in the entry on purpose
(`codeSplitGroupings: []`, no `export default`): splitting them out measured
about 60 chunks per signed-in screen and a slower flow list. The Zod
`@__PURE__` comment warning is filtered and the chunk limit is 850 kB, each with
a comment saying why.

At 4x CPU on slow 4G the signed-in screens load 5 to 9% sooner, view 6%; login
13% later (the form chunk is one more hop). On a LAN they are within noise.
2026-10-28 18:02:06 +01:00
stroblme 87e750c776 Code editor: CodeMirror 6 replaces Monaco
Node source (Python) and the JSON dialog share one CodeMirror editor with
find and replace, autocomplete, folding, indent guides, comment toggle and a
JSON lint, themed from the CSS tokens so light and dark follow the .dark class
without a second palette. Programmatic value changes no longer fire onChange,
blur ignores focus moving into the find bar, readOnly keeps selection and copy.

The editor chunk is 470 kB (157 kB gzipped) against 2.7 MB (704 kB) for Monaco,
which also pulled in a 1.2 MB JSON mode, two workers and a font. At 4x CPU on
slow 4G a node panel is ready in 5.7 s instead of 10.8 s, the JSON dialog in
1.5 s instead of 6.7 s. Typing without a key delay keeps every character.

monaco-editor, @monaco-editor/react and the dompurify override are removed.
Specs: typed code reaches the node source, a JSON setting saves only text that
parses. Docs name Tab and Ctrl-M.
2026-10-28 17:32:49 +01:00
stroblme 5d0ea480bf Alerts: a Telegram channel, one chat or group each, connected by a one-time link
The channel sends through the Bot API with httpx only: HTML with escaped text,
cut to fit a message, silent for a warning, a short 429 waited out once, and
errors that say what to do about a blocked bot, a lost chat or a bad token and
never quote the request URL. The bot token is a stored secret: saving a
channel refuses a literal one, the card's "Store token" writes the secret and
a reference, and no endpoint returns it.

A chat id is never typed. Connect starts a ten minute code, and the chat that
sends `/start <code>` back through the bot is the one connected, so it is a
chat somebody opened a link for. The engine fetches updates only while a code
is waiting (no standing poll, no webhook, no inbound address) and ignores
everything else without a reply. Connect needs a saved form, and the form
takes the connected chat in without touching what was typed since.

The token is part of every Bot API URL, so the httpx logger gets the same
filter the request lines have, which now also masks an httpx.URL argument.
2026-10-28 13:06:58 +01:00
stroblme bfaaa7cd03 Alerts: cooldowns survive a restart, and a connection must stay down a minute before it alerts
The alert manager kept its cooldowns and its ceiling per hour in memory, so a
node that stayed down was announced again by every restart. Each delivered
alert is now a row in the new alert_sent table (additive migration), loaded
for the last seven days when the manager starts and pruned as new ones come
in. A ledger that fails never stops an alert. The manager's clock is wall
time for the same reason.

A node_health "down" alert also waits 60 s (HEALTH_GRACE_S) in a pending task
per node; a later event that is not "down" cancels it, as does stopping the
manager, so a broker restarting with the stack alerts nobody.

The cooldown used to be stamped before the ceiling per hour was checked, so
an alert the ceiling held back still spent its cooldown. It is stamped only
once the alert is sent now.
2026-10-28 12:46:42 +01:00
stroblme 00842fa175 Health summary: a node is failing after two failures in a row; one is a warning, a good run ends both
The summary read degraded for 15 minutes after any single node_error, even
once the node had run well again and including one bad input from a visitor.
The controller now counts a node's failures since it last ran well and notes
when it recovered; both carry across rebuilds and an acknowledgement resets
the count. Two in a row inside the 15 minute window is "failing" (a problem),
one is listed under warnings, and nodes.failing keeps meaning "failing now".
2026-10-28 12:42:35 +01:00
stroblme 2dc20911a3 Alerts: configuration, test and search entries are a superuser's; subscribing a browser stays open
Any signed-in account could save an ntfy channel with its own `server` and a
`{"$secret": ...}` token and press Test, which sent the stored secret to that
host. The config GET/PUT, the test route and the global search's alert entries
now answer to superusers, like /secrets. The webpush key and subscribe routes
stay open to every account, and the Alerts page shows a non-superuser only the
button for its own browser. The events table in the docs gains run_finished.
2026-10-28 12:40:44 +01:00
stroblme 29f4d93c2a Search: node settings are indexed, with credentials masked on the server
/search gains a `setting` category, `<node>.<setting>` under its flow with the
node type and the value in a new `detail` field, so Ctrl-K finds which node
uses a broker or a timeout and opens the node. `setting_text` is the one place
that decides what may be said: a reference to a stored secret shows its name
(to superusers only, as the secrets list does), a plain value in an `x-secret`
field or under a key like password, token or api_key is masked and never
indexed, nested credentials in headers or query are masked, long values are
clipped. The global search ranks what contains the query as typed ahead of
loose spellings before it caps a group, which long setting values would
otherwise have crowded out.
2026-10-28 11:07:27 +01:00
stroblme 2e0ba32124 Node panel: ports read message first on inputs, code name is text until it differs
An input row now starts from its message and ends as the argument the code
takes it as ("as prompt"); an output keeps its key on top and the message
below. The name the code uses is muted text with a pencil while it follows the
message's last segment, and a field only when it differs. Device Follows ports
get the same order (message, then the port driven). A list port's item type
moves to the second row so it no longer squeezes the name, two ports deriving
one name get a warning, and the rows rebuilt use 44px touch targets on phones.
The stored node spec is unchanged.
2026-10-28 11:04:15 +01:00
stroblme c04e4883c2 MCP: mcp 2.3 (MCPServer, transport arguments on streamable_http_app, bearer read in middleware); tests for the 2026-07-28 revision and the resource check 2026-10-28 09:21:01 +01:00
stroblme c04d6b83c6 MCP tests: the SDK's own client connects over streamable HTTP with a bearer, and older protocol revisions still answer 2026-10-28 09:11:58 +01:00
stroblme a6487aeba9 Traefik access log drops the request path, where a hook's secret sits 2026-10-28 09:10:39 +01:00
stroblme 439daf5655 Backend: mcp 1.30, uv 0.13 and dnspython 2.9 for their advisories; MCP tokens checked against the resource URL 2026-10-28 08:42:07 +01:00
stroblme 9ed2f5fbea Frontend: tar, handlebars, dompurify and zod overridden to their fixed versions
Clears every bun audit finding but openapi-ts's own; the generated client is unchanged.
2026-10-28 08:42:06 +01:00
stroblme b5e1a4c5b2 Request log: a hook's secret is masked in uvicorn's lines and the tunnel connector's failure line 2026-10-28 08:41:52 +01:00
stroblme 5c7a8773ad SDK: sync and run import the declaring module in a child process
The command line no longer imports a research module, so import-time GPU work
ends with a short-lived child instead of holding a CUDA context for the length
of a followed run. fluksio.sdk.declare starts the child, which writes each
flow's document and shims as JSON; Declared stands in for Flow in sync().
Errors keep their type and message, a child's traceback reaches the user.
2026-10-28 08:11:14 +01:00
stroblme 34243e6902 SDK: artifacts work outside a node, run --follow prints status and queue waits, fluksio cancel
save_artifact/load_artifact outside a node use the worker's own store code in
$FLUKSIO_ARTIFACT_DIR or <tmp>/fluksio-artifacts, so a node can be called by
hand. --follow prints status changes and each node's resource wait; the
placer's waiting snapshot now carries the run. fluksio cancel <run>...
2026-10-28 08:07:35 +01:00
stroblme 4c2df681d3 Flow canvas: label and edge popovers follow pan and zoom, the editors' dock is a full-width bar on a phone
- InputInspector hangs off the label's element and EdgeInspector off a point in flow coordinates (ViewportPortal), both re-measured every frame while open and hidden once the anchor leaves the view. A label that leaves the canvas closes its popover.
- Dock: below md a bar across the width, flush with the bottom edge (bg-card, top border, safe-area inset, no shadow); from md the floating pill as before. The flow dock drops the Run caption and the dashboard editor clears the bar with its own padding, so the editing state fits one row from 360px.
- Tests: the search spec checks the popover rides along with a pan; the mobile spec checks both docks are a full-width bar flush with the bottom.
2026-10-28 07:38:04 +01:00
stroblme 5a831ba758 Flow canvas: second value restarts the edge pulse, dock uses the phone's width, search arrives on an input's label popover
- LiveEdge: a value inside a running pulse restarts the animation by hand, since the class is already on and would show nothing.
- Dock, FlowDock: the bar is centred on left-1/2, where a shrink-wrapped box gets only the half of the screen to the right and wrapped into a 195px pill. w-max with the viewport cap keeps one row whenever the buttons fit. The dashboard editor's dock had the same squeeze.
- FlowEditor: arriving on an input drawn on the canvas centres its label and opens the label popover once the view is fit, as clicking it does. An input the canvas does not draw still opens the flow settings.
- Tests: the phone dock may not be confined to half the screen; the search spec reads the popover first and reaches the settings through its pencil.
2026-10-27 21:50:18 +01:00
stroblme f52d9c3077 Service node ends its call when the loop cancels every task; test order and timing leaks
_serve_once treated a cancelled call future as finished, so an event loop
cancelling all tasks at once (shutdown) never sent the worker a cancel and the
call's thread kept asyncio.run waiting for SILENCE_S. The cameras audio test
now removes its voice flow and device, the unpaired-frame test waits for its
first frame before the unpair, and test_connector no longer inherits the
controller an app test bound to demand.
2026-10-27 21:23:40 +01:00
stroblme 540c915c5e development.md: the CI section lists all six workflows 2026-10-27 17:53:03 +01:00
stroblme e37aa2bdd7 Backend messages and field descriptions that reach the UI lose their em-dashes
Per the Microcopy rule: size and duration errors, the flavor errors, run
parameter errors, the Resources field descriptions, 401/409/400 details,
secret errors, the websocket node description and its on_connect setting.
The generated client follows.
2026-10-27 17:52:55 +01:00
stroblme 4cb028de41 Chat: one phone header button, quick-prompt chips, double-click to reuse a message
The phone header carries a single speech bubble: Control on Home, the
assistant elsewhere. Chips above the input offer the user's most sent
messages per tab, then starters; a tap or a double-click on a sent or
heard message fills the input without sending.
2026-10-27 17:39:36 +01:00
Melvin Strobl 00cda9d2f1 connector: wanted(port) says whether anyone watches or reads a port 2026-10-27 15:20:24 +01:00
Melvin Strobl 7b453af3fe wren node: calibration setting, passed to wren's training as the installation's calibration records 2026-10-26 22:17:48 +01:00
Melvin Strobl ba75c78820 wren node: install hints and docs pin the public v2.0.0 tag 2026-10-25 17:17:17 +01:00
Melvin Strobl b443240f92 up 2026-10-25 14:56:25 +01:00
Melvin Strobl 82c64a10b6 Make documentation standalone and generalize examples 2026-10-25 14:43:20 +01:00
Melvin Strobl 96ba512d40 wren node: untrained targets left out of the prompt, a read beside a held-back set goes on whole 2026-10-25 12:09:37 +01:00
stroblme e9941b770d wren node: Train in the panel, as a run of wren's training flow
POST/GET /flows/{name}/nodes/{id}/train submit wren_train on the node's
targets, examples and tests and read its latest run (run.for_node,
cause "train"); "Use this model" writes `model` into the draft.
2026-10-25 01:18:54 +02:00
stroblme 07fa5a2dc2 wren node: targets from the wiring, on wren 2
Every output is a target and every input besides prompt a reading. A
targets setting gives descriptions, values, ranges and per-target
guards, model takes a trained pack's artifact digest, min_confidence
defaults to the model's threshold. A command is applied whole or not at
all, and targets the model was not trained on read degraded. The home
path (area/kind ports, groups, brightness, temperature answers) is gone.
2026-10-24 21:28:42 +02:00