185 Commits
Author SHA1 Message Date
bbassieandClaude Opus 5.5 f14bfc3222 Address review of template inheritance
- Unlinking a field enables Apply on its own and sends the current value.
- The link button no longer toggles the checkbox of its label.
- Link and disconnect icons for all four states, pending ones highlighted.
- instance.is_template is hidden.
- ctl create takes --as-template.
- Clearer names for the InstanceManager inheritance helpers.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:57:01 +00:00
bbassieandClaude Opus 5.5 b5c372f109 Rename bases to templates and add per property inheritance
Review changes for #1142:
- base becomes template everywhere (is_template, template_id,
  --template, --is-template, --templates, InstanceDetails.isTemplate).
- Object fields inherit per property. inherited_fields lists the
  overridden properties for object fields and the template object is
  merged with them.
- Linking a template also inherits fields that hold the default value.
  The server name stays per instance.
- Ports are never inherited.
- InstanceDetails carries templateId.

Web UI: inherited values are locked until unlocked, overridden values
can be inherited again, templates are listed separately, the create
modal can create templates and instances from templates, and the
instance page has "Create template" which can link the source.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:54:37 +00:00
bbassieandClaude Opus 5.5 01cf789f5d Add base instances for instance config inheritance
Instances can be marked as a base with instance.is_base and other
instances inherit config values from it through instance.base_id.
The controller copies base values into inheriting configs and keeps
track of inherited fields in the hidden instance.inherited_fields, so
hosts receive resolved configs and need no changes. Setting a field
stops inheriting it, `instance config inherit` undoes that.

Bases cannot be assigned to a host, are hidden from `instance list`
unless --bases is given, and cannot be deleted or unmarked while
other instances inherit from them. Chains of bases are allowed and
cycles are rejected.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:54:21 +00:00
Danielv123 49a0b27d29 Address review of host pairing
Revoking a host now unassigns its instances instead of being refused
while it has any, and removes its system metrics.

Approving and revoking no longer force a save. Hosts are saved by
autosave and on exit like everything else, which also removes the
approving state from pairing requests.

The approval code and link are logged as warnings so they show up in
the systemd service, which only prints warnings and above. The pairing
limits are now the controller.host_pairing_timeout and
controller.host_pairing_max_pending config values, 0 removes the limit.

Subscribers take an optional permitted function, used for the pairing
requests instead of overrides in a subclass.
2026-10-31 16:31:45 +01:00
Danielv123 2cf7c66187 Merge remote-tracking branch 'origin/master' into host-pairing
# Conflicts:
#	test/integration/clusterio.js
2026-10-30 21:41:06 +01:00
Danielv123 4a49a198f0 Pair hosts when they start instead of in the installer
The installer only writes the controller URL and host name. Running the
host requests to join the cluster if it has not been approved yet, or if
the controller no longer accepts its token, and waits until an admin
approves it. Once approved it saves its id and token and starts as
normal. A rejected host exits with code 8.

clusteriohost run --user-token approves the host right away with the
token of a user allowed to approve hosts. The token is not saved.

This moves the pairing client into the host package, where it uses the
same certificate setup as the host's connection to the controller, and
removes --no-pair and the rerun handling from the installer.
2026-10-30 21:13:14 +01:00
Danielv123 37e79d9c41 Remove tests that pin implementation details
Drops tests that restate message declarations, check that removed
commands are gone, test internal helpers or repeat the same behaviour
at another layer.
2026-10-30 10:11:14 +01:00
Danielv123 87d4f5c64e Replace host tokens with host pairing
Hosts join the cluster by running the installer, which shows a code and
waits until the request is approved under Hosts > Add host in the web
interface or with clusterioctl host approve. An admin can pass their
user token to the installer to approve the host right away. The token
is only used for that and is not saved.

The controller only accepts hosts that have a record, and only
approving a host creates one. Revoking a host deletes its record, so
its token can no longer be used to reconnect. Hosts that have connected
before keep working.

Removes host generate-token, revoke-token and create-config from
clusterioctl, bootstrap generate-host-token, the host token messages and
the core.host.generate_token, revoke_token and create_config
permissions, which are also stripped from stored roles. Adds
clusterioctl host pending, approve, reject and revoke, and the
core.host.approve and core.host.revoke permissions.

