Files
attune/work-summary/2026-02-nodejs-runtime-fix.md

94 lines
4.3 KiB
Markdown

# Node.js Runtime Fix
**Date**: 2026-02
**Scope**: Worker runtime loading, environment setup, action execution
## Problem
Node.js actions from installed packs (e.g., `nodejs_example`) were stuck in "In Progress" status and never completing. Three root causes were identified:
### 1. Runtime Name Mismatch (Blocking)
The `ATTUNE_WORKER_RUNTIMES` env var for the node worker is `shell,node`, but the database runtime name is "Node.js" which lowercases to `"node.js"`. The worker's filter used exact string matching (`filter.contains(&rt_name)`), so `"node.js"` was never found in `["shell", "node"]`. **The Node.js ProcessRuntime was never registered**, meaning no worker could handle Node.js executions.
### 2. Broken Environment Setup
The Node.js `execution_config` in `packs/core/runtimes/nodejs.yaml` had:
- `create_command: ["npm", "init", "-y"]` — ran in the pack directory (read-only in Docker), didn't create anything at `{env_dir}`
- `install_command: ["npm", "install", "--prefix", "{pack_dir}"]` — tried to write `node_modules` into the read-only pack directory
### 3. Missing NODE_PATH
Even with correct installation, Node.js couldn't find modules installed at `{env_dir}/node_modules` because Node resolves modules relative to the script location, not the environment directory. No mechanism existed to set `NODE_PATH` during execution.
## Solution
### Runtime Name Normalization
Added `normalize_runtime_name()` and `runtime_in_filter()` to `crates/common/src/runtime_detection.rs`. These functions map common aliases to canonical names:
- `node` / `nodejs` / `node.js``node`
- `python` / `python3``python`
- `shell` / `bash` / `sh``shell`
- `native` / `builtin` / `standalone``native`
Updated `crates/worker/src/service.rs` and `crates/worker/src/env_setup.rs` to use `runtime_in_filter()` instead of exact string matching.
### RuntimeExecutionConfig.env_vars
Added an `env_vars: HashMap<String, String>` field to `RuntimeExecutionConfig` in `crates/common/src/models.rs`. Values support the same template variables as other fields (`{env_dir}`, `{pack_dir}`, `{interpreter}`, `{manifest_path}`).
In `ProcessRuntime::execute` (`crates/worker/src/runtime/process.rs`), runtime env_vars are resolved and injected into the action's environment before building the command.
### Fixed Node.js Runtime Config
Updated `packs/core/runtimes/nodejs.yaml` and `scripts/seed_runtimes.sql`:
```yaml
execution_config:
interpreter:
binary: node
args: []
file_extension: ".js"
environment:
env_type: node_modules
dir_name: node_modules
create_command:
- sh
- "-c"
- "mkdir -p {env_dir} && cp {manifest_path} {env_dir}/ 2>/dev/null || true"
interpreter_path: null
dependencies:
manifest_file: package.json
install_command:
- npm
- install
- "--prefix"
- "{env_dir}"
env_vars:
NODE_PATH: "{env_dir}/node_modules"
```
## Files Changed
| File | Change |
|------|--------|
| `crates/common/src/models.rs` | Added `env_vars` field to `RuntimeExecutionConfig` |
| `crates/common/src/runtime_detection.rs` | Added `normalize_runtime_name()`, `runtime_matches_filter()`, `runtime_in_filter()` with tests |
| `crates/worker/src/service.rs` | Use `runtime_in_filter()` for ATTUNE_WORKER_RUNTIMES matching |
| `crates/worker/src/env_setup.rs` | Use `runtime_in_filter()` for runtime filter matching in env setup |
| `crates/worker/src/runtime/process.rs` | Inject `env_vars` into action environment during execution |
| `crates/worker/src/runtime/local.rs` | Added `env_vars` field to fallback config |
| `packs/core/runtimes/nodejs.yaml` | Fixed `create_command`, `install_command`, added `env_vars` |
| `scripts/seed_runtimes.sql` | Fixed Node.js execution_config to match YAML |
| `crates/worker/tests/*.rs` | Added `env_vars` field to test configs |
| `AGENTS.md` | Documented runtime name normalization, env_vars, and env setup requirements |
## Testing
- All 424 unit tests pass
- Zero compiler warnings
- New tests for `normalize_runtime_name`, `runtime_matches_filter`, `runtime_in_filter`
## Deployment Notes
After deploying, the Node.js runtime config in the database needs to be updated. This happens automatically when packs are re-registered (the pack loader reads the updated YAML). Alternatively, run the updated `seed_runtimes.sql` script.