Summary
With the astrid:process/host@1.0.0 install fix applied (see #23), a JS capsule installs and its @install / @run hooks run correctly — but bus-routed CLI-verb / tool dispatch never reaches it.
Setup that should work on 0.9.4:
astrid-capsule-cli 0.2.0 installed (CLI verbs route through it; its manifest shows the current schema — topic delivery declared via [publish] / [subscribe] tables).
- Provider-targeted run topic confirmed from capsule-cli source:
cli.v1.command.run.<provider-id>.
- Our manifest declares
[subscribe] "cli.v1.command.run.arcade-player" and [publish] "cli.v1.command.result.*"; the interceptor handler logs on its first line; the @run loop is healthy.
Result: astrid capsule arcade status times out after 70 s and the entry log never appears — the hook is never invoked. The Rust-SDK capsule (astrid-capsule-cli) demonstrably receives subscribed topics on the same kernel.
Conclusion: the published JS SDK 0.1.0 implements lifecycle hooks and the run loop (both verified working) but not whatever guest export current kernels call to deliver bus topics. JS capsules therefore cannot receive tool/CLI dispatch until the SDK ships that interface.
Airtight confirmation (capsule-to-capsule probe)
A dedicated probe capsule (league-pinger, [publish] "arcade.v1.league.ping") publishing to a topic our capsule subscribes to, both loaded in the same daemon:
- JS publish works from the
@run (runtime) instance — kernel log confirms published arcade.v1.league.ping (daemon).
- JS publish fails from lifecycle instances —
[HostError] ipc.publish(...) during install/upgrade. So publish is available to the @run runtime instance but not to the lifecycle instance — a capability difference between the two instance kinds.
- Delivery to the subscribed JS capsule never happens (0 of N pings), while the Rust capsule receives its subscribed topics on the same kernel.
Impact
JS capsules cannot expose tools or CLI verbs to the rest of the system until the SDK ships the subscribe-delivery guest export — a large capability gap versus Rust capsules.
Two smaller items surfaced alongside (docs, or split out if preferred)
1. A fresh install doesn't build. npm i @unicity-astrid/{sdk,build}@0.1.0 does not build: the build tool resolves the SDK runtime at node_modules/@unicity-astrid/astrid-sdk and the canonical WIT at node_modules/contracts/host, neither of which npm creates. Required manual aliasing:
- link/junction
@unicity-astrid/astrid-sdk → @unicity-astrid/sdk — must be a link, not a copy (a copy makes esbuild bundle two SDK instances and the decorator registry splits, yielding "No @capsule class registered"), and
- copy
@unicity-astrid/contracts → node_modules/contracts.
2. A returned @run is treated as a crash. Returning from @run right after runtime.signalReady() produces WASM run loop exited unexpectedly and a restart storm (5 attempts). The run loop must block forever; the naive "signal and return" reading of the API is fatal and should be documented.
Environment
@unicity-astrid/sdk 0.1.0, @unicity-astrid/build 0.1.0 (npm)
- astrid 0.9.4 release binary, x86_64-unknown-linux-gnu;
astrid-capsule-cli 0.2.0 (release asset)
- Ubuntu 24.04 (WSL2), Node 22
Part of a small batch of findings from building a non-trivial JS capsule. Related: #20, #23, #24. A minimal reproducing repo (including the league-pinger probe) is available on request.
Summary
With the
astrid:process/host@1.0.0install fix applied (see #23), a JS capsule installs and its@install/@runhooks run correctly — but bus-routed CLI-verb / tool dispatch never reaches it.Setup that should work on 0.9.4:
astrid-capsule-cli0.2.0 installed (CLI verbs route through it; its manifest shows the current schema — topic delivery declared via[publish]/[subscribe]tables).cli.v1.command.run.<provider-id>.[subscribe] "cli.v1.command.run.arcade-player"and[publish] "cli.v1.command.result.*"; the interceptor handler logs on its first line; the@runloop is healthy.Result:
astrid capsule arcade statustimes out after 70 s and the entry log never appears — the hook is never invoked. The Rust-SDK capsule (astrid-capsule-cli) demonstrably receives subscribed topics on the same kernel.Conclusion: the published JS SDK 0.1.0 implements lifecycle hooks and the run loop (both verified working) but not whatever guest export current kernels call to deliver bus topics. JS capsules therefore cannot receive tool/CLI dispatch until the SDK ships that interface.
Airtight confirmation (capsule-to-capsule probe)
A dedicated probe capsule (
league-pinger,[publish] "arcade.v1.league.ping") publishing to a topic our capsule subscribes to, both loaded in the same daemon:@run(runtime) instance — kernel log confirmspublished arcade.v1.league.ping (daemon).[HostError] ipc.publish(...)during install/upgrade. So publish is available to the@runruntime instance but not to the lifecycle instance — a capability difference between the two instance kinds.Impact
JS capsules cannot expose tools or CLI verbs to the rest of the system until the SDK ships the subscribe-delivery guest export — a large capability gap versus Rust capsules.
Two smaller items surfaced alongside (docs, or split out if preferred)
1. A fresh install doesn't build.
npm i @unicity-astrid/{sdk,build}@0.1.0does not build: the build tool resolves the SDK runtime atnode_modules/@unicity-astrid/astrid-sdkand the canonical WIT atnode_modules/contracts/host, neither of which npm creates. Required manual aliasing:@unicity-astrid/astrid-sdk→@unicity-astrid/sdk— must be a link, not a copy (a copy makes esbuild bundle two SDK instances and the decorator registry splits, yielding "No @capsule class registered"), and@unicity-astrid/contracts→node_modules/contracts.2. A returned
@runis treated as a crash. Returning from@runright afterruntime.signalReady()producesWASM run loop exited unexpectedlyand a restart storm (5 attempts). The run loop must block forever; the naive "signal and return" reading of the API is fatal and should be documented.Environment
@unicity-astrid/sdk0.1.0,@unicity-astrid/build0.1.0 (npm)astrid-capsule-cli0.2.0 (release asset)Part of a small batch of findings from building a non-trivial JS capsule. Related: #20, #23, #24. A minimal reproducing repo (including the
league-pingerprobe) is available on request.