Standalone installs add the host to the controller database directly,
since the controller is not running during the install.
2026-10-30 06:11:32 +01:00
bbassieandClaude Opus 5.5 814a4a201a Replace the PluginDetails booleans with a single state
loaded, enabled, pendingInstall, removed, noEntrypoint and nameTaken
allowed far more combinations than can happen, and every consumer had
to know which took priority. PluginDetails now has one state, with
loaded, loadsOnRestart and removed as getters over it.

2.0.3 web UIs and ctl send PluginListRequest straight to hosts and
require loaded and enabled, so those are still sent next to the
optional state. Plugins listed by 2.0.3 hosts get their state from
loaded and enabled.

The web UI planner gets the same treatment: MachinePlugin has one
state in place of disabled, noEntrypoint, dependencyTaken and
nameTaken. ctl host plugin list shows the state.

Closes #1134

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 00:05:23 +00:00
bbassieandClaude Opus 5.5 11e067a933 Keep auto paused instances paused
Every RCON command makes an auto paused Factorio server run one tick, so
subspace_storage's 1 s inventory update and 5 s ping, plus a
statistics_exporter gather on every Prometheus scrape, kept a server with
nobody online running at about 1.3 ticks per second.

The instance now records the auto_pause server setting it was started
with and exposes autoPaused, true while that setting is on and no players
are online, along with an autoPauseChanged hook. subspace_storage skips
its timers while paused and sends a ping and inventory as soon as a player
joins. statistics_exporter gathers once after the game pauses and keeps
serving those values until it resumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:40:30 +00:00
Cooldude2606 ebdbb1fe56 Merge pull request #1141 from bbassie/test/plugin-integration
Finish the plugin system integration tests
2026-10-28 23:35:32 +00:00
bbassieandClaude Opus 5.5 9ab36151bd Finish the plugin system integration tests
Adds the last three checks from #342 to test_plugin: an InstanceEcho
request handled on a running instance, a Lua module with a remote
interface that the request calls over RCON, and log tagging checks for
the plugin field and host/instance ids on the controller, host and
instance log lines.

test_plugin now has a module, so the create_scenario and patch unit
tests that relied on it having none use global_chat instead.

Closes #342

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:01:27 +00:00
bbassieandClaude Opus 5.5 e94abe51c9 Lint with Oxlint through Vite+ instead of ESLint
Replace ESLint, typescript-eslint, eslint-plugin-node and the flat
config helpers with vite-plus, which runs Oxlint for vp lint. The rules
are carried over from eslint.config.mjs by @oxlint/migrate into the lint
section of the root vite.config.ts. The formatting rules ESLint
deprecated run as @stylistic rules through Oxlint's JS plugin support,
and disable comments naming them are updated to the @stylistic names.

@stylistic understands TypeScript syntax that the core rules skipped,
so the autofix adds missing semicolons and spaces in type literals and
aliases. Leading | and & in multiline types stay where they were.

Oxlint's node/no-sync also reports identifiers ending in Sync that are
not calls. The test helper techSync is renamed and the two other names
are ignored.

Refs #1127

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 22:53:13 +00:00
bbassieandClaude Opus 5.5 1c7c00b033 Keep error classes across links
Error responses now carry the registered name of the thrown error's
class, and the requester rejects with an instance of that class instead
of a generic RequestError. Error classes are registered like message
classes, and plugins list their own in `errors`.

RequestError is replaced by a hierarchy following the HTTP status codes:
ClientError (ValidationError, ForbiddenError, NotFoundError,
TimeoutError, ConflictError) and ServerError (InternalServerError).
PermissionError is PermissionDeniedError and the config errors
InvalidValue, InvalidField and InvalidAccess gain an Error suffix.

Link logs only errors whose class is not registered. These arrive on the
other side as InternalServerError with the original message and stack,
so an error forwarded through the controller is logged once, where it
was thrown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 22:03:13 +00:00
Bastiaan ffe82f81d8 Merge pull request #1010 from bbassie/feature/plugin-browser
Plugin management through web interface
2026-10-27 21:32:51 +00:00
bbassieandClaude Opus 5.5 e16d0bc410 Address review of save transfer and instance dir naming
Instance dir and save names are tried without a retry cap, as on master
and in downloadFile, so an 11th instance named "New Instance" still gets
a folder.

Moves fall back to an exclusive copy when hard links are unavailable
(EXDEV, EPERM, ENOTSUP), and remove the new target if the source can't
be unlinked, so a failed move leaves no duplicate.

Local transfers keep appending .zip to the target name, as
findUnusedName did.

