Repository navigation
Instant print job delivery via Mercure (+ FrankenPHP Mercure 1.x fix) - #1628
Draft
francisrath wants to merge 18 commits into
Draft
francisrath wants to merge 18 commits into
francisrath wants to merge 18 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a Tools entry for the print agent page and documents the agent contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bulk lot action and hand-typed ids can contain spaces, ';' or empty entries, which agents don't have to accept. Also: lowercase pairing key, popup without opener, per-profile labels on the menu icons, and the docs now say which bytes form the HMAC key and how agents should check ts, replays and src. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agents register with an API token of their owner and identify themselves by a UUID, so renewing the token keeps them. Jobs are never re-queued automatically. Label profiles get an optional default printer id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agents send a heartbeat with their printers, list their pending jobs, claim one atomically and report the result. Only API tokens are accepted, and agents only see their owner's jobs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The label dialog offers the user's agent printers (default printer of the profile first), the label menu icon prints on the profile's default printer, and the parts table gets "print labels" actions. Without agent printers the buttons keep handing labels to the browser's local agent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The print agent page lists your agents with their printers and recent jobs, with "send again", cancel and remove. Label profiles get a default printer field that suggests the printer ids your agents report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- reject heartbeats over 64 KiB, and require API access for agents - the lot print action falls back to the dialog for parts without lots - cancelling a job is atomic against an agent claiming it - "send again" refuses printers the agent no longer reports - redirects after printing refuse control characters - label menus look up the user's printers once per request - the agents page shows the reported model, media and status Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jobs are published as private updates on urn:part-db:agent:<id>:jobs when MERCURE_PUBLIC_URL is set. Uses symfony/mercure directly; the hub is only built when push is on, so Part-DB works unchanged without a hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FrankenPHP 1.13 ships Mercure 1.x, which refuses the top-level publisher_jwt/subscriber_jwt directives as soon as a key is set, and no longer knows transport_url. Move the keys into an issuer block (MERCURE_TRUSTED_ISSUERS, default https://localhost) and use the bolt transport directive. An issuer needs a key, so the entrypoint uses a random publisher key when none is configured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…configured A missing or too short MERCURE_JWT_SECRET made every heartbeat fail, so agents stopped printing; now they just poll. Publishing uses short HTTP timeouts, so an unreachable hub can't hold up the Print button for 60 s. Document resource_identifier for hubs behind a TLS-terminating proxy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agents only need to list jobs when there are some, which halves their idle requests, with or without Mercure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 29, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1625; the third of three alternative PRs. It includes #1626 and #1627. The commits new here start at "feat(agents): publish new print jobs to Mercure".
With #1627, agents poll every 30 s. This PR lets Part-DB notify them through a Mercure hub, so a job prints about a second after the click. Polling stays as the fallback.
What it adds
urn:part-db:agent:<id>:jobs.symfony/mercure(0.9, Mercure protocol 1.0) directly, not the bundle. With an emptyMERCURE_URLoutside FrankenPHP, the bundle's Twig extension builds the hub on every Twig boot and throws, which brokecache:clearin my test. Here the hub is only built when push is on.mercure: {hub, topic, token}. The token is a 1-hour subscriber token for that one topic.pending, the number of pending jobs. Agents only list jobs when there are some, and an agent with a live subscription can check in less often. A lost event is still picked up at the next check-in.MERCURE_PUBLIC_URLis set. Push misconfigurations (an unreachable hub, an empty or too short secret) are only logged; agents keep polling.MERCURE_PUBLIC_URL,MERCURE_URL,MERCURE_JWT_SECRET,MERCURE_JWT_ISSUER. They are documented indocs/configuration.md, including the sidecar hub for the Apache image andresource_identifierbehind a TLS-terminating proxy.FrankenPHP image fix (useful on its own)
dunglas/frankenphp:1-php8.4-bookwormis now FrankenPHP 1.13.4 with Mercure 1.1.5. With the current.docker/frankenphp/Caddyfile:MERCURE_PUBLISHER_JWT_KEYis set, the server refuses to start. Thepublisher_jwt/subscriber_jwtdirectives "work only in compatibility mode" in Mercure 1.x.transport_urlis "not available anymore" and is ignored.This PR moves the keys into an
issuer {$MERCURE_TRUSTED_ISSUERS:https://localhost}block and usestransport bolt. An issuer block needs a key, so the entrypoint sets a random publisher key when none is configured. Keyless installs keep starting, and the hub then accepts no outside tokens.I checked both cases with
frankenphp validatein that image, but I have not built and run the full Part-DB image.Tests
AgentJobPusherTest(private update, publish failure doesn't break job creation, unreachable hub doesn't block, short secret), andAgentApiControllerTest(offer present or absent, token claims, misconfigured secret, pending count).🤖 Generated with Claude Code