Commit Graph
1320 Commits
Author SHA1 Message Date
bbassieandClaude Fable 5.1 dc129b2fad Include schema validation errors in InvalidMessage stack
Generic error handlers such as FactorioServer.handle() and the event
handler catch in Link only log err.stack, so the ajv errors attached to
an InvalidMessage were lost unless the catch site checked for the class
explicitly. Appending the errors to the stack in the constructor makes
every stack-logging handler show which field failed validation without
special casing each one.

Fixes #986

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 11:57:05 +00:00
Hornwitser 71f78c14a3 Add command for copying static assets to a folder
This is useful in case the static assets are hosted by a separate web
server like Nginx, or for testing that the static_url option works as
intended.
2026-08-31 20:14:13 +02:00
Hornwitser 7c0d7faa24 Add config to set URL for static assets
Add option in the controller config to set where static assets are
loaded from in order to support deployments where the static assets are
server via a CDN while the API is directly accessed.

This is useful because some CDNs put low limits on how large requests
bodies can be, which causes issues with uploading large mods and saves.
2026-08-31 19:17:31 +02:00
Cooldude2606 0f1fbb8fe5 Merge pull request #981 from bbassie/fix/static-root-web-root
Make staticRoot include the static/ prefix and use it for plugin bundles
2026-08-31 00:38:31 +03:00
bbassieandClaude Fable 5 2f4e0c4418 Make staticRoot include the static/ prefix and use it for plugin bundles
staticRoot was set to the same value as webRoot, so the static/ prefix
ended up scattered across consumers: index.html relied on the manifest
path containing it, the export stores hardcoded `${staticRoot}static/`,
and bootstrap loaded plugin bundles from webRoot instead. Now staticRoot
is `${webRoot}static/` and the controller strips the static/ prefix from
bundle paths before handing them to the page and to /api/plugins.

Plugins that fetch assets with `${staticRoot}static/...` need to drop the
static/ part.

Fixes #980

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 09:50:08 +00:00
bbassieandClaude Fable 5 a25f8c8611 Fix lua lint warnings reported by emmylua
The largest source was compat.lua returning the setmetatable call
directly, which made emmylua infer the module type from the metatable
rather than the LibCompat class, hiding every field from consumers.