The copy auto-rename test now checks that the existing target save is
left untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:22:17 +01:00
a6d2d48e48 Remove lib.findUnusedName
The name it returns may already be taken by a concurrently running process
by the time the caller uses it. Callers now try the name and retry with a
-2, -3, ... suffix on EEXIST instead.

Save transfers previously renamed with fs.rename, which silently overwrites
a save that appears at the target, and copied without COPYFILE_EXCL. Moves
now use link + unlink like save rename does, and copies pass COPYFILE_EXCL,
so both fail with EEXIST rather than overwrite.

Supersedes #880.

Co-authored-by: Hornwitser <code@hornwitser.no>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:22:17 +01:00
c3abf27266 Skip max_pollution_to_restore_trees in 2.1.18+ exchange strings (#1132)
Factorio 2.1.18 removed PollutionSettings::max_pollution_to_restore_trees,
but readPollution still read it for every version, so every map setting
after it was read 9 bytes late: enemy_attack_pollution_consumption_modifier
came out as garbage, the enemy_evolution factors shifted by one field and
enemy_expansion.enabled and both expansion distances came out null.

Read the field only for strings older than 2.1.18. Add modified_v2_1_17
and modified_v2_1_18 fixtures generated with headless 2.1.17 and 2.1.18
from the modified 2.1 settings (base game only), unit tests asserting
their full decoded output, and use the 2.1.18 string for the
load-scenario integration test on 2.1.18 and later.

Fixes #1131

Co-authored-by: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com>
Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
2026-10-22 17:22:09 +01:00
Arvids Godjuks 0fb9e793d7 Stabilize player-list integration tests and RCON fixtures (#1117)
* Wait for player list replication in integration tests

* Select available RCON ports before starting Factorio

* Document zero as automatic RCON port allocation

* Address review wording and test organization

* Restrict port collision hardening to integration fixtures
2026-10-18 22:01:43 +03:00
Bastiaan cbf2b10782 Add notes to hosts, instances, users and other resources (#1000) 2026-10-18 20:06:15 +03:00
Arvids Godjuks 5ca61414b3 Add Node.js 26 to the CI test matrix (#1088)
* Add Node.js 26 to the CI test matrix
2026-10-15 14:17:08 +03:00
bbassieandClaude Opus 5.5 8363c5f0ef Rename inventory_sync to player_sync
The plugin syncs more than inventories now (settings, appearance, recipe
notifications), so name it for what it does. The package becomes
@clusterio/plugin-player_sync and every inventory_sync identifier follows.

A migration entrypoint renames the inventory_sync config fields, including
load_plugin, and the inventory view permission. The plugin moves
inventories.json in the database directory to player_sync.json and
storage.inventory_sync in saves to storage.player_sync itself, and closes
dialogs left open under the old GUI names on startup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:37:12 +00:00
Cooldude2606 b07c25aa7e Merge pull request #1014 from bbassie/feature/patched-scenario
Add create-scenario command to clusteriohost
2026-10-06 16:37:16 +01:00
bbassieandClaude Fable 5.1 1299ea80b8 Cover the mark-restart-required ctl commands
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 22:31:20 +00:00
Hornwitser 92d2662078 Move slowTest and externalTest to test/common.js
These are used outside of the integration tests, but including the
integration index file causes the very slow setup of the contorller
and host.  Avoid the integration setup costs when running commands
such as `pnpm mocha test/lib` by moving these utility functions to
the common test functions.
2026-09-15 22:18:16 +02:00
Hornwitser 2c52c265cf Require config files to be explicitly created (#1040)
The behaviour of creating the config file if it's missing while
convenient causes confusion and problems when the tools are run
in the wrong folder.  They end up creating a shadow installation
and misdirect the user with nonsensical error messages, instead
pointing to the real problem which is that the tool can't find
the configuration needed to actually run.

Require a config file to be present at startup and refuse to run if this
file is missing.  To support the odd use-case of creating the config
when missing a --create-config flag is added. To conveniently generate
a config file to edit out from the "config create" command is added.
2026-09-15 22:16:54 +02:00
bbassieandClaude Fable 5.1 187138bec5 Merge upstream/master and address review
Resolves the ESM conflicts in patch.ts, Instance.ts and host.ts. The
module gathering in Instance now goes through patch.loadModules with
the instance's loadedPlugins, and imports use explicit extensions.

Review changes: the existing-output check only treats ENOENT as
"does not exist" and rethrows anything else, a --zip option keeps the
output as a zip file for transfer (extracting stays the default), the
--scenario option describes that it takes a path and what the default
is, and the docs note that IPC is not available outside Clusterio.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 22:21:46 +00:00
Cooldude2606 06a6a7edf3 Merge branch 'master' into feature/update-all 2026-09-12 02:07:54 +01:00
Hornwitser 5dcaca4d0b Migrate to ECMAScript Modules
Migrate the usage of CommonJS specific code and imports to ESM.  Roughly
summarised this:
- Changes all require() calls to import statement or expressions.
- Changes usage of module.exports to export statements.
- Replaces require.resolve with import.meta.resolve.
- Replaces conditional imports with conditional subpath imports.
- Replaces module === require.main with import.meta.main.
- Adds the full filename with extention to all relative imports.
- Changes all usage of __dirname and __filename to their import.meta
  equivalents.
- Sets "type": "module" in package.json files.
- Removes usage of "use strict".
- Adds .ts an an alias of .js for the webpack build.

Due to differences in how require and import resolves files the plugin
entrypoints will have to include the filename and extension after this
migration in order to work.
2026-09-11 22:43:57 +02:00
bbassieandClaude Fable 5.1 1072e5f1de Cover the create-scenario command handler and module loading
Adds tests for handleCreateScenarioCommand using a fake Factorio
install with the test scenario as freeplay, for loadModules skipping
plugins without a module and rejecting a name clash with a bundled
module, and an integration test running clusteriohost create-scenario.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 00:36:35 +00:00
bbassieandClaude Fable 5.1 637ec0f901 Cover the --all flag on the ctl update commands
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 22:51:13 +00:00
bbassieandClaude Fable 5.1 c997fd8ea7 Add save copying and start options when creating an instance
The create instance modal can now copy the active save of the instance
whose config is copied, optionally saving the running game first, and
start the new instance once the copy is done. The new instance is
assigned to the host of the source so the save can be transferred.

Saving a running game is a new InstanceSaveGameRequest handled by the
host through /server-save, which waits for the save to finish. It is
also exposed as clusterioctl instance save-game.

Closes #405

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:19:24 +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
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
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 ffc79ea0e7 Disable user verification in start-all and auto_start tests
Factorio contacts auth.factorio.com on startup when
require_user_verification is enabled, which made these tests fail
offline. Match the other integration tests by turning it off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 20:26:30 +00:00
bbassieandClaude Opus 4.8 b415d16408 Add integration tests for the plugin system
Nothing covered whether plugins are loaded at all, or whether the messages
they declare are routed. The integration tests only loaded the first party
plugins, so what coverage existed came incidentally from testing those.

Add a plugin under test/file which exists purely to be loaded by the tests.
It is plain JavaScript rather than TypeScript so that it needs no build
step, which the plugin loader is happy with as it requires the entry point
and reads the version from package.json like any other plugin.

Covered so far: the plugin loads on the controller and on the host, its
title and version are reported, the command it adds is available in
clusterioctl, a request from control reaches the controller, a request from
control reaches a host, and an event sent with broadcastEventToHosts
arrives at the hosts.

Left for a follow up, as all three need a running instance: a request to an
instance, a Lua module reached over rcon, and log statements being tagged
with the plugin and host or instance id.

Part of #342, and a step towards #721 as core no longer needs a first party
plugin to have the plugin system under test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 18:13:28 +00:00
Cooldude2606 8b4c8ad337 New exchange string format for enemy expansion settings 2026-07-18 22:12:15 +01:00
bbassieandClaude Opus 4.8 dfe132699e Add integration test for starting an instance with a release channel
Configures an instance with the "experimental" channel and starts it,
exercising channel resolution end to end across the controller and host.
The resolved version may or may not be installed depending on the host's
Factorio install, so the test asserts the instance reaches a terminal state
rather than a specific outcome.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:38:29 +00:00
Cooldude2606 e54b292fce Fix tests relating to permissions 2026-05-29 18:38:07 +01:00
Hornwitser 2e48b728dd Remove fs-extra and klaw (#882)
These packages are obsoleted by the promises API to the built-in Node fs
module.  The fs-extra package is also causing problems with broken APIs
that don't behave the same as Node's fs module, and throwing errors
without code properties on them.
2026-04-09 22:53:13 +02:00
Cooldude2606 2afd7aab38 Fix failing tests 2026-03-24 18:25:53 +00:00
Hornwitser 05b6bf8c04 Replace remaining usage of phin (#862)
Replace the remaining places in the codebase using phin with fetch.
With the exception of HttpCloser which uses the http/https request
method instead.
2026-03-06 15:27:12 +01:00
Hornwitser fb3fe09466 Remove tlsCa option
The migration from phin to fetch in #755 was hampred by the lack of an
option to set the CA when using fetch.  This meant places that used phin
to talk to the controller with the tlsCa option could not be migrated to
use fetch.

Remove the tlsCa option to unblock this dependency. The replacement for
this option is to set the NODE_EXTRA_CA_CERTS environment variable to a
path to the certificate file instead, which works with fetch.
2026-03-04 17:43:32 +01:00
HornwitserandHornwitser 94692dc418 Fix tests failing due broken Factorio detection (#839)
* Fix tests failing due to missmatched Factorio detection

When the configured factorio directory is full of junk then the tests
use the first directory it finds for one of its tests, while the server
code uses a more elaborate logic to figure this out.

Turns out this is complicating things more than needed. Simply trying
each entry in the directory and seeing if it works when treated as a
directory always work, regardsless of symlinks, junctions,
reparse-points or whatever else filesystem magic there is.

* Skip tests requiring Factorio if it's not present

Prevent confusing test failures by skipping all the tests that require a
working Factorio installation to sucteed, and print a warning at the end
of the test suite if tests may have been skipped due to this.

* Handle ENOTDIR when determining Factorio version

If the path to the factorio install contains a component that is not a
file then ENOTDIR may be returned on POSIX like systems.  Treat this the
same as ENOENT so that detection logic works when used on a file.

---------

Co-authored-by: Hornwitser <code@hornwitser.no>
2026-03-01 23:06:03 +00:00
Ryan WalkerandSolar 6733aed3fb Extended export data to cover more icon types (#838)
* Enhance export functionality and add icon reference page

- Expanded exported assets to include additional spritesheets and metadata for recipes, signals, technologies, planets, qualities, and entities.
- added logging metrics to game logs
- Introduced a new Icon Reference page to display available icons with filtering capabilities.
- Updated ExportManifest documentation to reflect new asset categories.

* Add support for exporting static icons and update metadata handling

* -added data export snapshot metrics

* Fix HTML attribute syntax in IconReferencePage component

* Applied PR sugestions:
-removed un needed x === "string"s
-removed isntancename / modpack from icon reference and export
-logging per line on export
-fixed variable names
-linting

* linted

* Shows  "no export data available" when no export happens

* -single sprite sheet
-removed static icons with no prototype

* Fix formatting

* Rename types to allowedTypes  for clarity

* -removed duplication check (redundant)
-fixed bug overwriting metadata with same name from previous categories

* Rename asset keys for consistency in export validation  test

---------

Co-authored-by: Solar <solar@homelab.local>
2026-03-01 23:05:39 +00:00
Cooldude2606andHornwitser 2235294c59 Refactor user and instance manager (#837)
* Refactor UserManager to use Datastore

* Rename lib.User to UserDetails also adds to/fromUserDetails

* Fix existing tests

* Add user class tests

* Add tests for finaliseUsers

* Move HostDetails to own file with tests

* Move InstanceDetails to its own file with tests

* Rename HostInfo and add tests

* Rename InstanceInfo and add tests

- Also changes the names of the host properties related to instances
- Also removes the need to specify the location of InstanceInfo

* Make json round trip test validate schema

* Add InstanceManager

* Fix lint errors

* Extra tests

* Add final test coverage

* Add one last test to InstanceManager

* Response to comments

* Use generator rather than return anonumous generator

Co-authored-by: Hornwitser <github@hornwitser.no>

* Fix lint issue with generator

---------

Co-authored-by: Hornwitser <github@hornwitser.no>
2026-03-01 23:04:37 +00:00
Cooldude2606 d48b731ea4 Add lockfile bypass 2026-01-08 22:48:00 +00:00
Cooldude2606 bccb6527a7 Add lock file for configs 2026-01-07 01:20:40 +00:00
Cooldude2606 d52931a326 Better handling of mod dependencies (#801)
* Add ModDependency and version types

* Add warnings for mod dependencies

* Add warning for wrong factorio version

* Add fix issues button

* Add automatic dependency resolution and download

* Add abilty to check for updates

* Add extra test coverage

* One extra test

* Fix copy paste error
2025-11-07 19:22:17 +00:00