Skip to content

Switch to isolated-vm and event based automation scripts - Attempt 3 and final one#80

Merged
heavyrubberslave merged 14 commits into
mainfrom
feature/isolated-vm-attempt-3-event-based
Jun 1, 2026
Merged

Switch to isolated-vm and event based automation scripts - Attempt 3 and final one#80
heavyrubberslave merged 14 commits into
mainfrom
feature/isolated-vm-attempt-3-event-based

Conversation

@heavyrubberslave

@heavyrubberslave heavyrubberslave commented May 31, 2026

Copy link
Copy Markdown
Member

Fixes #17

Summary by CodeRabbit

  • Chores

    • Bumped Node.js engine requirement to 24.x and updated CI test matrix to 24.x and 26.x
    • Added esbuild dependency; removed vm2
  • New Features

    • Improved automation script runtime: more reliable startup/shutdown, ordered event dispatching, console.log forwarding, and device attribute proxying
  • Tests

    • Expanded unit tests for automation scripts and various device protocols
  • Health

    • System OS fields in health metrics now always populated (non-null)

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@heavyrubberslave, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 58 minutes and 13 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0279f22c-3ecd-4c6c-aa11-f4d79ea12aec

📥 Commits

Reviewing files that changed from the base of the PR and between 3a69d41 and b96a4e4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • .github/workflows/test.yml
  • package.json
  • src/automation/scriptRuntime.ts
  • src/device/protocol/buttplugIo/buttplugIoWebsocketDeviceProvider.ts
  • src/serviceProvider/automationServiceProvider.ts
  • src/util/error.ts
  • tests/unit/automation/scriptRuntime.spec.ts
📝 Walkthrough

Walkthrough

Refactors ScriptRuntime from vm2 to isolated-vm with an injected BOOTSTRAP_SCRIPT, makes lifecycle methods async with serialized event dispatch, updates controller to await load, adjusts device test constructors and TypeScript test config, bumps Node engine/CI versions, adds esbuild, and narrows health OS types.

Changes

ScriptRuntime isolated-vm refactoring and integration

Layer / File(s) Summary
ScriptRuntime contracts and isolated-vm implementation
src/automation/scriptRuntime.ts
Switch to isolated-vm, export SupportedDeviceEvent with non-nullable type/device, add BOOTSTRAP_SCRIPT exposing runtime API, and replace vm/sandbox state with Isolate/Context/Reference fields.
RunScriptController async load integration
src/controller/automation/runScriptController.ts
execute() becomes async and awaits this.scriptRuntime.load(scriptCode).
Comprehensive ScriptRuntime isolated-vm test suite
tests/unit/automation/scriptRuntime.spec.ts
Adds a Vitest suite exercising lifecycle events, onStart/onStop, console.log forwarding, event payloads, device proxy get/set, devices.getAll/getById, event ordering, and safe no-op behavior.

Device test constructor and TypeScript configuration updates

Layer / File(s) Summary
Device test constructors with EventEmitter and Logger injection
tests/unit/device/protocol/buttplugIo/buttplugIoDevice.spec.ts, tests/unit/device/protocol/slvCtrlPlus/genericSlvCtrlPlusDevice.spec.ts, tests/unit/device/protocol/virtual/display/displayVirtualDevice.spec.ts, tests/unit/device/testDeviceProvider.ts
Test device factories now pass EventEmitter() and mocked Logger to device constructors; TestDeviceProvider is parameterized and requires DeviceManager in constructor with init() marked override.
Test-specific TypeScript configuration
tests/tsconfig.json
Adds tests/tsconfig.json extending root config with noEmit: true, rootDir set to parent, and includes for source and test files.

Environment, dependencies, and type updates

Layer / File(s) Summary
Node.js version updates in CI workflows
.github/workflows/release.yml, .github/workflows/test.yml
Release workflow Node version changed to 24.x; test matrix updated to [24.x, 26.x].
Package.json dependencies and scripts
package.json
Adds esbuild dependency, removes vm2, updates engines.node to >=24.0.0 <27.0.0, and expands typecheck script to run tsc for tests/tsconfig.json.
Health metrics system.os type narrowing
src/health/healthMetricsCollector.ts
HealthMetrics.system.os fields name, type, arch, platform changed from `string

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

"A rabbit hops through code so grand,
From vm2's cage to isolate's land,
Bootstrap scripts whisper, events align,
Tests applaud the queued dispatch line,
Node 24 hums — the server's new band."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating automation scripts from vm2 to isolated-vm with event-based execution model.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/isolated-vm-attempt-3-event-based

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@heavyrubberslave heavyrubberslave added the minor Creates a new minor release if merged label May 31, 2026
@heavyrubberslave heavyrubberslave marked this pull request as ready for review May 31, 2026 16:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/automation/scriptRuntime.ts (1)

74-77: 💤 Low value

setAttribute is fire-and-forget; script won't observe failures.

The isolate's setAttribute uses applySync and immediately returns Promise.resolve() (lines 74-77), while the host-side handler (lines 213-218) catches errors but doesn't propagate them back. If the underlying device rejects, the script will never know.

If this is intentional for performance, consider documenting it in the bootstrap JSDoc. Otherwise, propagate errors back to the isolate.

Also applies to: 213-218

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/automation/scriptRuntime.ts` around lines 74 - 77, The setAttribute
implementation is currently fire-and-forget: it calls
__setAttribute.applySync(...) and always returns Promise.resolve(), so failures
on the host never reach the script; fix by propagating errors: change
setAttribute to call __setAttribute.applySync(...) inside a try/catch and return
a resolved promise on success and a rejected promise (or rethrow) with the
caught error on failure, and update the host-side attribute handler (the
function that currently catches errors when handling attribute sets) to not
swallow errors but to rethrow or return the error to the isolate so
applySync/applyAsync can surface the failure to setAttribute.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test.yml:
- Line 14: CI Node test matrix excludes Node 22 while package.json allows
>=22.0.0, so either add "22.x" back into the node-version matrix entry in the
GitHub Actions workflow (the node-version array in .github/workflows/test.yml)
to test 22,24,26, or update the package.json engines.node range to ">=24.0.0
<27.0.0" to match the current matrix; pick one and make the corresponding change
so the declared engines and CI matrix are aligned.