The remaining fixes are nil guards on gui element and character
accesses, type annotations for research_sync's script data and
player_auth's rcon arguments, casts where the api's read/write union
types are wider than the values at runtime, and scoped diagnostic
disables for the vendored json.lua and the version fallback code that
touches apis removed from the current typedefs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 22:25:24 +00:00
Cooldude2606andClaude Fable 5 59cd193b65 Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings (#977)
* Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings

Factorio 2.1.13 added build_base_unit_dispatch_cooldown as the last
field of enemy_expansion in the map exchange string, and rejects map
settings without it. Track the full version in the reader state and
read the field for 2.1.13 and later. Adds a fixture string generated
with 2.1.14 and uses it in the load-scenario integration test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Replace version flags with versionAtLeast in exchange string reader

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 23:24:58 +01:00
BastiaanandClaude Fable 5 7bd408b45e Restore pagination on the instance save list (#973)
useTableQueryState returns pagination=false unless a pagination config
is passed, and SavesList did not pass one after #930, so the saves table
rendered all saves on one page.

Fixes #967

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 23:22:57 +01:00
bbassieandClaude Fable 5 d3c618a7ad Replace version flags with versionAtLeast in exchange string reader
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 22:57:36 +00:00
bbassieandClaude Fable 5 021ee1725b Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings
Factorio 2.1.13 added build_base_unit_dispatch_cooldown as the last
field of enemy_expansion in the map exchange string, and rejects map
settings without it. Track the full version in the reader state and
read the field for 2.1.13 and later. Adds a fixture string generated
with 2.1.14 and uses it in the load-scenario integration test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 22:21:28 +00:00
bbassieandClaude Fable 5 5e6b06f009 Filter dependency resolution by the mod pack's Factorio version
The dependency resolver picked the newest release satisfying the version
range regardless of which Factorio version it targeted, so a 2.0 mod pack
could end up with 2.1 dependencies. Only consider portal releases and
local mods whose factorio_version matches the mod pack.

Fixes #965

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 14:30:32 +00:00
Floze 0be4fd77e5 Allow disabling player online autosaves (#971) 2026-08-14 10:24:15 +01:00
Cooldude2606 03f2616f30 Merge pull request #955 from bbassie/feature/console-log-filter-labels
Fix the instance console hiding Factorio diagnostics
2026-08-13 00:20:44 +01:00
Cooldude2606 244d637fac Reduce tooltip length 2026-08-12 22:25:31 +01:00
bbassieandClaude Opus 5 71db8e57f5 Label Factorio output in the console with its log level
The Web UI console rendered server level entries as bare parsed Factorio
output, while every other level was prefixed with its name. Nothing in a
line distinguished what the game wrote from what Clusterio wrote about it,
which matters more now that both modes of the filter carry a mix of the
two.

Emit the [server] prefix for those entries as well, matching what
TerminalFormat already does for the host and controller logs, and style the
level the same bold the terminal colourises it with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 10:58:30 +00:00
bbassieandClaude Opus 4.8 16092ec967 Make the instance console log filter discoverable
The filter was a Switch showing only the label of the mode it was already
in, so with the default of chat mode it read as a button labelled "Chat"
with no hint that a Log mode existed or that anything was being hidden.

Swap it for a Segmented control listing both Chat and Log, so the mode that
is not active is always visible, and add a tooltip describing what each one
covers.

Fixes #853

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-12 10:58:21 +00:00
bbassieandClaude Opus 4.8 42dc83df80 Stop the instance console filter swallowing diagnostics
The console's chat filter kept only Factorio output parsed as an action,
which dropped the server's replies to those actions. Running a console
command with a typo in it echoed the command and then appeared to print
nothing, because "Cannot execute command. Error: ..." is a plain message
rather than an [ACTION] line. Factorio's engine level errors were dropped
the same way, so a failed multiplayer connection left no trace either.

Filter by where a line came from instead. Factorio interleaves two streams
on stdout: the date stamped console log, which carries what was done to the
server and the replies to it, and the seconds stamped engine log, which is
verbose diagnostic output. Keep the console log whole and take only
warnings and errors from the engine log.

Apply the same rule to Clusterio's own entries, which were previously kept
in full and drowned the Factorio output they were mixed with. Keep those at
warn or worse and drop the routine bookkeeping. Replayed over a development
cluster's history this drops 415 informational lines and surfaces engine
errors that were never visible in either mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-12 10:58:19 +00:00
Cooldude2606 15a8215a4f Merge pull request #956 from bbassie/feature/plugin-change-refresh
Reload the web interface when the controller's plugins change
2026-08-10 16:57:30 +01:00
Cooldude2606 499d219933 Remove overly verbose comments 2026-08-10 16:45:56 +01:00
bbassieandClaude Opus 4.8 1e4b586fa3 Distinguish a plugin that was never loaded from one that failed
The plugin list reported "Error loading module" for anything without a
loaded package, which includes plugins the controller has picked up since
the page was opened. Nothing had been tried for those, so reporting a
failure was both wrong and unhelpful: the fix is to reload, not to
investigate.

Report those as needing a reload instead. Plugins the controller cannot
serve at all are excluded during startup before they reach the loaded
plugin map, so those are checked for first and still count as an error.

Fixes #729

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 10:01:38 +00:00
bbassie e3c18db7b8 Reload the web interface when the controller's plugins change
The web interface loads plugin bundles, registers their messages and
adds their config fields once in bootstrap, and has no way to undo any
of it.  A controller restarted to pick up a plugin being installed,
enabled or disabled comes back running a different set, and a controller
started in recovery mode runs none of them while still accepting control
connections, so open pages carry on sending messages it no longer
handles.

The controller already states the plugins it is running in the handshake
it sends on every connection, which is what hosts track as serverPlugins
and use to decide what to forward and which instance plugins to load.
Compare that against the set the page loaded and stop rendering on a
mismatch, asking to be reloaded instead.

The comparison is against loaded rather than enabled because loaded is
what the handshake reports and what the controller is actually running;
enabled also takes load_plugin into account, which only applies from the
next restart and would otherwise latch a permanent mismatch.

Closes #804
2026-08-10 10:01:35 +00:00
Cooldude2606 c0138c3fe0 Merge pull request #961 from bbassie/feature/instance-host-broadcast
Deliver broadcasts to all hosts, not just the local one
2026-08-08 23:51:48 +01:00
Cooldude2606 bdf5e3011f Merge pull request #959 from bbassie/feature/forwarded-request-cleanup
Forget a forwarded request that could not be sent
2026-08-08 23:34:19 +01:00
Cooldude2606 48c07f9ab3 Make explanation comment less verbose 2026-08-08 23:26:40 +01:00
Cooldude2606 6e74db1f9e Merge pull request #963 from lntutor/feat/transitive-plugin-discovery-953
Discover transitive NPM plugins
2026-07-31 18:44:55 +01:00
Cooldude2606 bfbd41d64b Cleaner path resolution and tests 2026-07-31 18:33:19 +01:00
Loi Nguyen dfb6037797 fix(lib): address transitive plugin review 2026-07-31 00:01:16 +07:00
Cooldude2606 3a689b379a Merge branch 'master' into feature/download-file-cleanup 2026-07-29 23:13:29 +01:00
Cooldude2606 e4c2ccd58f Merge pull request #954 from bbassie/feature/redact-mod-portal-token
Redact credentials from URLs in download errors
2026-07-29 23:02:12 +01:00
Cooldude2606 417ee87422 Maintain repeated credential parameters 2026-07-29 22:56:19 +01:00
Cooldude2606 d7eb647c09 Merge pull request #952 from lntutor/feat/mod-info-recommendations-949
Show informational mod recommendations
2026-07-26 12:34:09 +01:00
Loi NguyenandLoi Nguyen 08651b5917 fix: prevent restart downgrades (#951)
Co-authored-by: Loi Nguyen <1948922+lntutor@users.noreply.github.com>
2026-07-26 12:20:22 +01:00
Loi NguyenandClaude Opus 4.8 74bd19c063 Remove redundant recommended-dependency duplicate guard
Address review feedback: modsByName is built from annotatedMods so the
advisories array always exists, and each source mod is iterated once with
no duplicate dependencies, so the existence check can never fail. Push the
recommended_dependency advisory directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8b9dEaJtpvydzz4ttM7JH
2026-07-26 17:26:17 +07:00
Loi Nguyen dfe52df7bb Show informational mod recommendations 2026-07-26 17:05:44 +07:00
Loi Nguyen 40a6142e3e feat(lib): discover transitive npm plugins 2026-07-26 13:23:20 +07:00
bbassieandClaude Opus 4.8 09c667252c Deliver broadcasts to all hosts, not just the local one
An event broadcast from an instance to all hosts only reached the host
running that instance. The host side of an instance connection has the
host's own address as its source, so a broadcast aimed at hosts counts as
addressed to it, and a link that a message is addressed to handles it and
stops. The event was delivered locally and never routed onwards.

A broadcast is addressed to every link of the type it targets, so arriving
at one of them does not mean it has arrived everywhere. Keep routing such a
message after handling it locally. Only links that route are affected, as
instances and controls have no router and would otherwise throw on every
broadcast they receive.

The only links whose source type matches a broadcast target and which route
are the host and its instance connections, both matching a broadcast to
hosts, so add the missing case for that to the host router. It forwards to
the controller, which already passes broadcasts on to every other host, and
stops when the message came from the controller to begin with.

Drop the exclusion this bug had in the routing test matrix, which now
covers it along with the plugin filtering variants.

Fixes #575

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 17:39:25 +00:00
Cooldude2606 9ff090c30b Merge pull request #958 from bbassie/feature/subscription-event-address
Stop addressing subscription events to a request id
2026-07-25 15:28:55 +01:00
Cooldude2606 cc92e851ad Rename subscriber.dst to subscriber.address 2026-07-25 15:10:57 +01:00
Cooldude2606 870e1f2125 Remove unneeded comments 2026-07-25 14:51:14 +01:00
bbassieandClaude Opus 4.8 70305b7937 Silence the deprecation warning on install
execFile was called with shell: true and an array of arguments, which Node
deprecates in DEP0190 because it concatenates the arguments by space
without escaping anything, giving the impression they have been made safe.
The warning was printed to the console on every install.

The arguments here are already escaped for the shell by escapeArg, so join
them into the command instead of handing them to execFile to concatenate.
Nothing about what ends up being run changes.

Point the escaping test at the same form so it covers what the installer
actually does, and stops emitting the warning itself. Its several hundred
round trip cases still pass, which is what confirms the escaping is
unaffected.

Fixes #851

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 23:02:37 +00:00
bbassieandClaude Opus 4.8 3512042cf3 Forget a forwarded request that could not be sent
forwardRequest records a request so that its response can be sent back the
way it came, but recorded it before handing the message to the connector.
When the connector threw, because the session to the next hop was gone, the
record stayed behind for a request that had never left.

Routers answer the origin themselves when forwarding throws, so the request
was already accounted for. The leftover record then had it answered a
second time when the link cleared its pending requests on disconnect,
producing a duplicate response and "Received response N without a pending
request" at the far end.

Remove the record when the forward throws. Doing it here rather than in the
routers covers both the host, which catches and responds, and the
controller router, which does not.

Fixes #650

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:58:37 +00:00
bbassieandClaude Opus 4.8 4a9c241eee Stop addressing subscription events to a request id
A subscribe arrives as a request, so the source address the subscription
controller handles it with carries the request id used to route the
response back to the caller. That address was stored verbatim as the
destination for the subscription, so every event broadcast to it, and the
replay event sent when the subscription is created, went out addressed to
a request that had long since been answered.

A request id identifies one request in flight and has no meaning on an
event. The receiving link keys response routing off it, so events carrying
one are put through paths meant for response traffic.

Strip the request id when taking the subscription's destination from the
request that created it. Add Address.withoutRequestId for that, as the
same address is used in three places here.

The subscription map is keyed on addressIndex, which already excludes the
request id, so subscriptions were never duplicated by this and only the
address events are sent to was wrong.

Fixes #847

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:49:04 +00:00
bbassieandClaude Opus 4.8 3883c1932c Discard the temporary file when a download fails
downloadFile opens a write stream before fetching, but only closed the
stream and removed the file it was writing to when the server answered
with a bad status. A request that never reached the server, or a
connection dropped part way through the body, left both the temporary file
and an open file handle behind.

Mods are downloaded through this in a Promise.all over every mod in a pack,
so one bad moment on the network scatters temporary files across the mods
directory. They are not harmless: createTempWriteStream picks the first
free name, so each leftover file permanently shifts the name used for
later downloads of the same mod, as the existing "should not overwrite
existing temp files" test describes.

Move the fetch and the body write into a try block and discard the stream
and its file on the way out. Destroy rather than end the stream, as the
content is being thrown away, and skip both if the stream has already
closed so that a failure after the body was written does not wait forever
for a close event that has already fired.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:26:18 +00:00
bbassieandClaude Opus 4.8 feb0e46aab Redact credentials from URLs in download errors
The mod portal takes the Factorio account token as a query parameter on the
download URL. When a download failed, downloadFile put the whole URL into the
error message, which ModStore then logged verbatim, printing the token into the
log at error level on every failed mod download.

Add a redactUrl helper that replaces the value of credential-carrying query
parameters with REDACTED, and use it when downloadFile builds its error.
Parameter names and the rest of the URL are kept so the message stays useful
for diagnosing which request failed; username is deliberately left intact as it
is not a secret and identifies the account in use.

Fixes #795

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:38:09 +00:00
Cooldude2606 5e0ab3423f Chore: More consistent package files
This commit does not contain any material changes.
2026-07-19 02:29:29 +01:00
Cooldude2606 e4a38e803c Add repository to all packages 2026-07-19 01:53:53 +01:00
Cooldude2606 95cd830c01 2.0.0-alpha.27 2026-07-19 00:10:11 +01:00
Cooldude2606 ddc03eaf30 Bump engines requirement 2026-07-18 23:50:33 +01:00
Cooldude2606 3add08b100 Update naming within exchange string lib 2026-07-18 23:32:15 +01:00
Cooldude2606 ff28357d7a Remove hard coded versions 2026-07-18 22:12:15 +01:00