100 Commits
Author SHA1 Message Date
Cooldude2606andClaude Opus 5.5 7cf9cf8d3e Unpatch saves started with save patching disabled (#1184)
* Unpatch saves started with save patching disabled

Turning factorio.enable_save_patching off did nothing to a save that
was patched before. Its control.lua still loaded every module, so the
clusterio_api interface, player_sync's join handling and the other
module event handlers kept running with no plugin on the host side.

When patching is disabled the instance now restores the original
control.lua that patching kept as scenario.lua and removes the module
files, locale files included, as listed in clusterio.json. The patch
info stays with an empty module list so the patch number keeps
counting. Otherwise patching the save again would reuse the number
stored in the game and on_server_startup would not be raised. Data
the modules put in storage is left alone.

PatchInfo.fromSave dropped the version read from the save, so the
check against newer patch versions never fired. It now keeps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove scenario.lua when unpatching a save

Unpatching moves scenario.lua back to control.lua instead of copying it, so the save only keeps clusterio.json for the patch number. Patching such a save again copies control.lua back to scenario.lua.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:58:13 +00:00
Cooldude2606andClaude Opus 5.5 308cca9b7d Add HostManager for host records and host tokens (#1180)
* Add HostManager for host records and host tokens

controller.hosts is now a HostManager wrapping the hosts datastore, the
same way controller.users wraps the users datastore. It owns creating
hosts (moved from HostPairingManager), revoking them (moved from
ControlConnection) and signing and verifying host tokens (moved from
Controller, generateHostToken is renamed signHostToken to match
signUserToken).

UserManager gains verifyUserToken, replacing the copies of the user
token check in WsServer and routes. The WsServer copy accepted a token
without iat when the user had no tokenValidAfter; the shared check
always requires iat, which jsonwebtoken adds by default.

Follows Cooldude2606's comment on #1161.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Return the host record from verifyHostToken

Matches verifyUserToken returning the user. HostConnection._saveInfo is
renamed saveRecord to match User.saveRecord.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 00:51:36 +00:00
Cooldude2606andClaude Opus 5.5 6eaf48aefe Follow teleports of a syncing player in player_sync (#1168)
* Keep a syncing spectator where its body stood

A player waiting for player_sync data is a spectator who could walk away,
so restore_position teleported them back at the end. That also undid
teleports by other mods made during the sync, such as universal_edges
placing an arriving player.

The spectator is now pulled back on every tile change, and a jump of more
than 4 tiles or to another surface counts as another mod's teleport and
moves where the body comes back. A failed acquire that takes the body
keeps its place too.

Closes #908

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rename body place to controller location

Review asked for location and controller over place and body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Let a syncing spectator look around

Review asked for the spectator to move freely while it waits. Only a jump
of more than 16 tiles from where it was last seen, or to another surface,
counts as a teleport and moves the restore location. The body place names
from #1156 become restore_location, keep_restore_location and
controller_taken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 23:43:42 +00:00
Cooldude2606andClaude Opus 5.5 0a23af61f9 Add instance templates for instance config inheritance (#1142)
* 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>

* 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>

* Give templates their own table and keep rcon_password per instance

The templates table shows name, inheritor count and a create instance
action instead of the instance status and start/stop columns.
factorio.rcon_password joins the ports as never inherited.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use link and disconnect icons for template inheritance

Overridden values show a disconnect icon. Clicking it stages a link,
shown with a link icon, which is applied together with the other
changes instead of right away.

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show the template value while a link is staged

Part of #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 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>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:48:55 +00:00
d7d1859aa0 Report missing base game icons when exporting from headless Factorio (#1153)
* Report missing base game icons when exporting from headless Factorio

The headless build has no graphics, so export-data found none of the
base game's icon files. Plain icons were skipped silently and layered
icons were drawn blank and counted as exported.

Skip a layered icon when any of its layers is missing, and log one
warning with the number of base game icon files that were not found.
Add host.factorio_graphics_directory to read builtin mod graphics from
a full install when the instance runs the headless build.

Fixes #1068

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Export without the graphics directory when it has no matching install

If host.factorio_graphics_directory had no install matching the major and
minor version of the instance, or did not exist, findVersion threw and the
whole export failed. Without the setting the same instance exports everything
except the base game icons, so it now logs a warning that names the setting
and exports without it.

* Find a full install in host.factorio_directory instead of a setting

Per review, drop host.factorio_graphics_directory. findVersion takes a
headless option that filters installs by build, telling them apart by
data/base/graphics/icons. When the instance runs the headless build the
export looks for a full install of the same major.minor version in the
host's factorio directory. Server startup is unchanged.

If none is found the export still runs, and the reason is included in
the missing icons warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address review on findVersion and the export graphics lookup

findVersion takes headless as a plain optional argument, the helper is
checkInstallType, the warning is checkMissingBuiltinIcons and the full
install lookup is inlined into exportData.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Danielv123 <danielv@danielv.no>
2026-11-01 23:09:55 +00:00
Cooldude2606andClaude Opus 5.5 9b654b1ad8 Start the controller and host CLIs faster (#1159)
* Answer the disconnect prepare in the close old connections test

The test's first connector had no Link, so the controller's disconnect
prepare went unanswered and it waited the full 10 s timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Start the controller and host CLIs faster

Like ctl, the controller and host now compile schema validators on first
use instead of at import, which cost about 120 ms per invocation. The
Controller and Host classes are only imported by the commands that
start them, so config, bootstrap and the other commands no longer load
express, ws and the rest of the servers. clusteriocontroller config set
goes from 413 ms to 231 ms and clusteriohost config set from 412 ms to
225 ms.

The integration tests run the bins, like npx does, instead of the
package directories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:43:33 +00:00
Cooldude2606 cc8888fc69 Merge pull request #1160 from bbassie/ci/split-tests
Split each CI test job into two halves
2026-10-30 20:40:52 +00:00
Cooldude2606andClaude Opus 5.5 3707fc35fc Drop unused dependencies from web_ui and tests (#1163)
* Take FieldData from antd instead of rc-field-form

web_ui depended on rc-field-form 1.x for a single type import, while
antd 5 uses rc-field-form 2.x. Derive the type from antd's FormProps
and drop the dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Move typescript to web_ui devDependencies

web_ui only needs typescript to build its own types. The runtime
dependency is a leftover from webpack's ts-loader, Vite doesn't use it,
and it pulls in a 30 MB native compiler for anyone installing web_ui.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use native FormData in controller route tests

Node's FormData and Blob do what the form-data package did here, and
fetch sets the multipart Content-Type header itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove leftover typedoc.json files

TypeDoc was dropped in #1147, but these configs still ship in the
published packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 20:29:12 +00:00
Cooldude2606 53a4fce934 Merge pull request #1151 from bbassie/fix/stop-while-starting
Fix stopping an instance while it is starting
2026-10-30 20:04:22 +00:00
Cooldude2606 fa2883d74e Merge pull request #1147 from bbassie/build/typescript-7
Drop TypeDoc and move to TypeScript 7
2026-10-29 17:55:01 +00:00
Cooldude2606 96dddb1526 Merge pull request #1146 from bbassie/feature/compat-breaks
Allow known breaks in the version compatibility test
2026-10-29 00:51:37 +00:00
Cooldude2606andClaude Opus 5.5 a82af49c05 Keep auto paused instances paused (#1143)
* 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>

* Check connection and status in subspace_storage timers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:05:32 +00:00
Cooldude2606 2fb5300ec5 Merge pull request #1140 from bbassie/fix/remove-web-plugin-column
Remove the web UI status column from the plugin pages
2026-10-28 23:54:46 +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
Cooldude2606 188de979b6 Merge pull request #1130 from bbassie/build/vite-plus
Draft: Replace webpack with Vite and load plugins as ES modules (vote)
2026-10-28 23:17:45 +00:00
Cooldude2606andClaude Opus 5.5 35125a28ab Keep error classes across links (#999)
* 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>

* Add UnavailableError and keep the PermissionError name

Host not connected, host shutting down and instance not running are
now UnavailableError (503) instead of ConflictError. The request error
migration notes move to a new 2.1 guide, since this ships in 2.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the new error classes in code added on master

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 22:47:01 +00:00
Cooldude2606 e36a76acc5 Merge pull request #1129 from bbassie/refactor/async-factory
Replace bootstrap methods with async factories
2026-10-27 21:31:49 +00:00
Cooldude2606 bda5c5a8c0 Merge pull request #1128 from bbassie/remove-find-unused-name
Remove lib.findUnusedName
2026-10-27 21:31:21 +00:00
Cooldude2606 7fa511873b Prepare 2.0.3 2026-10-25 20:15:27 +00:00
Cooldude2606 4128f75328 Merge pull request #1012 from bbassie/feature/inventory-sync-tools
Add inspect and modify tools for inventory sync
2026-10-20 00:43:33 +01:00
Cooldude2606 4fd1d56b4a Merge pull request #1106 from ILLISIS/ps-armor-grid-bonus
Keep main inventory slots from armor equipment when restoring a character
2026-10-18 20:14:02 +01:00
Cooldude2606 ba800e853d Merge pull request #996 from bbassie/feature/instance-udp
Add UDP communication support for instances
2026-10-17 20:47:54 +01:00
Cooldude2606 5e2ecdd143 Merge pull request #1061 from bbassie/feature/inventory-sync-components
Allow inventory sync components to be turned off per instance
2026-10-17 19:45:19 +01:00
Cooldude2606andClaude Opus 5.5 ef943f22a4 Rename inventory_sync to player_sync (#1072)
* Let plugins migrate the data core stores for them

Plugins had no way to update the config fields and permissions core stores
for them, so renaming inventory_sync needed migrations in core packages.

A plugin can now declare a migrationEntrypoint. It's loaded on the
controller and hosts before configs and roles are read, for every plugin in
the plugin list, and attaches handlers to sync hooks for the controller,
host and instance configs and roles. lib gets helpers to rename config
fields and permissions, and each config gets a hidden <plugin>.version field
set after load so migrations can tell which version saved the data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Write plugin versions while loading configs

The staging copy made by Config.set() went through the constructor, so
plugin migrations ran a second time on the live fields. A version-gated
migration was applied twice when setPluginVersions() changed the version.
Staging now copies the fields with update() instead.

A config saved without <plugin>.version got the running version as its
default, so setPluginVersions() saw no change and nothing saved the stamp.
The constructor now writes the registered plugin versions after the
migrations run and marks the config dirty when that changes a loaded value.
Controller.bootstrap() saves the instances that came out dirty, and
setPluginVersions() is gone.

Restructure the SyncHook and plugin config tests by method, and suggest the
controller config version for role migrations in the docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 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>

* Let old saves through the player left guard

on_pre_player_left_game runs before on_server_startup when an old autosave
with connected players is loaded, so the data is still under inventory_sync.
The guard returned before get_script_data() could move it, and the recovery
upload was never sent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:54:55 +01:00
Cooldude2606 70dff9ef68 Merge pull request #1073 from bbassie/feature/plugin-migrations
Let plugins migrate the data core stores for them
2026-10-14 17:49:27 +01:00
Cooldude2606 2c506df38a Merge pull request #1062 from PHIDIAS0303/patch-1
Add links to Exp Cluster and Chat Relay plugins
2026-10-13 21:38:53 +01:00
Cooldude2606 04c08efb0e Update ExpCluster description 2026-10-13 21:32:54 +01:00
Cooldude2606 b4cbb1294a Fix running Clusterio from published packages (#1070)
* Fix running Clusterio from published packages

Since the move to ES modules and pnpm 12, packages installed from npm
fail to start:

- The package imports of @clusterio/lib point Node at TypeScript
  sources, which Node refuses to strip types from inside node_modules.
- The controller, host and ctl import their package.json from next to
  the compiled entrypoint, a copy pnpm leaves out of the package.
- ctl, host and web_ui have no .npmignore, so pnpm packs them using the
  repository .gitignore and leaves out nearly all of dist/.

Resolve the package imports to the compiled files at runtime while
keeping the sources for type checking, import package.json by package
name so it resolves to the real file, and add .npmignore files so pnpm
packs the same files as npm.

* Load plugins installed from npm by package name

Plugins installed from npm are listed by package name, but the plugin
loader joined that name onto the main and entrypoint paths and imported
the result as a file, which resolves relative to the working directory.
Every npm installed plugin then failed to load. Resolve these paths from
the directory of the plugin's package.json instead.
2026-10-13 21:24:16 +01:00
Cooldude2606 48e8c53776 Merge pull request #1064 from ILLISIS/fix-ws-error-before-handshake
Handle WebSocket errors before the handshake completes
2026-10-12 22:03:08 +01:00
Cooldude2606 490b10e07a Merge pull request #1058 from bbassie/fix/subscriber-account-update
Resend subscriptions when the account permissions change
2026-10-08 18:52:33 +01:00
Cooldude2606 8a170eb2ac Merge pull request #1057 from bbassie/fix/bounded-send-buffer
Limit unacknowledged messages buffered for a connection
2026-10-08 17:25:57 +01:00
Cooldude2606 208ecb826e Merge pull request #1056 from bbassie/fix/subscription-permission-revoke
Drop subscriptions when a user loses permission for them
2026-10-08 17:23:43 +01:00
Cooldude2606 ec1ce79be9 Merge pull request #1055 from bbassie/fix/subscription-limits
Limit the number of subscription filters per subscriber
2026-10-08 17:02:33 +01:00
Cooldude2606andClaude Opus 5.5 faaaf399cb Add a plugin migration guide for 2.0 (#1050)
* Add a plugin migration guide for 2.0

Covers the plugin API changes between 2.0.0-alpha.27 and 2.0.0: ESM,
entrypoint functions and hooks, loadedPlugins, permissions on the
plugin declaration, the ctl renames, staticRoot and optional peer
dependencies.

Document that from 2.0 major versions contain breaking changes for end
users while minor versions may contain breaking changes for the plugin
API, and fix stale examples and names in writing-plugins.md and
managing-a-cluster.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Show the fromContext class pattern in the migration guide

Plugins that still want a class for state use a private constructor,
an async static fromContext and a static WeakMap lookup, which also
replaces controller.plugins.get() for cross-plugin access. Check
loadedPlugins with Set.has on the info from pluginInfos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Attach hooks in fromContext and export it as the entrypoint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:57:09 +01:00
Cooldude2606 ce6bfe282f Merge pull request #1052 from bbassie/refactor/migrate-plugins-2.0
Migrate bundled plugins to entrypoint functions
2026-10-08 11:12:21 +01:00
Cooldude2606 c3337bdedb Merge pull request #1049 from ColumbusLabs/fix/796-config-editor-field-names
Fix CLI config editor field names
2026-10-07 20:01:18 +01:00
Cooldude2606 c11079260d Fix copy paste error in error message 2026-10-07 19:52:16 +01: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
Cooldude2606 6eb7e1a81f Merge pull request #1048 from ILLISIS/fix/load-json-object-throw-on-missing
Throw on a missing file when loadJsonObject is asked to (follow-up to #1046)
2026-10-06 16:35:56 +01:00
Cooldude2606 d89a6e33f3 Merge pull request #1044 from bbassie/feature/role-permission-sections
Add a permission group list to the role page
2026-10-05 01:30:20 +01:00
Cooldude2606 7b2f7064b9 Fix lint error 2026-10-05 00:46:06 +01:00
Cooldude2606 4c0c89c5ff Fix broken manifest url resolution 2026-10-05 00:30:47 +01:00
Cooldude2606 d1b1f58f00 Fix broken manifest path resolution (#1047) 2026-10-04 20:42:57 +01:00
Cooldude2606andClaude Fable 5.1 2efb53aab1 Add a request to mark hosts and the controller as needing a restart (#1043)
* Add a request to mark hosts and the controller as needing a restart

Not every plugin arrives through PluginInstallRequest. Plugins checked
out into external_plugins or installed by hand on the machine never
flag a restart, so the web UI shows nothing and tooling that acts on
the flag never fires.

HostMarkRestartRequiredRequest and ControllerMarkRestartRequiredRequest
set config.restartRequired, guarded by the existing restart
permissions. They are exposed as "Mark Restart Required" in the control
dropdown of the host and controller pages, hidden once the flag is set,
and as mark-restart-required ctl commands. The mark shows up on the
next system metrics poll.

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

* Cover the mark-restart-required ctl commands

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 21:14:23 +01:00
Cooldude2606 1d1d9bb1c4 Merge pull request #1031 from bbassie/fix/stop-before-rcon-ready
Resolve instance stop when Factorio exits before RCON is ready
2026-09-13 18:35:14 +01:00
Cooldude2606 a6f2c2829e Merge pull request #1036 from bbassie/fix/host-save-details-ownership
Reject save detail updates for instances not on the sending host
2026-09-13 18:29:56 +01:00
Cooldude2606 0065d07fe3 Merge pull request #1028 from bbassie/fix/instance-config-change-crash
Log errors from instance config change handlers instead of exiting the host
2026-09-13 18:16:59 +01:00
Cooldude2606 16f28ec5c7 Merge pull request #1037 from bbassie/feature/changelog-stats
Add contributors title and statistics to the changelog script
2026-09-13 18:14:21 +01:00
Cooldude2606 9d04681a42 Change format output 2026-09-13 18:01:23 +01:00
Cooldude2606 775dd8126f Merge pull request #1029 from bbassie/fix/instance-assign-failure
Keep instances assignable after a failed assign or create
2026-09-13 17:47:48 +01:00
Cooldude2606andClaude Opus 5 a28e2bf9f6 Terminate control connections of deleted users (#1030)
* Terminate control connections of deleted users

ControlConnection keeps the User object it authenticated with and checks
permissions against it. Deleting the user removed the record but left
its sessions open, so a deleted user kept all its permissions until the
WebSocket closed.

Repro: create "victim" with role 0 and connect as it. Another admin
sends UserDeleteRequest("victim"). Victim can still send
UserCreateRequest("mallory") and UserUpdateRolesRequest("mallory", [0]).

Terminate the deleted user's connections like revoke-token does. When
a user deletes itself, defer terminating the requesting connection so
the response is sent first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Terminate deleted user sessions immediately

Drop the special case that deferred terminating the connection issuing
the delete, so deleting yourself behaves the same as revoke-token and
every session of the deleted user is closed before the handler returns.

Set tokenValidAfter to the current time in createUser instead of 0.
Deleting a user and recreating it under the same name left tokens issued
before the deletion valid, as the check is iat < tokenValidAfter. Doing
it in createUser covers every path that makes a user. A token signed in
the same second as creation is still accepted, so create-admin and the
other production paths sign after the user exists and are unaffected.

The integration harness signed the control token at import time, before
bootstrap create-admin created the user. Sign it in the root before hook
instead and reuse it in link.js.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Handle SessionLost when deleting your own account

Deleting your own account now terminates the session immediately, so the
web UI received a SessionLost rejection and reported "Error deleting
user" for a delete that had succeeded. Use the same branch revoke does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
2026-09-13 15:31:46 +01:00
Cooldude2606 c355fc6888 Merge branch 'master' into fix/terminate-deleted-user-sessions 2026-09-13 15:18:25 +01:00
Cooldude2606 3c692c4321 Merge branch 'master' into fix/instance-assign-failure 2026-09-13 14:04:45 +01:00
Cooldude2606 a7024c8f72 Merge pull request #1025 from bbassie/fix/esm-coverage
Collect coverage with c8 instead of nyc
2026-09-13 01:15:12 +01:00
Cooldude2606 820bafa11f Merge branch 'master' into fix/esm-coverage 2026-09-13 01:08:43 +01:00
Cooldude2606 82a9d0ee9e Merge pull request #1024 from bbassie/ci/pnpm-store-cache
Cache builds in CI and skip them where unused
2026-09-13 01:06:13 +01:00
Cooldude2606 5a3a0a189c Merge pull request #1023 from bbassie/chore/dependency-updates
Bump dependency versions
2026-09-13 00:41:25 +01:00
Cooldude2606 68d47ec507 Merge pull request #1022 from bbassie/chore/bump-deps
Bump pnpm, GitHub Actions and CI Factorio version
2026-09-13 00:08:55 +01:00
Cooldude2606 8be251447a Merge pull request #1018 from bbassie/feature/plugin-install
Add plugin install command to controller, host and ctl
2026-09-12 23:35:26 +01:00
Cooldude2606 008e1d7805 Merge pull request #1019 from bbassie/feature/metrics-auth
Require a token to scrape /metrics
2026-09-12 23:34:59 +01:00
Cooldude2606 2b981a5734 Merge pull request #1020 from bbassie/refactor/plugin-hooks-v2
Add typed hook system for plugins
2026-09-12 22:57:14 +01:00
Cooldude2606 e6b642fc19 Merge pull request #1021 from Hornwitser/package-dependencies
Fix plugin dependencies
2026-09-12 22:11:06 +01:00
Cooldude2606 512f62c22b Merge branch 'master' into feature/plugin-install 2026-09-12 22:10:52 +01:00
Cooldude2606 9f67d7ab76 Merge pull request #998 from bbassie/feature/update-all
Add update all for the controller, hosts and plugins
2026-09-12 02:19:04 +01:00
Cooldude2606 c2325a8b6e Add missing as const for permission string 2026-09-12 02:12:49 +01:00
Cooldude2606 06a6a7edf3 Merge branch 'master' into feature/update-all 2026-09-12 02:07:54 +01:00
Cooldude2606andClaude Fable 5.1 a80b035a0a Send only recipe notification changes on inventory download (#997)
* Send only recipe notification changes on inventory download

Lua includes its current cleared recipe list in the download request. The instance plugin diffs it against the stored list and sends back the names to add and remove instead of the full list. Lua rebuilds the full list from the snapshot kept in the download record and runs the existing deserializer, so the end state is unchanged.

Also stop the serializer from appending to the stored failed recipe list in place, which grew the list on every upload without a download in between.

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

* Test the inventory sync download handler

Covers the empty response, the full list without a snapshot, the delta with a snapshot, and the IPC registration.

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

* Apply recipe notification deltas directly in Lua

Review feedback: the download field now always carries the delta and the deserializer applies it against the current state instead of rebuilding the full list first. The previous failed recipe names are passed in so they survive the round trip. The version check moved to the call site.

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

* Fix failed not being cleared

* Send only the local state on download init

The current state sent with the download request no longer includes the failed names from the previous deserialization. Stored names unknown on this instance always end up in the add list, so the deserializer recomputes the failed list from that on every download and needs no previous state.

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

* Convert inventory sync test to ES module

Master migrated to ESM after this branch was created, so the CommonJS test failed to load on the merge commit.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
2026-09-12 01:49:40 +01:00
Cooldude2606 2aee5379b5 Merge pull request #984 from Hornwitser/migrate-to-esm
Migrate to ESM
2026-09-11 23:14:50 +01:00
Cooldude2606 68ce13ead4 Fix failed not being cleared 2026-09-11 22:46:44 +01:00
Cooldude2606 7987a5d925 Merge pull request #991 from bbassie/feature/clone-instance-save
Add save copying and start options when creating an instance
2026-09-09 20:03:27 +01:00
Cooldude2606 6671e3493e Merge pull request #994 from bbassie/fix/log-invalid-message-errors
Include schema validation errors in InvalidMessage stack
2026-09-09 15:29:03 +01:00
Cooldude2606 abfc5ff8d5 Merge pull request #990 from bbassie/fix/inventory-sync-shortcut-toggles
Sync shortcut toggles and game view settings in inventory_sync
2026-09-08 00:33:35 +01: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
Cooldude2606 1915250529 Merge pull request #979 from bbassie/feature/emmylua-lint
Add emmylua lint CI for module Lua
2026-08-31 00:25:06 +03:00
Cooldude2606 4d75f8d402 Add luarc to git ignore file 2026-08-30 22:15:56 +01:00
Cooldude2606 cc8f590bf4 Merge pull request #982 from Cooldude2606/ci/change-strategy
Update the test strategy
2026-08-30 23:55:49 +03:00
Cooldude2606 384a185a2e Update the test strategy
We now only perform one test on the oldest node version. In addition, timeout values were added and some other small tidy ups.
2026-08-30 21:50:47 +01:00
Cooldude2606 285a0018c9 Merge pull request #978 from bbassie/feature/version-compat-ci
Add CI check for controller and host version compatibility
2026-08-30 23:07:49 +03: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
Cooldude2606andClaude Fable 5 00ce7cdca6 Filter dependency resolution by the mod pack's Factorio version (#974)
* 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>

* Move dependency resolver tests to test/messages/mod.test.js

Also update the existing local mod fixtures to carry a factorio_version
and drop enable-all-feature-flags from the live expectations: the 2.0
releases of pyalternativeenergy only list it as optional, it was only
pulled in before because 2.1 releases were being selected.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:21:21 +01:00
Cooldude2606andClaude Fable 5 b5a0d43c96 Add NO_EXTERNAL_TEST flag to skip tests using external APIs (#976)
* Add NO_EXTERNAL_TEST flag to skip tests using external APIs

Adds an externalTest(this) helper alongside slowTest that skips the test
when NO_EXTERNAL_TEST is set and raises the timeout to 60s, plus a
no-external-test script. Marks the tests hitting factorio.com, the mod
portal and GitHub with it.

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

* 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>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:07:08 +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
Cooldude2606 3c9229087b Merge pull request #962 from bbassie/feature/plugin-system-tests
Add integration tests for the plugin system
2026-08-11 15:25:17 +01: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
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 a186bde2a9 Fix CI not performing coverage 2026-08-08 23:47:24 +01:00
Cooldude2606 e9450db83b Move test location 2026-08-08 23:45:03 +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 f6240d0911 Merge pull request #947 from solarcloud7/lint/link-method-binding
Document Link method binding requirements
2026-07-31 18:47:27 +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
Cooldude2606 02e50211f4 Merge pull request #957 from bbassie/feature/download-file-cleanup
Discard the temporary file when a download fails
2026-07-29 23:34:06 +01: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
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