In `@src/automation/scriptRuntime.ts`:
- Around line 259-269: In load(), the await on the lifecycle handler (via
this.pendingLifecycleDone and this.lifecycleRef!.apply('start')) can reject and
leak the created isolate, context, logWriter and runningSince; wrap that await
in a try/catch and perform a cleanup/partial-teardown when it fails: catch the
rejection from the pendingLifecycleDone promise, call the existing teardown/stop
helper (or add a small private method like partialTeardown that releases
isolate, context, clears logWriter and runningSince), rethrow the original error
so callers still see the failure, and ensure pendingLifecycleDone is cleared;
this ensures failure inside the onStart handler won’t leave resources allocated.
- Around line 316-339: runForEvent pushes a closure that dereferences
this.dispatchRef at execution time, which can race with stop() setting
dispatchRef to null; capture the reference at enqueue time (e.g. const dispatch
= this.dispatchRef) and use that captured variable inside the Promise instead of
this.dispatchRef and avoid the non-null assertion, or alternatively add a
null-check inside the queued task before calling apply; update runForEvent (and
the queued closure that calls dispatch.apply) to use the captured symbol
(dispatch) and keep existing pendingEventDone/resolve/reject behavior so
processQueue and eventQueue semantics remain unchanged.

---

Nitpick comments:
In `@src/automation/scriptRuntime.ts`:
- Around line 74-77: The setAttribute implementation is currently
fire-and-forget: it calls __setAttribute.applySync(...) and always returns
Promise.resolve(), so failures on the host never reach the script; fix by
propagating errors: change setAttribute to call __setAttribute.applySync(...)
inside a try/catch and return a resolved promise on success and a rejected
promise (or rethrow) with the caught error on failure, and update the host-side
attribute handler (the function that currently catches errors when handling
attribute sets) to not swallow errors but to rethrow or return the error to the
isolate so applySync/applyAsync can surface the failure to setAttribute.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31fe3e2d-1bbf-4f72-8e61-b8d71aa0e9e3

📥 Commits

Reviewing files that changed from the base of the PR and between 923c07c and d29aa1b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • package.json
  • src/automation/scriptRuntime.ts
  • src/controller/automation/runScriptController.ts
  • src/health/healthMetricsCollector.ts
  • tests/tsconfig.json
  • tests/unit/automation/scriptRuntime.spec.ts
  • tests/unit/device/protocol/buttplugIo/buttplugIoDevice.spec.ts
  • tests/unit/device/protocol/slvCtrlPlus/genericSlvCtrlPlusDevice.spec.ts
  • tests/unit/device/protocol/virtual/display/displayVirtualDevice.spec.ts
  • tests/unit/device/testDeviceProvider.ts

Comment thread .github/workflows/test.yml Outdated
Comment thread src/automation/scriptRuntime.ts Outdated
Comment thread src/automation/scriptRuntime.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 62: The engines.node range in package.json currently allows Node 24/25
but the repo depends on isolated-vm ^7.0.0 which requires Node 26.x; update the
engines.node field (package.json -> "engines.node") to restrict to Node 26 only
(for example ">=26.0.0 <27.0.0") or alternatively bump/downgrade the isolated-vm
dependency to match the Node versions you intend to support (reference
isolated-vm ^7.0.0 and the package.json "engines.node" entry when making the
change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 844a1ed3-9510-4d6c-b885-324f4a965dda

📥 Commits

Reviewing files that changed from the base of the PR and between d29aa1b and 3a69d41.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • src/automation/scriptRuntime.ts

Comment thread package.json Outdated
@heavyrubberslave heavyrubberslave merged commit 711fcc8 into main Jun 1, 2026
7 checks passed
@heavyrubberslave heavyrubberslave deleted the feature/isolated-vm-attempt-3-event-based branch June 1, 2026 21:34
This was referenced Jun 4, 2026
This was referenced Jun 7, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Creates a new minor release if merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Switch form vm2 to isolated-vm

1 participant