Skip to content

Merge 1.2.0 alpha cycle into main - #23

Open
GhostTypes wants to merge 59 commits into
mainfrom
alpha
Open

GhostTypes wants to merge 59 commits into
mainfrom
alpha

Conversation

@GhostTypes

Copy link
Copy Markdown
Member

Merges the 1.2.0 alpha cycle (alpha.1 through alpha.8, plus the changes since alpha.8) into main ahead of the 1.2.0 stable release.

Highlights since 1.1.0

  • Creator 5 and Creator 5 Pro support. Connect, monitor, and control both models, with per-tool, bed, and chamber temperatures and a Material Station card with a slot editor.
  • Upload a print job from the browser. .gcode, .gx, and .3mf, with the thumbnail, slicer details, and tool-to-slot material matching.
  • SSH tools for the Adventurer 5M, 5M Pro, and AD5X. A file manager, a calibration assistant, and remote reboot.
  • Better Spoolman tracking. Per-job, per-tool charging on material-station printers, and single-spool tracking for an AD5X without a station.
  • Camera video runs through the authenticated WebUI port. Only one port needs forwarding.
  • Faster connects. Modern printers are identified from the discovery broadcast.
  • Thumbnail previews, printer selection by URL (?ip= / ?serial=), and an About window that shows the version (Version info #22).
  • Fixes: the ETA no longer moves while paused, a paused Creator 5 Pro no longer shows Unknown, logout revokes the session token, and WebUI start and shutdown races are fixed.

Security

  • axios, express (with proxy-addr, path-to-regexp, qs, and body-parser), ws, and form-data are updated past their advisories.
  • fast-xml-parser and fast-xml-builder are already at fixed versions on alpha.
  • Merging deletes the old npm package-lock.json, which all 86 open Dependabot alerts on main are filed against. Dependabot then rescans pnpm-lock.yaml.
  • Known remaining: adm-zip 0.5.x via @parallel-7/slicer-meta. It needs a slicer-meta release that moves to adm-zip ^0.6. Low exposure: slicer-meta only reads zip entries in memory, and only logged-in users can upload.

Upgrade notes for users coming from 1.1.0

  • Running from source: data/config.json and data/printer_details.json are no longer tracked. Back them up before git pull.
  • Dependencies now install from public npm with pnpm. No GitHub Packages token is needed (Error Building from Source on Ubuntu 26.04 #18).
  • Remote camera: forward only the WebUI port, never go2rtc's 1984.
  • Adding a modern printer by hand now needs its serial number. On the command line, the Creator 5 series needs the creator-5 / creator-5-pro type and its serial.

Before merging

  • Live GUI test with real printers.
  • Release commit on alpha: the ## [1.2.0] CHANGELOG section and package.json -> 1.2.0 (chore(release): prepare 1.2.0).
  • CI green, including the emulator E2E job.
  • Merge with a merge commit, not a squash, so alpha can fast-forward to main afterwards.

After the merge: dispatch Build and Release on main with 1.2.0 and pre-release unchecked, then fast-forward alpha to main.

Co-Authored-By: Claude Code noreply@anthropic.com

GhostTypes and others added 30 commits July 4, 2026 18:14
…ration, SSH settings, and remote reboot

- Brings over the desktop app's SSH-based features in one pass: printer file browsing and management, the full calibration assistant, per-printer SSH settings, and one-click printer reboot with live progress
- Passwords can be changed but never read back from the browser
- Only works on Adventurer 5M / 5M Pro / AD5X printers set up with the EasySSH USB tool; buttons stay hidden on other models
- Fixed the calibration window being too narrow and its export button rendering comically large

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
data/config.json and data/printer_details.json were committed to the repo, so
a fresh clone shipped with the maintainer's local settings and saved printers.
printer_details.json in particular carried real IPs, serials and check codes --
the check code is a printer's LAN auth credential. It also meant every local
run left the working tree dirty.

Ignore data/ wholesale, keeping only data/*.example.json, and add sanitized
config.example.json and printer_details.example.json to document the schema for
headless deployments. Neither file is read by the app: ConfigManager falls back
to DEFAULT_CONFIG and PrinterDetailsManager starts with an empty printer map,
and both write themselves out on first save.

Also records the already-shipped PrinterDiscovery delegation in the changelog
and updates CLAUDE.md, which documented the old data/runtime/-only ignore.
PORTING_TODO.md records what the 2026-07-10 SSH sync brought over from
FlashForgeUI-Electron, the port-specific caveats (SecureStorage is base64-only
here, passwords stay write-only over REST), and the known remaining drift --
notably the ifs-station -> material-station rename this repo has not taken yet.

research-tcp-probe-discovery.md is the analysis behind 1fc1f1c: the UDP
broadcast already carries the product ID and serial the probe was consulted
for, so the probe is only required for genuine legacy printers. Both were
written alongside shipped commits but never committed.
The displayed completion time was computed as now + remaining on every poll,
which only holds still while the firmware counts estimatedTime down. It
freezes that field the moment the print stops advancing, so the ETA stepped
forward a minute every minute and receded for as long as the pause lasted.
The dashboard panel and the Discord embed both kept recomputing with nothing
to suppress them.

Each now shows --:-- (and the Discord embed omits the ETA field) unless the
print is actually advancing, via the new isPrintAdvancing predicate in
types/polling.ts, mirrored browser-side in webui/static/shared/formatting.ts.
Heating is excluded along with the paused states: the pre-print warmup does
not advance the job either and drifts identically, just for minutes rather
than hours.
Creator 5 / Creator 5 Pro firmware neither sends nor accepts material
mappings over the HTTP API, so starting a previously uploaded local or
recent job dead-ends at material selection. Only a fresh .3mf upload and
start works.

- webui panels: gate the start controls via the hasMultiTool feature
  flag with a title tooltip
- webui dialogs: guard the file-list load with a toast
- standardized message: "Local job management is not available on this
  printer."
Brings the standalone WebUI to parity with FlashForgeUI-Electron's
browser E2E. Ported from that suite with the same hard rule: no stubs
or fixtures. The harness boots real flashforge-emulator-v2 instances,
seeds an isolated DATA_DIR (printer_details.json + deterministic auth
config), launches the real built server
(node dist/index.js --all-saved-printers), and waits for its HTTP API
and connected printer contexts.

- webui-auth.spec.ts: bad password, login + websocket auth gate,
  remembered-token session restore, anonymous/forged-token 401s,
  logout revocation
- webui-smoke.spec.ts: versioned assets, every referenced asset
  served, printer context switching verified against the server's own
  active-context state
- webui-creator5-gating.spec.ts: pins the Creator 5 series local-job
  gating (commit 27c67ca) across 5M Pro / Creator 5 / Creator 5 Pro
  emulated printers, including the HTTP-only Home Axes gate
- playwright.config.ts pins workers:1 (specs boot emulators on fixed
  ports; parallel workers collide with EADDRINUSE)
- type-check now covers tsconfig.e2e.json; new test:e2e script

Two real bugs the suite caught on its first runs, now fixed:

- SECURITY: /api/auth/logout never revoked tokens. The route runs
  without the auth middleware, so req.auth was never populated and the
  revocation branch was dead code; a logged-out token kept full API
  access. The token is now extracted from the Authorization header via
  the same helper the middleware uses (mirrors FlashForgeUI-Electron).
- BasePrinterBackend.buildFeatureSet() hardcoded gcodeCommands and
  statusMonitoring as always available, silently discarding child
  backend declarations - Creator5Backend's gcode-unavailable flag
  never reached the feature set. The builder now honors child values
  with the old defaults as fallback (mirrors FFUI-E commit 113613e).

The login rate limiter (5 attempts / 15 min / IP, successes count)
gives each spec file an exact login budget; every spec documents its
spend so a future test knows to shave one first.

Validation: 11/11 browser tests against real emulated printers,
jest 388/388, type-check (app + e2e) and biome clean.
Port the desktop Upload Job dialog to the WebUI, 1:1. Users can now
send a .gcode, .gx, or .3mf file from the browser and start it.
Before this, only files already on the printer could be started.

A browser knows no local file path, so the upload runs in three steps:
POST /api/jobs/upload/stage takes the bytes, parses the file, and
returns an upload handle plus the metadata. POST /api/jobs/upload/start
hands the printer backend a real path. POST /api/jobs/upload/cancel
drops the staged file.

Material matching reuses the existing dialog through new optional
onConfirm and onCancel hooks, so the resident-job start path is
unchanged. The Creator 5 series is not gated: a fresh 3MF carries its
own tool data, so upload is the one job path those printers support.

Staged files are capped at 512 MB, live in one temp directory each,
expire after 30 minutes, and are removed on server stop. The server
accepts only a bare .gcode, .gx, or .3mf name.

Bump @parallel-7/slicer-meta to 1.5.0 for the layer height, infill,
layer count, first layer time, and warning fields.
GhostTypes and others added 29 commits August 19, 2026 18:57
Mirror the FFUI-Electron material-matching and upload dialog rework (c9506a9) in the standalone WebUI.

- Matching dialog: the confirm button now reads Confirm instead of Start Print, a dedicated card lists every color mismatch, and mapping rows show color swatches on both sides.
- Upload dialog: confirmed mappings show as chips (tool, slot, material, swatches), the primary button reads Start Print with Start Now ticked and Send File otherwise, slice warnings moved to their own card, the print-time highlight became a full-width banner, and phone layout stacks the preview above the metadata.
- Add a headless UI screenshot harness: scripts/ui-shot.mjs plus fixtures for the upload and matching dialogs, the ui:shot npm script, and .ui-shots/ in .gitignore, so layout changes can be checked without launching the app.
- Shorten the AD5X color difference warning text in createColorDifferenceWarning.
- Record the rework under [Unreleased] in CHANGELOG.md.

Verified: type-check (app + e2e), 24 jest suites / 388 tests. The .pi/settings.json deletion in the working tree is unrelated local noise and is not part of this commit.
Remove the ui-shot headless screenshot harness that commit 62ddd44 added.

The harness served the built WebUI over a throwaway HTTP server, applied a fixture that filled the dialog DOM with representative data, and captured PNGs in headless Chromium. It no longer earns its keep, so it goes.

Deleted:
- scripts/ui-shot.mjs, the runner
- scripts/ui-shot-fixtures/upload.mjs and matching.mjs, the two dialog fixtures
- the "ui:shot" npm script
- the .ui-shots/ ignore block from .gitignore

No CHANGELOG changes needed — the harness was never mentioned in the changelog entries. The .pi/settings.json deletion in the working tree remains unrelated local noise, not part of this commit.
…ss printers

The four single-extruder temperature routes sent raw ~M140/~M104 G-code
through executeGCodeCommand, which hard-fails on HTTP-only backends --
so bed and extruder temps could not be set on the Creator 5 series at
all (real firmware runs no TCP service on those printers).

Bed/extruder set/cancel are now proper backend methods that branch on
legacy client presence: dual-API printers keep the legacy TCP wire
command (identical bytes to before), HTTP-only printers (Creator 5 /
5 Pro) use the ff-api tempControl endpoints with no TCP attempt, and
GenericLegacyBackend (AD3/AD4) keeps its G-code path unchanged.

Verified against live emulator instances: Creator 5 and AD5M Pro, all
four routes per printer, asserting each emulator's /detail target temps
(and the C5 four-nozzle array) reflect the commands.
Add .github/workflows/ci.yml for FlashForgeWebUI with two jobs.

verify: type-check, lint, build, and unit tests on Node 20. Sets up GitHub Packages auth for the @GhostTypes and @Parallel-7 scopes so npm ci can install the scoped deps.

e2e-emulator: browser E2E with Playwright chromium against flashforge-emulator-v2, checked out at pinned tag v0.2.0 (overridable via the emulator-ref workflow_dispatch input). The emulator supplies the printers, so no real hardware is needed. Its install skips the Electron binary because only the headless Node runtime is used; this track drives dist/index.js, not an Electron window. Playwright test-results and report upload as artifacts on failure.

Triggers on push and pull_request to main/alpha, ignores doc-only paths, and cancels in-progress runs per ref.
Change the file mode of all five bundled go2rtc binaries (darwin-arm64, darwin-x64, linux-arm, linux-arm64, linux-x64) from 100644 to 100755. Contents are unchanged; only permissions differ.

The binaries are committed to the repo, so a fresh Linux/macOS checkout restores them without the executable bit, and the postinstall downloader skips chmod because the files already exist ("Already exists" early return). Spawning go2rtc then fails with EACCES and server startup aborts with "[Fatal] go2rtc process exited during startup".

This broke the new CI e2e-emulator job (dist/index.js on ubuntu-latest) while Windows local runs stayed green, since Windows has no exec bit. The pkg track never hit it because extractBundledBinary() already chmods after unpacking.

Fixes the mode at the source so every checkout, git archive, and clone gets executable binaries on Unix platforms.
Expand the standalone WebUI browser E2E suite from 14 to 38 tests, mirroring FlashForgeUI-Electron's per-model emulator track, and refactor it onto shared infrastructure.

- helpers/targets.ts: four-model emulator matrix (Adventurer 5M Pro, AD5X, Creator 5, Creator 5 Pro) with capability flags (isCreator5Series, hasMaterialStation, productId, printerModel). Specs loop this list so every model is covered everywhere by construction, exactly like FFUI-Electron's support/track.ts. The matrix, never the printer's /detail, is the source of truth - stock Creator 5 firmware no longer reports the material station in /detail but still has one and still requires mappings on a fresh upload.
- New upload spec (mirrors FFUI-E's upload.spec.ts): browser-driven upload through the real dialog per model, material matching dialog must appear for every 3MF on station printers (AD5X, Creator 5, Creator 5 Pro, single tool included) and never on 5M-series (bounded 10s wait), tool-to-slot assignment then confirm, Start Now unchecked, file-landed assertion via the recent-jobs list (the printer's /gcodeList), and an assertNotPrinting safety gate reading the emulator's own /detail. API-level three-call upload for all models pins the gcode-refused-on-station gate and the mapping payload.
- New connect spec: auto-connect of the saved printer on boot, per-model manual connect via POST /printers/connect (productId for HTTP-only models), wrong check-code rejection leaving no context, UDP discovery finding every instance with the saved one flagged known.
- New LED spec: on/off cycle per model asserted in the emulator's /detail lightStatus.
- helpers/api.ts: shared token fetch, context resolution, raw/JSON POST, remembered-token page open, printer switch. Tests open pages via the remembered-token restore path to stay inside the server's 5-logins-per-15-minutes rate limit.
- standalone-server harness: seedPrinters option to boot emulators without auto-connecting them (used by the connect spec).
- Gating and heater specs refactored onto the shared matrix: gating now covers AD5X too, heaters cover all four models plus the nozzles-array contract on both Creator 5s.
- Fixtures copied from FlashForgeUI-Electron: ad5x-single-tool.3mf and adventurer5m-single-color.gcode.

Full suite: 38 passed in 2.0m locally against emulator v0.2.0.
- Per-tool estimates captured at upload (3mf filament data; AD5X toolData fallback), stored per printer serial
- Deduction at terminal states only: full on completion, progress-fraction on cancel, none on pause/resume
- Slot-to-spool assignments persisted per serial; Spoolman panel re-enabled with per-slot view
- Exactly-once via reserve-first ledger; Spoolman errors never affect printing
- AD5X without station now supported via the standard single-spool flow
- Captures printer-reported total filament weight at print start for stored single-material jobs
- Deducts to the sole assigned slot's spool; ambiguous or multi-material stays untracked
- One delayed retry for recent-list propagation lag; capture never blocks the start response
Material-station printers report no per-tool filament use. The old
flow kept a spool per slot for each printer serial, but the spools on
the printer can change at any time and the app cannot see that.

Now the matching dialog asks for the spool of each tool, or "Do not
track". The choice applies to that one print only. The app deletes it
when the print ends.

- Add a spool picker to the matching dialog for app-started jobs.
- Charge the full slicer estimate per tool when a print completes.
- Charge a cancelled print per tool from the 3MF gcode. The printer
  reports progress as the gcode byte position, so each tool gets the
  part it printed up to that point.
- Charge the last saved progress, marked approximate, when a print
  ended while the app was not running.
- Track AD5X multi-material stored files that start through the
  matching dialog. Charge a cancel linearly by progress.
- Remove the slot-to-spool store, its route, and single-material
  stored-file tracking.
- Send the gcode tool number (filament id - 1), not the list position.
  A plate with filaments 1 and 3 printed its second tool from the
  wrong slot.
- Let the matching dialog wait up to 15 s for the station status.
- Add unit tests and emulator e2e tests with a tool-change fixture.
The per-job Spoolman e2e tests need 3MF slice weights per tool and
AD5X tool ids 0-3. Emulator v0.4.0 adds both.
Move the unreleased CHANGELOG entries into a dated 1.2.0-alpha.8
section. Bump the package version to match.

Correct the CI entry: the emulator pin is v0.4.0, not v0.2.0.
Add a note that Creator 5 tracking is not yet tested on a real
printer.
Port of FlashForgeUI-Electron e4d9e9c (from FlashForgeUI-Electron PR
#80). startWebUI() enables auto-start right before it calls start(),
so a configUpdated event during startup starts a second attempt. The
two attempts race to bind the same port, and the loser's
cleanupFailedStart() closes the winner's sockets.

- Make start() share one in-flight attempt. Clear it in a finally()
  callback so a failed attempt can be retried.
- Make stop() wait for an in-flight start, so the start cannot mark
  the server running again after the stop.
- Clear the close-timeout timer in stop(). A clean close left it
  armed, which kept the process open for the rest of the timeout.
- Kill go2rtc on process exit. The startup-failure and hard-deadline
  paths call process.exit(), which skips the graceful shutdown, so
  go2rtc was orphaned holding its ports.
- Add lifecycle tests for concurrent starts, retry after a failed
  start, and stop during a start.

Co-Authored-By: jsalmon00 <jsalmon00@users.noreply.github.com>
A top-bar info button opens an About modal showing the running version,
with links to releases, source, and the issue tracker. The version is
stamped into index.html from package.json by copy-webui-assets.js at
build time, so packaged binaries and source builds both report it.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Clears the runtime advisories pnpm audit reports against the shipped
dependencies: axios 1.13.6 -> 1.20.0, express 5.1.0 -> 5.3.0 (and its
proxy-addr, path-to-regexp, qs, body-parser), ws 8.18.3 -> 8.22.0,
form-data 4.0.5 -> 4.0.6. The package.json floors are raised so a fresh
resolve cannot land on a vulnerable version. Only these packages moved;
nothing else in the lockfile changed.

The remaining runtime advisory is adm-zip 0.5.x via
@parallel-7/slicer-meta, which needs a slicer-meta release that widens
its adm-zip range to ^0.6.

Co-Authored-By: Claude Code <noreply@anthropic.com>
PREVIOUS_TAG took the newest older tag, so v1.2.0's "Compare changes
since" link would have pointed at v1.2.0-alpha.8. Git's default version
sort also ranks v1.2.0-alpha.8 above v1.2.0, so v1.3.0-alpha.1 would
have compared against alpha.8 as well.

Sort with versionsort.suffix=- so a prerelease ranks below its release,
and skip prerelease tags when cutting a stable release. The greps get
`|| true` because the step runs under pipefail and grep exits 1 when
nothing matches.

Co-Authored-By: Claude Code <noreply@anthropic.com>
README: feature table now covers job upload, the material station, the
SSH tools, Discord notifications, per-job Spoolman tracking, and the
authenticated camera proxy. Binary names gain the .bin suffix the
release assets actually use. Adds the missing config keys, the About
window as the way to check the version, the E2E suite, troubleshooting
rows for remote camera access and the GitHub Packages 401 (#18), and a
Contributing pointer.

CONTRIBUTING.md: work from alpha and target PRs at alpha; the alpha ->
main -> stable release cycle; setup, pre-PR checks, conventions, bug
report details, and the maintainer release steps.

CLAUDE.md: scoped packages now resolve from public npm, and note the
adm-zip advisory that rides in on slicer-meta.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Move the unreleased CHANGELOG entries into a dated 1.2.0 section and
bump the package version to match.

This is the first stable release since 1.1.0, and the workflow uses
this section as the release page, so it opens with highlights from the
whole alpha cycle and upgrade notes for 1.1.0 users: untracked data/
files on git pull, public-npm installs (#18), go2rtc port forwarding,
and the serial number now required for manual and CLI connects.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@GhostTypes GhostTypes self-assigned this Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant