mikrojs 0.0.0 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +76 -0
- package/bin/mikrojs.js +3 -0
- package/dist/_exports/ble.d.ts +3 -0
- package/dist/_exports/ble.d.ts.map +1 -0
- package/dist/_exports/ble.js +2 -0
- package/dist/_exports/ble.js.map +1 -0
- package/dist/_exports/cbor.d.ts +3 -0
- package/dist/_exports/cbor.d.ts.map +1 -0
- package/dist/_exports/cbor.js +2 -0
- package/dist/_exports/cbor.js.map +1 -0
- package/dist/_exports/console.d.ts +2 -0
- package/dist/_exports/console.d.ts.map +1 -0
- package/dist/_exports/console.js +2 -0
- package/dist/_exports/console.js.map +1 -0
- package/dist/_exports/env.d.ts +2 -0
- package/dist/_exports/env.d.ts.map +1 -0
- package/dist/_exports/env.js +2 -0
- package/dist/_exports/env.js.map +1 -0
- package/dist/_exports/format.d.ts +2 -0
- package/dist/_exports/format.d.ts.map +1 -0
- package/dist/_exports/format.js +2 -0
- package/dist/_exports/format.js.map +1 -0
- package/dist/_exports/fs.d.ts +2 -0
- package/dist/_exports/fs.d.ts.map +1 -0
- package/dist/_exports/fs.js +2 -0
- package/dist/_exports/fs.js.map +1 -0
- package/dist/_exports/http/helpers.d.ts +3 -0
- package/dist/_exports/http/helpers.d.ts.map +1 -0
- package/dist/_exports/http/helpers.js +5 -0
- package/dist/_exports/http/helpers.js.map +1 -0
- package/dist/_exports/http/request.d.ts +4 -0
- package/dist/_exports/http/request.d.ts.map +1 -0
- package/dist/_exports/http/request.js +10 -0
- package/dist/_exports/http/request.js.map +1 -0
- package/dist/_exports/i2c.d.ts +2 -0
- package/dist/_exports/i2c.d.ts.map +1 -0
- package/dist/_exports/i2c.js +2 -0
- package/dist/_exports/i2c.js.map +1 -0
- package/dist/_exports/index.d.ts +61 -0
- package/dist/_exports/index.d.ts.map +1 -0
- package/dist/_exports/index.js +4 -0
- package/dist/_exports/index.js.map +1 -0
- package/dist/_exports/inspect.d.ts +2 -0
- package/dist/_exports/inspect.d.ts.map +1 -0
- package/dist/_exports/inspect.js +2 -0
- package/dist/_exports/inspect.js.map +1 -0
- package/dist/_exports/kv/nvs.d.ts +3 -0
- package/dist/_exports/kv/nvs.d.ts.map +1 -0
- package/dist/_exports/kv/nvs.js +2 -0
- package/dist/_exports/kv/nvs.js.map +1 -0
- package/dist/_exports/kv/rtc.d.ts +3 -0
- package/dist/_exports/kv/rtc.d.ts.map +1 -0
- package/dist/_exports/kv/rtc.js +2 -0
- package/dist/_exports/kv/rtc.js.map +1 -0
- package/dist/_exports/neopixel.d.ts +2 -0
- package/dist/_exports/neopixel.d.ts.map +1 -0
- package/dist/_exports/neopixel.js +2 -0
- package/dist/_exports/neopixel.js.map +1 -0
- package/dist/_exports/pin.d.ts +2 -0
- package/dist/_exports/pin.d.ts.map +1 -0
- package/dist/_exports/pin.js +2 -0
- package/dist/_exports/pin.js.map +1 -0
- package/dist/_exports/pwm.d.ts +2 -0
- package/dist/_exports/pwm.d.ts.map +1 -0
- package/dist/_exports/pwm.js +2 -0
- package/dist/_exports/pwm.js.map +1 -0
- package/dist/_exports/reader.d.ts +2 -0
- package/dist/_exports/reader.d.ts.map +1 -0
- package/dist/_exports/reader.js +2 -0
- package/dist/_exports/reader.js.map +1 -0
- package/dist/_exports/result.d.ts +2 -0
- package/dist/_exports/result.d.ts.map +1 -0
- package/dist/_exports/result.js +2 -0
- package/dist/_exports/result.js.map +1 -0
- package/dist/_exports/schema.d.ts +2 -0
- package/dist/_exports/schema.d.ts.map +1 -0
- package/dist/_exports/schema.js +2 -0
- package/dist/_exports/schema.js.map +1 -0
- package/dist/_exports/sim.d.ts +199 -0
- package/dist/_exports/sim.d.ts.map +1 -0
- package/dist/_exports/sim.js +8 -0
- package/dist/_exports/sim.js.map +1 -0
- package/dist/_exports/sleep.d.ts +2 -0
- package/dist/_exports/sleep.d.ts.map +1 -0
- package/dist/_exports/sleep.js +2 -0
- package/dist/_exports/sleep.js.map +1 -0
- package/dist/_exports/sntp.d.ts +3 -0
- package/dist/_exports/sntp.d.ts.map +1 -0
- package/dist/_exports/sntp.js +2 -0
- package/dist/_exports/sntp.js.map +1 -0
- package/dist/_exports/spi.d.ts +2 -0
- package/dist/_exports/spi.d.ts.map +1 -0
- package/dist/_exports/spi.js +2 -0
- package/dist/_exports/spi.js.map +1 -0
- package/dist/_exports/stdio.d.ts +2 -0
- package/dist/_exports/stdio.d.ts.map +1 -0
- package/dist/_exports/stdio.js +2 -0
- package/dist/_exports/stdio.js.map +1 -0
- package/dist/_exports/stream.d.ts +2 -0
- package/dist/_exports/stream.d.ts.map +1 -0
- package/dist/_exports/stream.js +2 -0
- package/dist/_exports/stream.js.map +1 -0
- package/dist/_exports/sys.d.ts +2 -0
- package/dist/_exports/sys.d.ts.map +1 -0
- package/dist/_exports/sys.js +2 -0
- package/dist/_exports/sys.js.map +1 -0
- package/dist/_exports/test.d.ts +2 -0
- package/dist/_exports/test.d.ts.map +1 -0
- package/dist/_exports/test.js +2 -0
- package/dist/_exports/test.js.map +1 -0
- package/dist/_exports/uart.d.ts +2 -0
- package/dist/_exports/uart.d.ts.map +1 -0
- package/dist/_exports/uart.js +2 -0
- package/dist/_exports/uart.js.map +1 -0
- package/dist/_exports/wifi.d.ts +3 -0
- package/dist/_exports/wifi.d.ts.map +1 -0
- package/dist/_exports/wifi.js +2 -0
- package/dist/_exports/wifi.js.map +1 -0
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +215 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/cliWrapper.d.ts +3 -0
- package/dist/cli/cliWrapper.d.ts.map +1 -0
- package/dist/cli/cliWrapper.js +27 -0
- package/dist/cli/cliWrapper.js.map +1 -0
- package/dist/cli/commands/build-runtime.d.ts +14 -0
- package/dist/cli/commands/build-runtime.d.ts.map +1 -0
- package/dist/cli/commands/build-runtime.js +122 -0
- package/dist/cli/commands/build-runtime.js.map +1 -0
- package/dist/cli/commands/build.d.ts +31 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +145 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/clean.d.ts +16 -0
- package/dist/cli/commands/clean.d.ts.map +1 -0
- package/dist/cli/commands/clean.js +148 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/console.d.ts +19 -0
- package/dist/cli/commands/console.d.ts.map +1 -0
- package/dist/cli/commands/console.js +32 -0
- package/dist/cli/commands/console.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +63 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +242 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +41 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +170 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/docs.d.ts +7 -0
- package/dist/cli/commands/docs.d.ts.map +1 -0
- package/dist/cli/commands/docs.js +20 -0
- package/dist/cli/commands/docs.js.map +1 -0
- package/dist/cli/commands/env.d.ts +59 -0
- package/dist/cli/commands/env.d.ts.map +1 -0
- package/dist/cli/commands/env.js +257 -0
- package/dist/cli/commands/env.js.map +1 -0
- package/dist/cli/commands/erase.d.ts +18 -0
- package/dist/cli/commands/erase.d.ts.map +1 -0
- package/dist/cli/commands/erase.js +90 -0
- package/dist/cli/commands/erase.js.map +1 -0
- package/dist/cli/commands/flash.d.ts +30 -0
- package/dist/cli/commands/flash.d.ts.map +1 -0
- package/dist/cli/commands/flash.js +243 -0
- package/dist/cli/commands/flash.js.map +1 -0
- package/dist/cli/commands/home.d.ts +7 -0
- package/dist/cli/commands/home.d.ts.map +1 -0
- package/dist/cli/commands/home.js +20 -0
- package/dist/cli/commands/home.js.map +1 -0
- package/dist/cli/commands/ls.d.ts +17 -0
- package/dist/cli/commands/ls.d.ts.map +1 -0
- package/dist/cli/commands/ls.js +62 -0
- package/dist/cli/commands/ls.js.map +1 -0
- package/dist/cli/commands/sim/clean.d.ts +7 -0
- package/dist/cli/commands/sim/clean.d.ts.map +1 -0
- package/dist/cli/commands/sim/clean.js +34 -0
- package/dist/cli/commands/sim/clean.js.map +1 -0
- package/dist/cli/commands/sim/deploy.d.ts +46 -0
- package/dist/cli/commands/sim/deploy.d.ts.map +1 -0
- package/dist/cli/commands/sim/deploy.js +127 -0
- package/dist/cli/commands/sim/deploy.js.map +1 -0
- package/dist/cli/commands/sim/dev.d.ts +40 -0
- package/dist/cli/commands/sim/dev.d.ts.map +1 -0
- package/dist/cli/commands/sim/dev.js +254 -0
- package/dist/cli/commands/sim/dev.js.map +1 -0
- package/dist/cli/commands/sim/env.d.ts +56 -0
- package/dist/cli/commands/sim/env.d.ts.map +1 -0
- package/dist/cli/commands/sim/env.js +204 -0
- package/dist/cli/commands/sim/env.js.map +1 -0
- package/dist/cli/commands/sim/profile.d.ts +67 -0
- package/dist/cli/commands/sim/profile.d.ts.map +1 -0
- package/dist/cli/commands/sim/profile.js +354 -0
- package/dist/cli/commands/sim/profile.js.map +1 -0
- package/dist/cli/commands/sim/repl.d.ts +16 -0
- package/dist/cli/commands/sim/repl.d.ts.map +1 -0
- package/dist/cli/commands/sim/repl.js +119 -0
- package/dist/cli/commands/sim/repl.js.map +1 -0
- package/dist/cli/commands/sim/reset.d.ts +11 -0
- package/dist/cli/commands/sim/reset.d.ts.map +1 -0
- package/dist/cli/commands/sim/reset.js +59 -0
- package/dist/cli/commands/sim/reset.js.map +1 -0
- package/dist/cli/commands/sim/scaffold.d.ts +11 -0
- package/dist/cli/commands/sim/scaffold.d.ts.map +1 -0
- package/dist/cli/commands/sim/scaffold.js +41 -0
- package/dist/cli/commands/sim/scaffold.js.map +1 -0
- package/dist/cli/commands/sim/test.d.ts +49 -0
- package/dist/cli/commands/sim/test.d.ts.map +1 -0
- package/dist/cli/commands/sim/test.js +364 -0
- package/dist/cli/commands/sim/test.js.map +1 -0
- package/dist/cli/commands/sim.d.ts +220 -0
- package/dist/cli/commands/sim.d.ts.map +1 -0
- package/dist/cli/commands/sim.js +69 -0
- package/dist/cli/commands/sim.js.map +1 -0
- package/dist/cli/commands/tail.d.ts +14 -0
- package/dist/cli/commands/tail.d.ts.map +1 -0
- package/dist/cli/commands/tail.js +50 -0
- package/dist/cli/commands/tail.js.map +1 -0
- package/dist/cli/commands/test.d.ts +39 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +432 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/components/DevicePicker.d.ts +9 -0
- package/dist/cli/components/DevicePicker.d.ts.map +1 -0
- package/dist/cli/components/DevicePicker.js +71 -0
- package/dist/cli/components/DevicePicker.js.map +1 -0
- package/dist/cli/components/EnvEditor.d.ts +12 -0
- package/dist/cli/components/EnvEditor.d.ts.map +1 -0
- package/dist/cli/components/EnvEditor.js +201 -0
- package/dist/cli/components/EnvEditor.js.map +1 -0
- package/dist/cli/components/TextInput.d.ts +28 -0
- package/dist/cli/components/TextInput.d.ts.map +1 -0
- package/dist/cli/components/TextInput.js +73 -0
- package/dist/cli/components/TextInput.js.map +1 -0
- package/dist/cli/components/TitledBox.d.ts +27 -0
- package/dist/cli/components/TitledBox.d.ts.map +1 -0
- package/dist/cli/components/TitledBox.js +111 -0
- package/dist/cli/components/TitledBox.js.map +1 -0
- package/dist/cli/hooks/useDevices.d.ts +23 -0
- package/dist/cli/hooks/useDevices.d.ts.map +1 -0
- package/dist/cli/hooks/useDevices.js +19 -0
- package/dist/cli/hooks/useDevices.js.map +1 -0
- package/dist/cli/lib/RenderAndExit.d.ts +6 -0
- package/dist/cli/lib/RenderAndExit.d.ts.map +1 -0
- package/dist/cli/lib/RenderAndExit.js +8 -0
- package/dist/cli/lib/RenderAndExit.js.map +1 -0
- package/dist/cli/lib/Spinner.d.ts +11 -0
- package/dist/cli/lib/Spinner.d.ts.map +1 -0
- package/dist/cli/lib/Spinner.js +22 -0
- package/dist/cli/lib/Spinner.js.map +1 -0
- package/dist/cli/lib/agent.d.ts +38 -0
- package/dist/cli/lib/agent.d.ts.map +1 -0
- package/dist/cli/lib/agent.js +87 -0
- package/dist/cli/lib/agent.js.map +1 -0
- package/dist/cli/lib/boards.d.ts +23 -0
- package/dist/cli/lib/boards.d.ts.map +1 -0
- package/dist/cli/lib/boards.js +67 -0
- package/dist/cli/lib/boards.js.map +1 -0
- package/dist/cli/lib/build.d.ts +47 -0
- package/dist/cli/lib/build.d.ts.map +1 -0
- package/dist/cli/lib/build.js +533 -0
- package/dist/cli/lib/build.js.map +1 -0
- package/dist/cli/lib/connectSim.d.ts +21 -0
- package/dist/cli/lib/connectSim.d.ts.map +1 -0
- package/dist/cli/lib/connectSim.js +20 -0
- package/dist/cli/lib/connectSim.js.map +1 -0
- package/dist/cli/lib/deploy.d.ts +42 -0
- package/dist/cli/lib/deploy.d.ts.map +1 -0
- package/dist/cli/lib/deploy.js +121 -0
- package/dist/cli/lib/deploy.js.map +1 -0
- package/dist/cli/lib/deployProgress.d.ts +8 -0
- package/dist/cli/lib/deployProgress.d.ts.map +1 -0
- package/dist/cli/lib/deployProgress.js +32 -0
- package/dist/cli/lib/deployProgress.js.map +1 -0
- package/dist/cli/lib/dotenv.d.ts +16 -0
- package/dist/cli/lib/dotenv.d.ts.map +1 -0
- package/dist/cli/lib/dotenv.js +33 -0
- package/dist/cli/lib/dotenv.js.map +1 -0
- package/dist/cli/lib/esbuild.d.ts +7 -0
- package/dist/cli/lib/esbuild.d.ts.map +1 -0
- package/dist/cli/lib/esbuild.js +15 -0
- package/dist/cli/lib/esbuild.js.map +1 -0
- package/dist/cli/lib/esptool.d.ts +41 -0
- package/dist/cli/lib/esptool.d.ts.map +1 -0
- package/dist/cli/lib/esptool.js +43 -0
- package/dist/cli/lib/esptool.js.map +1 -0
- package/dist/cli/lib/firmware.d.ts +24 -0
- package/dist/cli/lib/firmware.d.ts.map +1 -0
- package/dist/cli/lib/firmware.js +379 -0
- package/dist/cli/lib/firmware.js.map +1 -0
- package/dist/cli/lib/firmwareCompat.d.ts +19 -0
- package/dist/cli/lib/firmwareCompat.d.ts.map +1 -0
- package/dist/cli/lib/firmwareCompat.js +31 -0
- package/dist/cli/lib/firmwareCompat.js.map +1 -0
- package/dist/cli/lib/heapBaseline.d.ts +15 -0
- package/dist/cli/lib/heapBaseline.d.ts.map +1 -0
- package/dist/cli/lib/heapBaseline.js +39 -0
- package/dist/cli/lib/heapBaseline.js.map +1 -0
- package/dist/cli/lib/ink-table/index.d.ts +88 -0
- package/dist/cli/lib/ink-table/index.d.ts.map +1 -0
- package/dist/cli/lib/ink-table/index.js +217 -0
- package/dist/cli/lib/ink-table/index.js.map +1 -0
- package/dist/cli/lib/loadMikroConfig.d.ts +7 -0
- package/dist/cli/lib/loadMikroConfig.d.ts.map +1 -0
- package/dist/cli/lib/loadMikroConfig.js +26 -0
- package/dist/cli/lib/loadMikroConfig.js.map +1 -0
- package/dist/cli/lib/minify.d.ts +3 -0
- package/dist/cli/lib/minify.d.ts.map +1 -0
- package/dist/cli/lib/minify.js +89 -0
- package/dist/cli/lib/minify.js.map +1 -0
- package/dist/cli/lib/openSim.d.ts +29 -0
- package/dist/cli/lib/openSim.d.ts.map +1 -0
- package/dist/cli/lib/openSim.js +32 -0
- package/dist/cli/lib/openSim.js.map +1 -0
- package/dist/cli/lib/ospawn.d.ts +34 -0
- package/dist/cli/lib/ospawn.d.ts.map +1 -0
- package/dist/cli/lib/ospawn.js +90 -0
- package/dist/cli/lib/ospawn.js.map +1 -0
- package/dist/cli/lib/parseMinifier.d.ts +10 -0
- package/dist/cli/lib/parseMinifier.d.ts.map +1 -0
- package/dist/cli/lib/parseMinifier.js +49 -0
- package/dist/cli/lib/parseMinifier.js.map +1 -0
- package/dist/cli/lib/parseSize.d.ts +6 -0
- package/dist/cli/lib/parseSize.d.ts.map +1 -0
- package/dist/cli/lib/parseSize.js +25 -0
- package/dist/cli/lib/parseSize.js.map +1 -0
- package/dist/cli/lib/projectRoot.d.ts +11 -0
- package/dist/cli/lib/projectRoot.d.ts.map +1 -0
- package/dist/cli/lib/projectRoot.js +28 -0
- package/dist/cli/lib/projectRoot.js.map +1 -0
- package/dist/cli/lib/protocol.d.ts +144 -0
- package/dist/cli/lib/protocol.d.ts.map +1 -0
- package/dist/cli/lib/protocol.js +385 -0
- package/dist/cli/lib/protocol.js.map +1 -0
- package/dist/cli/lib/recover.d.ts +25 -0
- package/dist/cli/lib/recover.d.ts.map +1 -0
- package/dist/cli/lib/recover.js +144 -0
- package/dist/cli/lib/recover.js.map +1 -0
- package/dist/cli/lib/resolveEntry.d.ts +7 -0
- package/dist/cli/lib/resolveEntry.d.ts.map +1 -0
- package/dist/cli/lib/resolveEntry.js +30 -0
- package/dist/cli/lib/resolveEntry.js.map +1 -0
- package/dist/cli/lib/runHooks.d.ts +34 -0
- package/dist/cli/lib/runHooks.d.ts.map +1 -0
- package/dist/cli/lib/runHooks.js +90 -0
- package/dist/cli/lib/runHooks.js.map +1 -0
- package/dist/cli/lib/serial/InkReplConsole.d.ts +27 -0
- package/dist/cli/lib/serial/InkReplConsole.d.ts.map +1 -0
- package/dist/cli/lib/serial/InkReplConsole.js +24 -0
- package/dist/cli/lib/serial/InkReplConsole.js.map +1 -0
- package/dist/cli/lib/serial/InkReplMode.d.ts +38 -0
- package/dist/cli/lib/serial/InkReplMode.d.ts.map +1 -0
- package/dist/cli/lib/serial/InkReplMode.js +90 -0
- package/dist/cli/lib/serial/InkReplMode.js.map +1 -0
- package/dist/cli/lib/serial/ReplConsole.d.ts +21 -0
- package/dist/cli/lib/serial/ReplConsole.d.ts.map +1 -0
- package/dist/cli/lib/serial/ReplConsole.js +249 -0
- package/dist/cli/lib/serial/ReplConsole.js.map +1 -0
- package/dist/cli/lib/serial/devSession.d.ts +53 -0
- package/dist/cli/lib/serial/devSession.d.ts.map +1 -0
- package/dist/cli/lib/serial/devSession.js +160 -0
- package/dist/cli/lib/serial/devSession.js.map +1 -0
- package/dist/cli/lib/serial/dispatchReplCommand.d.ts +25 -0
- package/dist/cli/lib/serial/dispatchReplCommand.d.ts.map +1 -0
- package/dist/cli/lib/serial/dispatchReplCommand.js +27 -0
- package/dist/cli/lib/serial/dispatchReplCommand.js.map +1 -0
- package/dist/cli/lib/serial/openSession.d.ts +28 -0
- package/dist/cli/lib/serial/openSession.d.ts.map +1 -0
- package/dist/cli/lib/serial/openSession.js +48 -0
- package/dist/cli/lib/serial/openSession.js.map +1 -0
- package/dist/cli/lib/serial/replStateMachine.d.ts +169 -0
- package/dist/cli/lib/serial/replStateMachine.d.ts.map +1 -0
- package/dist/cli/lib/serial/replStateMachine.js +650 -0
- package/dist/cli/lib/serial/replStateMachine.js.map +1 -0
- package/dist/cli/lib/serial/runAgentRepl.d.ts +33 -0
- package/dist/cli/lib/serial/runAgentRepl.d.ts.map +1 -0
- package/dist/cli/lib/serial/runAgentRepl.js +80 -0
- package/dist/cli/lib/serial/runAgentRepl.js.map +1 -0
- package/dist/cli/lib/session.d.ts +163 -0
- package/dist/cli/lib/session.d.ts.map +1 -0
- package/dist/cli/lib/session.js +490 -0
- package/dist/cli/lib/session.js.map +1 -0
- package/dist/cli/lib/simConfig.d.ts +15 -0
- package/dist/cli/lib/simConfig.d.ts.map +1 -0
- package/dist/cli/lib/simConfig.js +25 -0
- package/dist/cli/lib/simConfig.js.map +1 -0
- package/dist/cli/lib/simPid.d.ts +30 -0
- package/dist/cli/lib/simPid.d.ts.map +1 -0
- package/dist/cli/lib/simPid.js +112 -0
- package/dist/cli/lib/simPid.js.map +1 -0
- package/dist/cli/lib/simTransport.d.ts +17 -0
- package/dist/cli/lib/simTransport.d.ts.map +1 -0
- package/dist/cli/lib/simTransport.js +80 -0
- package/dist/cli/lib/simTransport.js.map +1 -0
- package/dist/cli/lib/testRunner.d.ts +86 -0
- package/dist/cli/lib/testRunner.d.ts.map +1 -0
- package/dist/cli/lib/testRunner.js +469 -0
- package/dist/cli/lib/testRunner.js.map +1 -0
- package/dist/cli/lib/transport.d.ts +34 -0
- package/dist/cli/lib/transport.d.ts.map +1 -0
- package/dist/cli/lib/transport.js +117 -0
- package/dist/cli/lib/transport.js.map +1 -0
- package/dist/cli/lib/useObservable.d.ts +4 -0
- package/dist/cli/lib/useObservable.d.ts.map +1 -0
- package/dist/cli/lib/useObservable.js +22 -0
- package/dist/cli/lib/useObservable.js.map +1 -0
- package/dist/cli/lib/watcher.d.ts +10 -0
- package/dist/cli/lib/watcher.d.ts.map +1 -0
- package/dist/cli/lib/watcher.js +34 -0
- package/dist/cli/lib/watcher.js.map +1 -0
- package/dist/constants.d.ts +13 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +27 -0
- package/dist/constants.js.map +1 -0
- package/dist/simulator/builtins/ble.d.ts +3 -0
- package/dist/simulator/builtins/ble.d.ts.map +1 -0
- package/dist/simulator/builtins/ble.js +45 -0
- package/dist/simulator/builtins/ble.js.map +1 -0
- package/dist/simulator/builtins/console.d.ts +8 -0
- package/dist/simulator/builtins/console.d.ts.map +1 -0
- package/dist/simulator/builtins/console.js +24 -0
- package/dist/simulator/builtins/console.js.map +1 -0
- package/dist/simulator/builtins/http.d.ts +3 -0
- package/dist/simulator/builtins/http.d.ts.map +1 -0
- package/dist/simulator/builtins/http.js +32 -0
- package/dist/simulator/builtins/http.js.map +1 -0
- package/dist/simulator/builtins/i2c.d.ts +3 -0
- package/dist/simulator/builtins/i2c.d.ts.map +1 -0
- package/dist/simulator/builtins/i2c.js +16 -0
- package/dist/simulator/builtins/i2c.js.map +1 -0
- package/dist/simulator/builtins/index.d.ts +7 -0
- package/dist/simulator/builtins/index.d.ts.map +1 -0
- package/dist/simulator/builtins/index.js +49 -0
- package/dist/simulator/builtins/index.js.map +1 -0
- package/dist/simulator/builtins/kv.d.ts +3 -0
- package/dist/simulator/builtins/kv.d.ts.map +1 -0
- package/dist/simulator/builtins/kv.js +38 -0
- package/dist/simulator/builtins/kv.js.map +1 -0
- package/dist/simulator/builtins/neopixel.d.ts +3 -0
- package/dist/simulator/builtins/neopixel.d.ts.map +1 -0
- package/dist/simulator/builtins/neopixel.js +13 -0
- package/dist/simulator/builtins/neopixel.js.map +1 -0
- package/dist/simulator/builtins/nvs-kv.d.ts +3 -0
- package/dist/simulator/builtins/nvs-kv.d.ts.map +1 -0
- package/dist/simulator/builtins/nvs-kv.js +34 -0
- package/dist/simulator/builtins/nvs-kv.js.map +1 -0
- package/dist/simulator/builtins/pin.d.ts +3 -0
- package/dist/simulator/builtins/pin.d.ts.map +1 -0
- package/dist/simulator/builtins/pin.js +31 -0
- package/dist/simulator/builtins/pin.js.map +1 -0
- package/dist/simulator/builtins/pwm.d.ts +3 -0
- package/dist/simulator/builtins/pwm.d.ts.map +1 -0
- package/dist/simulator/builtins/pwm.js +29 -0
- package/dist/simulator/builtins/pwm.js.map +1 -0
- package/dist/simulator/builtins/sleep.d.ts +3 -0
- package/dist/simulator/builtins/sleep.d.ts.map +1 -0
- package/dist/simulator/builtins/sleep.js +32 -0
- package/dist/simulator/builtins/sleep.js.map +1 -0
- package/dist/simulator/builtins/sntp.d.ts +3 -0
- package/dist/simulator/builtins/sntp.d.ts.map +1 -0
- package/dist/simulator/builtins/sntp.js +12 -0
- package/dist/simulator/builtins/sntp.js.map +1 -0
- package/dist/simulator/builtins/spi.d.ts +3 -0
- package/dist/simulator/builtins/spi.d.ts.map +1 -0
- package/dist/simulator/builtins/spi.js +15 -0
- package/dist/simulator/builtins/spi.js.map +1 -0
- package/dist/simulator/builtins/types.d.ts +24 -0
- package/dist/simulator/builtins/types.d.ts.map +1 -0
- package/dist/simulator/builtins/types.js +2 -0
- package/dist/simulator/builtins/types.js.map +1 -0
- package/dist/simulator/builtins/uart.d.ts +3 -0
- package/dist/simulator/builtins/uart.d.ts.map +1 -0
- package/dist/simulator/builtins/uart.js +20 -0
- package/dist/simulator/builtins/uart.js.map +1 -0
- package/dist/simulator/builtins/wifi.d.ts +3 -0
- package/dist/simulator/builtins/wifi.d.ts.map +1 -0
- package/dist/simulator/builtins/wifi.js +54 -0
- package/dist/simulator/builtins/wifi.js.map +1 -0
- package/dist/simulator/devRunner.d.ts +41 -0
- package/dist/simulator/devRunner.d.ts.map +1 -0
- package/dist/simulator/devRunner.js +140 -0
- package/dist/simulator/devRunner.js.map +1 -0
- package/dist/simulator/loadSimStubs.d.ts +15 -0
- package/dist/simulator/loadSimStubs.d.ts.map +1 -0
- package/dist/simulator/loadSimStubs.js +54 -0
- package/dist/simulator/loadSimStubs.js.map +1 -0
- package/dist/simulator/simProcess.d.ts +11 -0
- package/dist/simulator/simProcess.d.ts.map +1 -0
- package/dist/simulator/simProcess.js +963 -0
- package/dist/simulator/simProcess.js.map +1 -0
- package/dist/simulator/simTemplates.d.ts +6 -0
- package/dist/simulator/simTemplates.d.ts.map +1 -0
- package/dist/simulator/simTemplates.js +21 -0
- package/dist/simulator/simTemplates.js.map +1 -0
- package/package.json +123 -2
- package/runtime.d.ts +95 -0
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simulator subprocess entry point.
|
|
3
|
+
*
|
|
4
|
+
* Speaks the same TLV protocol as the ESP32 firmware over stdin/stdout.
|
|
5
|
+
* Spawned by the CLI via createSimTransport(). Acts as a virtual device:
|
|
6
|
+
* boots QuickJS runtime, handles deploy/config/eval/restart commands.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node simProcess.js --fs-root <path> [--mem-limit <bytes>] [--fs-limit <bytes>] [--sim-dir <path>]
|
|
9
|
+
*/
|
|
10
|
+
// Suppress the ExperimentalWarning that `node:module.stripTypeScriptTypes`
|
|
11
|
+
// emits on first use. The sim deliberately uses it to strip .ts files before
|
|
12
|
+
// handing source to QuickJS; the warning has no actionable content for us
|
|
13
|
+
// and it leaks into the user's terminal via inherited stderr on every run.
|
|
14
|
+
// Filter by message so we don't swallow other experimental warnings.
|
|
15
|
+
{
|
|
16
|
+
const origEmitWarning = process.emitWarning.bind(process);
|
|
17
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
18
|
+
const message = typeof warning === 'string' ? warning : warning?.message;
|
|
19
|
+
if (message && message.includes('stripTypeScriptTypes'))
|
|
20
|
+
return;
|
|
21
|
+
return origEmitWarning(warning, ...rest);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
26
|
+
import * as pathlib from 'node:path';
|
|
27
|
+
import { parseArgs } from 'node:util';
|
|
28
|
+
import { encode as encodeCbor } from 'cbor2';
|
|
29
|
+
import { scan, takeWhile, tap } from 'rxjs';
|
|
30
|
+
import { buildFrame, CMD_COMPLETE, CMD_CONFIG_DELETE, CMD_CONFIG_LIST, CMD_CONFIG_SET, CMD_DEPLOY_ABORT, CMD_DEPLOY_CHECKSUM, CMD_DEPLOY_DONE, CMD_DEPLOY_ERASE, CMD_DEPLOY_KEEP, CMD_DEPLOY_PUT, CMD_DEPLOY_PUT_CHUNK, CMD_DIRECTIVE, CMD_EVAL, CMD_EXIT, CMD_RESTART, ENV_FLAG_SECRET, HEADER_SIZE, MSG_CHECKSUM_RESULT, MSG_COMPLETIONS, MSG_CONFIG_ENTRIES, MSG_DEBUG, MSG_ERR, MSG_ERROR, MSG_EVAL_ERROR, MSG_INFO, MSG_LOG, MSG_MANIFEST_DONE, MSG_OK, MSG_PROMPT, MSG_READY, MSG_RESULT, MSG_TEST, MSG_WARN, parseFrame, } from '../cli/lib/protocol.js';
|
|
31
|
+
import { createDevRunner } from './devRunner.js';
|
|
32
|
+
import { loadSimStubs } from './loadSimStubs.js';
|
|
33
|
+
// ── Args ────────────────────────────────────────────────────────────
|
|
34
|
+
const { values: args } = parseArgs({
|
|
35
|
+
options: {
|
|
36
|
+
'fs-root': { type: 'string' },
|
|
37
|
+
'mem-limit': { type: 'string' },
|
|
38
|
+
'fs-limit': { type: 'string' },
|
|
39
|
+
'fs-read-max': { type: 'string' },
|
|
40
|
+
'sim-dir': { type: 'string' },
|
|
41
|
+
'profile-output': { type: 'string' },
|
|
42
|
+
},
|
|
43
|
+
strict: false,
|
|
44
|
+
});
|
|
45
|
+
const fsRootArg = args['fs-root'];
|
|
46
|
+
if (!fsRootArg || typeof fsRootArg !== 'string') {
|
|
47
|
+
process.stderr.write('simProcess: --fs-root is required\n');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const fsRoot = fsRootArg;
|
|
51
|
+
const memLimitArg = args['mem-limit'];
|
|
52
|
+
const memLimit = typeof memLimitArg === 'string' ? parseInt(memLimitArg, 10) : undefined;
|
|
53
|
+
const fsLimitArg = args['fs-limit'];
|
|
54
|
+
const fsLimit = typeof fsLimitArg === 'string' ? parseInt(fsLimitArg, 10) : undefined;
|
|
55
|
+
const fsReadMaxArg = args['fs-read-max'];
|
|
56
|
+
const fsReadMax = typeof fsReadMaxArg === 'string' ? parseInt(fsReadMaxArg, 10) : undefined;
|
|
57
|
+
const simDirArg = args['sim-dir'];
|
|
58
|
+
const simDir = typeof simDirArg === 'string' ? simDirArg : process.cwd();
|
|
59
|
+
const profileOutputArg = args['profile-output'];
|
|
60
|
+
const profileOutput = typeof profileOutputArg === 'string' ? profileOutputArg : undefined;
|
|
61
|
+
const mikroDir = pathlib.dirname(fsRoot);
|
|
62
|
+
const configPath = pathlib.join(mikroDir, 'nvs.json');
|
|
63
|
+
function readStore(path) {
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return { entries: {} };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function writeStore(path, store) {
|
|
72
|
+
writeFileSync(path, JSON.stringify(store, null, 2) + '\n');
|
|
73
|
+
}
|
|
74
|
+
// ── Filesystem helpers ──────────────────────────────────────────────
|
|
75
|
+
const appDir = pathlib.join(fsRoot, 'app');
|
|
76
|
+
const stagingDir = pathlib.join(fsRoot, '.deploy-tmp');
|
|
77
|
+
const oldDir = pathlib.join(fsRoot, '.deploy-old');
|
|
78
|
+
/** Resolve a path and verify it stays within the given base directory. */
|
|
79
|
+
function resolveSandboxPath(base, userPath) {
|
|
80
|
+
const resolved = pathlib.resolve(base, userPath.replace(/^\/+/, ''));
|
|
81
|
+
const normalizedBase = pathlib.resolve(base);
|
|
82
|
+
if (resolved !== normalizedBase && !resolved.startsWith(normalizedBase + pathlib.sep)) {
|
|
83
|
+
throw new Error(`Path escapes sandbox: ${userPath}`);
|
|
84
|
+
}
|
|
85
|
+
return resolved;
|
|
86
|
+
}
|
|
87
|
+
function ensureDir(dir) {
|
|
88
|
+
mkdirSync(dir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
// ── Frame I/O ───────────────────────────────────────────────────────
|
|
91
|
+
function send(type, payload) {
|
|
92
|
+
const frame = buildFrame(type, payload);
|
|
93
|
+
process.stdout.write(frame);
|
|
94
|
+
}
|
|
95
|
+
function sendOk() {
|
|
96
|
+
send(MSG_OK);
|
|
97
|
+
}
|
|
98
|
+
function sendErr(message) {
|
|
99
|
+
send(MSG_ERR, message);
|
|
100
|
+
}
|
|
101
|
+
// ── HostMessage → TLV mapping ───────────────────────────────────────
|
|
102
|
+
const HOST_MSG_TYPE_MAP = {
|
|
103
|
+
'console:log': MSG_LOG,
|
|
104
|
+
'console:warn': MSG_WARN,
|
|
105
|
+
'console:error': MSG_ERROR,
|
|
106
|
+
'console:info': MSG_INFO,
|
|
107
|
+
'console:debug': MSG_DEBUG,
|
|
108
|
+
};
|
|
109
|
+
const TEST_PREFIX = '__TEST__';
|
|
110
|
+
function forwardConsoleMessage(msg) {
|
|
111
|
+
const msgType = HOST_MSG_TYPE_MAP[msg.type];
|
|
112
|
+
if (msgType === undefined)
|
|
113
|
+
return;
|
|
114
|
+
try {
|
|
115
|
+
const args = JSON.parse(msg.data);
|
|
116
|
+
const text = args.join(' ');
|
|
117
|
+
// Intercept test events emitted via console.log fallback
|
|
118
|
+
if (msg.type === 'console:log' && text.startsWith(TEST_PREFIX)) {
|
|
119
|
+
send(MSG_TEST, text.slice(TEST_PREFIX.length));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
send(msgType, text);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
send(msgType, msg.data);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Drain pending messages from the runtime and forward them as TLV frames.
|
|
129
|
+
* Called synchronously after evalScript to ensure console output appears
|
|
130
|
+
* before MSG_RESULT (same ordering as the firmware). Messages are also
|
|
131
|
+
* emitted to runner.messages$ so the subscriber doesn't re-process them. */
|
|
132
|
+
function forwardPendingMessages() {
|
|
133
|
+
if (!runner)
|
|
134
|
+
return;
|
|
135
|
+
for (const msg of runner.runtime.drainMessages()) {
|
|
136
|
+
forwardHostMessage(msg);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function forwardHostMessage(msg) {
|
|
140
|
+
if (msg.type.startsWith('console:')) {
|
|
141
|
+
forwardConsoleMessage(msg);
|
|
142
|
+
}
|
|
143
|
+
else if (msg.type === 'error:uncaught') {
|
|
144
|
+
try {
|
|
145
|
+
const { name, message, stack } = JSON.parse(msg.data);
|
|
146
|
+
// QuickJS stack traces are just frames (no leading "Name: message"),
|
|
147
|
+
// so synthesize the header to match firmware MSG_EVAL_ERROR formatting.
|
|
148
|
+
const header = name && message ? `${name}: ${message}` : (name ?? message ?? 'Unknown error');
|
|
149
|
+
const text = stack ? `${header}\n${stack}` : header;
|
|
150
|
+
send(MSG_EVAL_ERROR, text);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
send(MSG_EVAL_ERROR, msg.data);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (msg.type === 'oom') {
|
|
157
|
+
// OOM events fire when QuickJS heap headroom crosses a low-water mark
|
|
158
|
+
// either before or after module evaluation. Surface them to the user
|
|
159
|
+
// as warnings — the underlying error (if any) will follow as a normal
|
|
160
|
+
// MSG_EVAL_ERROR via error:uncaught.
|
|
161
|
+
try {
|
|
162
|
+
const { phase, filename, mallocSize, mallocLimit, headroom } = JSON.parse(msg.data);
|
|
163
|
+
const text = `OOM ${phase}: '${filename}' — QuickJS heap ${mallocSize} / ${mallocLimit} bytes ` +
|
|
164
|
+
`(${headroom} bytes free). Lower memReserved or split the module graph.`;
|
|
165
|
+
send(MSG_WARN, text);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
send(MSG_WARN, `OOM event: ${msg.data}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if (msg.type === 'test') {
|
|
172
|
+
send(MSG_TEST, msg.data);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// ── Runtime management ──────────────────────────────────────────────
|
|
176
|
+
let runner = null;
|
|
177
|
+
let stopRunner = null;
|
|
178
|
+
let messagesSub = null;
|
|
179
|
+
let showTime = false;
|
|
180
|
+
/** Resolve `package.json#main` (or `main.js` fallback) to an absolute path
|
|
181
|
+
* inside appDir, or null if no entry can be found. */
|
|
182
|
+
function findMainEntry() {
|
|
183
|
+
const pkgPath = pathlib.join(appDir, 'package.json');
|
|
184
|
+
if (existsSync(pkgPath)) {
|
|
185
|
+
try {
|
|
186
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
187
|
+
if (pkg.main) {
|
|
188
|
+
const mainBase = pkg.main.replace(/\.[tj]s$/, '');
|
|
189
|
+
const jsPath = pathlib.join(appDir, mainBase + '.js');
|
|
190
|
+
const bcPath = pathlib.join(appDir, mainBase + '.bjs');
|
|
191
|
+
if (existsSync(jsPath) || existsSync(bcPath))
|
|
192
|
+
return jsPath;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// ignore invalid package.json
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const mainPath = pathlib.join(appDir, 'main.js');
|
|
200
|
+
const mainBcPath = pathlib.join(appDir, 'main.bjs');
|
|
201
|
+
if (existsSync(mainPath) || existsSync(mainBcPath))
|
|
202
|
+
return mainPath;
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
/** Resolve `package.json#tests` into an array of absolute paths inside
|
|
206
|
+
* appDir. Returns an empty array if no tests array is present — the sole
|
|
207
|
+
* signal for "test mode", matching the firmware supervisor contract. The
|
|
208
|
+
* entries reference `.js` logical names but `.bjs` bytecode variants
|
|
209
|
+
* also count as present — the runtime resolves the two interchangeably,
|
|
210
|
+
* so an entry is valid if either exists on disk. */
|
|
211
|
+
function findTestManifest() {
|
|
212
|
+
const pkgPath = pathlib.join(appDir, 'package.json');
|
|
213
|
+
if (!existsSync(pkgPath))
|
|
214
|
+
return [];
|
|
215
|
+
try {
|
|
216
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
217
|
+
if (!Array.isArray(pkg.tests))
|
|
218
|
+
return [];
|
|
219
|
+
const resolved = [];
|
|
220
|
+
for (const entry of pkg.tests) {
|
|
221
|
+
if (typeof entry !== 'string')
|
|
222
|
+
continue;
|
|
223
|
+
const cleaned = entry.replace(/^\.\//, '');
|
|
224
|
+
const jsPath = pathlib.join(appDir, cleaned);
|
|
225
|
+
const bjsPath = jsPath.replace(/\.js$/, '.bjs');
|
|
226
|
+
if (existsSync(jsPath) || existsSync(bjsPath)) {
|
|
227
|
+
// Pass the .js logical name; MikroRuntime resolves to .bjs when present.
|
|
228
|
+
resolved.push(jsPath);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return resolved;
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function disposeRuntime() {
|
|
238
|
+
messagesSub?.unsubscribe();
|
|
239
|
+
messagesSub = null;
|
|
240
|
+
if (stopRunner) {
|
|
241
|
+
stopRunner();
|
|
242
|
+
stopRunner = null;
|
|
243
|
+
}
|
|
244
|
+
if (runner) {
|
|
245
|
+
runner.runtime.dispose();
|
|
246
|
+
runner = null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Spin up a fresh runtime evaluating `entryScript` (or the app's main,
|
|
250
|
+
* if omitted). Tears down any existing runtime first. By default host
|
|
251
|
+
* messages are forwarded to the CLI as TLV frames via `forwardHostMessage`.
|
|
252
|
+
*
|
|
253
|
+
* Callers that need richer observation — e.g. the supervisor's run_done
|
|
254
|
+
* detection with synthetic-event injection — pass a `composeMessages`
|
|
255
|
+
* operator that transforms the raw `messages$` into the subscribed
|
|
256
|
+
* pipeline. The pipeline is subscribed BEFORE `start()` so messages
|
|
257
|
+
* fired during initial module evaluation (fatal throws at TLA, etc.)
|
|
258
|
+
* are observed.
|
|
259
|
+
*
|
|
260
|
+
* When `composeMessages` is supplied, the returned Promise resolves
|
|
261
|
+
* when that pipeline completes — the caller uses it to detect end-of-
|
|
262
|
+
* file. For the default (no composer) case the returned Promise is
|
|
263
|
+
* null: the forward subscription stays open for the runtime's life. */
|
|
264
|
+
async function bootRuntime(entryScript, composeMessages) {
|
|
265
|
+
disposeRuntime();
|
|
266
|
+
const script = entryScript ?? findMainEntry();
|
|
267
|
+
if (!script) {
|
|
268
|
+
// No app deployed: runtime available for REPL but no main script
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
// Load env from the unified NVS store
|
|
272
|
+
const config = readStore(configPath);
|
|
273
|
+
const env = {};
|
|
274
|
+
for (const [key, entry] of Object.entries(config.entries)) {
|
|
275
|
+
env[key] = entry.value;
|
|
276
|
+
}
|
|
277
|
+
// Load user stub sources (run directly in QuickJS, replacing native modules)
|
|
278
|
+
const stubSources = loadSimStubs(simDir);
|
|
279
|
+
runner = createDevRunner({
|
|
280
|
+
script,
|
|
281
|
+
memLimit,
|
|
282
|
+
fsRoot,
|
|
283
|
+
fsLimit,
|
|
284
|
+
fsReadMax,
|
|
285
|
+
env,
|
|
286
|
+
stubSources,
|
|
287
|
+
profile: profileOutput !== undefined,
|
|
288
|
+
});
|
|
289
|
+
// Override __testEmit: the C-level one is a no-op in the addon (not in protocol mode).
|
|
290
|
+
// We replace it with a function that sends test events directly through the host
|
|
291
|
+
// message system, bypassing console.log (which wraps args in inspect() quotes).
|
|
292
|
+
// Set up sim-specific globals for test events and directives
|
|
293
|
+
runner.runtime.registerModuleSource('native:sim_bridge', `import { send } from 'native:host'\n` +
|
|
294
|
+
`import { memoryUsage, gc } from 'native:sys'\n` +
|
|
295
|
+
`globalThis.__testEmit = (data) => send('test', data)\n` +
|
|
296
|
+
`globalThis.__simMemoryUsage = memoryUsage\n` +
|
|
297
|
+
`globalThis.__simGc = gc\n`);
|
|
298
|
+
runner.runtime.evalModuleContent('native:sim_init', "import 'native:sim_bridge'");
|
|
299
|
+
/* Subscribe BEFORE start() so messages fired during initial module
|
|
300
|
+
* evaluation (fatal throws at TLA, etc.) reach the pipeline. When a
|
|
301
|
+
* composer is supplied, expose its completion as a Promise — that's
|
|
302
|
+
* how the supervisor detects end-of-file without a race. */
|
|
303
|
+
let completion = null;
|
|
304
|
+
if (composeMessages) {
|
|
305
|
+
const pipeline = composeMessages(runner.messages$);
|
|
306
|
+
completion = new Promise((resolve, reject) => {
|
|
307
|
+
messagesSub = pipeline.subscribe({
|
|
308
|
+
complete: () => resolve(),
|
|
309
|
+
error: reject,
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
messagesSub = runner.messages$.subscribe(forwardHostMessage);
|
|
315
|
+
}
|
|
316
|
+
// Register eval queue processing on tick
|
|
317
|
+
runner.onTick(() => {
|
|
318
|
+
processEvalQueue();
|
|
319
|
+
});
|
|
320
|
+
stopRunner = runner.start();
|
|
321
|
+
return completion;
|
|
322
|
+
}
|
|
323
|
+
function classify(msg, fatalSeen) {
|
|
324
|
+
if (msg.type === 'test') {
|
|
325
|
+
try {
|
|
326
|
+
const data = JSON.parse(msg.data);
|
|
327
|
+
if (data.e === 6)
|
|
328
|
+
return 'run_done';
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// malformed test frame, ignore
|
|
332
|
+
}
|
|
333
|
+
return 'none';
|
|
334
|
+
}
|
|
335
|
+
if (msg.type === 'error:uncaught') {
|
|
336
|
+
return fatalSeen ? 'duplicate_fatal' : 'fatal';
|
|
337
|
+
}
|
|
338
|
+
return 'none';
|
|
339
|
+
}
|
|
340
|
+
function reduceSupervisorState(prev, msg) {
|
|
341
|
+
const transition = classify(msg, prev.fatalSeen);
|
|
342
|
+
switch (transition) {
|
|
343
|
+
case 'run_done':
|
|
344
|
+
return { msg, transition, fatalSeen: prev.fatalSeen, done: true };
|
|
345
|
+
case 'fatal':
|
|
346
|
+
return { msg, transition, fatalSeen: true, done: true };
|
|
347
|
+
case 'duplicate_fatal':
|
|
348
|
+
return { msg, transition, fatalSeen: prev.fatalSeen, done: false };
|
|
349
|
+
case 'none':
|
|
350
|
+
return { msg, transition, fatalSeen: prev.fatalSeen, done: false };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const INITIAL_SUPERVISOR_STATE = {
|
|
354
|
+
msg: null,
|
|
355
|
+
transition: 'none',
|
|
356
|
+
fatalSeen: false,
|
|
357
|
+
done: false,
|
|
358
|
+
};
|
|
359
|
+
/** Build the per-file observable pipeline. Forwards most messages via the
|
|
360
|
+
* default forwarder, suppresses duplicate uncaught-rejection events, and
|
|
361
|
+
* injects synthetic e:3+e:6 test frames on a fatal module load so the CLI
|
|
362
|
+
* reducer attributes the failure to the correct file. Completes after the
|
|
363
|
+
* first `done` transition. */
|
|
364
|
+
function fileRunStream(messages$, logicalPath) {
|
|
365
|
+
return messages$.pipe(scan(reduceSupervisorState, INITIAL_SUPERVISOR_STATE), tap((state) => {
|
|
366
|
+
// Suppress the duplicate-fatal's default forward; everything else
|
|
367
|
+
// forwards normally (run_done, first-fatal, regular messages).
|
|
368
|
+
if (state.msg && state.transition !== 'duplicate_fatal') {
|
|
369
|
+
forwardHostMessage(state.msg);
|
|
370
|
+
}
|
|
371
|
+
// On first fatal, synthesize close-out events after forwarding the
|
|
372
|
+
// original eval_error so the CLI sees them in the right order.
|
|
373
|
+
if (state.transition === 'fatal') {
|
|
374
|
+
send(MSG_TEST, JSON.stringify({ e: 3, s: '<load>', t: logicalPath, d: 0, m: 'Evaluation threw' }));
|
|
375
|
+
send(MSG_TEST, JSON.stringify({ e: 6, p: 0, f: 1, k: 0, o: 0, d: 0 }));
|
|
376
|
+
}
|
|
377
|
+
}), takeWhile((state) => !state.done, true));
|
|
378
|
+
}
|
|
379
|
+
async function runTestManifest(tests) {
|
|
380
|
+
for (let i = 0; i < tests.length; i++) {
|
|
381
|
+
const entry = tests[i];
|
|
382
|
+
const logicalPath = '/' + pathlib.relative(fsRoot, entry).split(pathlib.sep).join('/');
|
|
383
|
+
send(MSG_DEBUG, `[supervisor] running ${i + 1}/${tests.length}: ${logicalPath}`);
|
|
384
|
+
/* Hand fileRunStream to bootRuntime as the message composer so the
|
|
385
|
+
* pipeline is subscribed BEFORE evalModule runs — otherwise a file
|
|
386
|
+
* that throws synchronously at TLA would emit error:uncaught before
|
|
387
|
+
* any post-start() subscriber saw it, and we'd hang forever waiting
|
|
388
|
+
* on a run_done that never comes. bootRuntime returns the pipeline's
|
|
389
|
+
* completion as a Promise, which resolves when takeWhile finishes. */
|
|
390
|
+
const completion = await bootRuntime(entry, (messages$) => fileRunStream(messages$, logicalPath));
|
|
391
|
+
if (completion === null)
|
|
392
|
+
continue;
|
|
393
|
+
await completion;
|
|
394
|
+
}
|
|
395
|
+
send(MSG_MANIFEST_DONE);
|
|
396
|
+
}
|
|
397
|
+
// ── Eval queue ──────────────────────────────────────────────────────
|
|
398
|
+
const evalQueue = [];
|
|
399
|
+
function processEvalQueue() {
|
|
400
|
+
if (!runner)
|
|
401
|
+
return;
|
|
402
|
+
while (evalQueue.length > 0) {
|
|
403
|
+
const code = evalQueue.shift();
|
|
404
|
+
const t0 = showTime ? performance.now() : 0;
|
|
405
|
+
try {
|
|
406
|
+
const result = runner.runtime.evalScript(code);
|
|
407
|
+
forwardPendingMessages();
|
|
408
|
+
if (result !== undefined) {
|
|
409
|
+
send(MSG_RESULT, result);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
send(MSG_RESULT); // empty payload = undefined
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
forwardPendingMessages();
|
|
417
|
+
const text = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
418
|
+
send(MSG_EVAL_ERROR, text);
|
|
419
|
+
}
|
|
420
|
+
send(MSG_PROMPT, showTime ? `${(performance.now() - t0).toFixed(1)}ms` : '');
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// ── Deploy handlers ─────────────────────────────────────────────────
|
|
424
|
+
/** In-progress streaming PUT — mirrors the firmware state machine.
|
|
425
|
+
* `path` is the absolute staging-dir path opened by CMD_DEPLOY_PUT;
|
|
426
|
+
* `remaining` is how many body bytes the host still owes us. Cleared
|
|
427
|
+
* when remaining hits zero or when the deploy session is reset. */
|
|
428
|
+
let putState = null;
|
|
429
|
+
function clearPut() {
|
|
430
|
+
putState = null;
|
|
431
|
+
}
|
|
432
|
+
function handleDeployPut(payload) {
|
|
433
|
+
// Payload: u16le name_len | name | u32le total_size
|
|
434
|
+
clearPut();
|
|
435
|
+
if (payload.length < 6) {
|
|
436
|
+
sendErr('put header too short');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const nameLen = payload.readUInt16LE(0);
|
|
440
|
+
if (payload.length < 2 + nameLen + 4) {
|
|
441
|
+
sendErr('put filename too long');
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const name = payload.subarray(2, 2 + nameLen).toString('utf-8');
|
|
445
|
+
const totalSize = payload.readUInt32LE(2 + nameLen);
|
|
446
|
+
const filePath = resolveSandboxPath(stagingDir, name);
|
|
447
|
+
ensureDir(pathlib.dirname(filePath));
|
|
448
|
+
// Truncate to size 0 so subsequent chunks append into a known-empty file.
|
|
449
|
+
writeFileSync(filePath, Buffer.alloc(0));
|
|
450
|
+
if (totalSize > 0) {
|
|
451
|
+
putState = { path: filePath, remaining: totalSize };
|
|
452
|
+
}
|
|
453
|
+
sendOk();
|
|
454
|
+
}
|
|
455
|
+
function handleDeployPutChunk(payload) {
|
|
456
|
+
if (!putState) {
|
|
457
|
+
sendErr('put chunk without active put');
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (payload.length > putState.remaining) {
|
|
461
|
+
clearPut();
|
|
462
|
+
sendErr('put chunk overruns declared size');
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
appendFileSync(putState.path, payload);
|
|
466
|
+
putState.remaining -= payload.length;
|
|
467
|
+
if (putState.remaining === 0) {
|
|
468
|
+
clearPut();
|
|
469
|
+
}
|
|
470
|
+
sendOk();
|
|
471
|
+
}
|
|
472
|
+
function handleDeployKeep(payload) {
|
|
473
|
+
const nameLen = payload.readUInt16LE(0);
|
|
474
|
+
const name = payload.subarray(2, 2 + nameLen).toString('utf-8');
|
|
475
|
+
// Firmware uses FS_BASE + name as source (e.g. /appfs + /app/main.bjs)
|
|
476
|
+
const src = resolveSandboxPath(fsRoot, name);
|
|
477
|
+
const dst = resolveSandboxPath(stagingDir, name);
|
|
478
|
+
ensureDir(pathlib.dirname(dst));
|
|
479
|
+
if (existsSync(src)) {
|
|
480
|
+
copyFileSync(src, dst);
|
|
481
|
+
}
|
|
482
|
+
sendOk();
|
|
483
|
+
}
|
|
484
|
+
function handleDeployChecksum(payload) {
|
|
485
|
+
const nameLen = payload.readUInt16LE(0);
|
|
486
|
+
const name = payload.subarray(2, 2 + nameLen).toString('utf-8');
|
|
487
|
+
const expectedHash = payload.subarray(2 + nameLen, 2 + nameLen + 32);
|
|
488
|
+
// Firmware checks FS_BASE + name (e.g. /appfs + /app/main.bjs)
|
|
489
|
+
const filePath = resolveSandboxPath(fsRoot, name);
|
|
490
|
+
if (!existsSync(filePath)) {
|
|
491
|
+
// File doesn't exist: no match
|
|
492
|
+
send(MSG_CHECKSUM_RESULT, Buffer.from([0x00]));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const actualHash = createHash('sha256').update(readFileSync(filePath)).digest();
|
|
496
|
+
const match = actualHash.equals(expectedHash);
|
|
497
|
+
send(MSG_CHECKSUM_RESULT, Buffer.from([match ? 0x01 : 0x00]));
|
|
498
|
+
}
|
|
499
|
+
function handleDeployErase() {
|
|
500
|
+
rmSync(appDir, { force: true, recursive: true });
|
|
501
|
+
sendOk();
|
|
502
|
+
}
|
|
503
|
+
function handleDeployDone() {
|
|
504
|
+
// Mirror firmware: refuse DONE while a streaming PUT is mid-flight so a
|
|
505
|
+
// CLI bug can't promote a truncated file into the live app dir.
|
|
506
|
+
if (putState) {
|
|
507
|
+
clearPut();
|
|
508
|
+
rmSync(stagingDir, { force: true, recursive: true });
|
|
509
|
+
sendErr('deploy done while put in progress');
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Atomic swap: .deploy-tmp/app → app (matches firmware behavior)
|
|
513
|
+
const stagedApp = pathlib.join(stagingDir, 'app');
|
|
514
|
+
rmSync(oldDir, { force: true, recursive: true });
|
|
515
|
+
if (existsSync(appDir)) {
|
|
516
|
+
renameSync(appDir, oldDir);
|
|
517
|
+
}
|
|
518
|
+
ensureDir(pathlib.dirname(appDir));
|
|
519
|
+
if (existsSync(stagedApp)) {
|
|
520
|
+
renameSync(stagedApp, appDir);
|
|
521
|
+
}
|
|
522
|
+
// Clean up staging dir and old backup
|
|
523
|
+
rmSync(stagingDir, { force: true, recursive: true });
|
|
524
|
+
rmSync(oldDir, { force: true, recursive: true });
|
|
525
|
+
sendOk();
|
|
526
|
+
}
|
|
527
|
+
function handleDeployAbort() {
|
|
528
|
+
clearPut();
|
|
529
|
+
rmSync(stagingDir, { force: true, recursive: true });
|
|
530
|
+
sendOk();
|
|
531
|
+
}
|
|
532
|
+
// ── Config handlers ─────────────────────────────────────────────────
|
|
533
|
+
function handleConfigList() {
|
|
534
|
+
const store = readStore(configPath);
|
|
535
|
+
const entries = Object.entries(store.entries).map(([key, entry]) => ({
|
|
536
|
+
key,
|
|
537
|
+
value: entry.secret ? '' : entry.value,
|
|
538
|
+
secret: entry.secret,
|
|
539
|
+
}));
|
|
540
|
+
const cbor = Buffer.from(encodeCbor(entries));
|
|
541
|
+
send(MSG_CONFIG_ENTRIES, cbor);
|
|
542
|
+
}
|
|
543
|
+
function handleConfigSet(payload) {
|
|
544
|
+
const flags = payload[0];
|
|
545
|
+
const secret = (flags & ENV_FLAG_SECRET) !== 0;
|
|
546
|
+
const keyLen = payload.readUInt16LE(1);
|
|
547
|
+
const key = payload.subarray(3, 3 + keyLen).toString('utf-8');
|
|
548
|
+
const valLen = payload.readUInt16LE(3 + keyLen);
|
|
549
|
+
const value = payload.subarray(5 + keyLen, 5 + keyLen + valLen).toString('utf-8');
|
|
550
|
+
const store = readStore(configPath);
|
|
551
|
+
const prev = store.entries[key];
|
|
552
|
+
const changed = !prev || prev.value !== value || prev.secret !== secret;
|
|
553
|
+
if (changed) {
|
|
554
|
+
store.entries[key] = { value, secret };
|
|
555
|
+
writeStore(configPath, store);
|
|
556
|
+
}
|
|
557
|
+
send(MSG_OK, Buffer.from([changed ? 1 : 0]));
|
|
558
|
+
}
|
|
559
|
+
function handleConfigDelete(payload) {
|
|
560
|
+
const keyLen = payload.readUInt16LE(0);
|
|
561
|
+
const key = payload.subarray(2, 2 + keyLen).toString('utf-8');
|
|
562
|
+
const store = readStore(configPath);
|
|
563
|
+
const existed = key in store.entries;
|
|
564
|
+
if (existed) {
|
|
565
|
+
const { [key]: _, ...rest } = store.entries;
|
|
566
|
+
store.entries = rest;
|
|
567
|
+
writeStore(configPath, store);
|
|
568
|
+
}
|
|
569
|
+
send(MSG_OK, Buffer.from([existed ? 1 : 0]));
|
|
570
|
+
}
|
|
571
|
+
// ── REPL handlers ───────────────────────────────────────────────────
|
|
572
|
+
function handleEval(payload) {
|
|
573
|
+
const code = payload.toString('utf-8');
|
|
574
|
+
if (runner) {
|
|
575
|
+
evalQueue.push(code);
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
send(MSG_EVAL_ERROR, 'No app deployed. Deploy an app first.');
|
|
579
|
+
send(MSG_PROMPT);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function handleComplete(payload) {
|
|
583
|
+
const partial = payload.toString('utf-8');
|
|
584
|
+
// Return empty completions for now
|
|
585
|
+
const cbor = Buffer.from(encodeCbor({ prefix: partial, items: [] }));
|
|
586
|
+
send(MSG_COMPLETIONS, cbor);
|
|
587
|
+
}
|
|
588
|
+
function handleDirective(payload) {
|
|
589
|
+
const line = payload.toString('utf-8');
|
|
590
|
+
const [cmd, ...rest] = line.split(' ');
|
|
591
|
+
const arg = rest.join(' ').trim();
|
|
592
|
+
switch (cmd) {
|
|
593
|
+
case '/help':
|
|
594
|
+
case 'help':
|
|
595
|
+
send(MSG_INFO, '/help Show this help\n' +
|
|
596
|
+
'/mem Show heap memory usage\n' +
|
|
597
|
+
'/gc Run garbage collector\n' +
|
|
598
|
+
'/time Toggle timing display\n' +
|
|
599
|
+
'/pause Pause app (suspend timers/callbacks)\n' +
|
|
600
|
+
'/resume Resume app\n' +
|
|
601
|
+
'/ls [DIR] List directory contents\n' +
|
|
602
|
+
'/cat FILE Print file contents\n' +
|
|
603
|
+
'/rm FILE Remove a file\n' +
|
|
604
|
+
'/info Show runtime info\n' +
|
|
605
|
+
'/df Show filesystem free/total space\n' +
|
|
606
|
+
'/du [PATH] Show disk usage for path\n' +
|
|
607
|
+
'/exit Exit REPL\n');
|
|
608
|
+
break;
|
|
609
|
+
case '/mem':
|
|
610
|
+
case 'mem': {
|
|
611
|
+
if (!runner) {
|
|
612
|
+
send(MSG_INFO, 'No runtime active');
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
try {
|
|
616
|
+
const json = runner.runtime.evalScript(`JSON.stringify(__simMemoryUsage())`);
|
|
617
|
+
if (json) {
|
|
618
|
+
const m = JSON.parse(json);
|
|
619
|
+
send(MSG_INFO, `Heap used: ${m.heapUsed} / ${m.heapTotal} bytes ` +
|
|
620
|
+
`(${m.heapTotal > 0 ? ((m.heapUsed / m.heapTotal) * 100).toFixed(1) : 0}%)`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
send(MSG_INFO, 'Memory info unavailable');
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
case '/gc':
|
|
629
|
+
case 'gc': {
|
|
630
|
+
if (!runner)
|
|
631
|
+
break;
|
|
632
|
+
try {
|
|
633
|
+
const beforeJson = runner.runtime.evalScript(`JSON.stringify(__simMemoryUsage())`);
|
|
634
|
+
runner.runtime.evalScript(`__simGc()`);
|
|
635
|
+
const afterJson = runner.runtime.evalScript(`JSON.stringify(__simMemoryUsage())`);
|
|
636
|
+
if (beforeJson && afterJson) {
|
|
637
|
+
const before = JSON.parse(beforeJson).heapUsed;
|
|
638
|
+
const after = JSON.parse(afterJson).heapUsed;
|
|
639
|
+
send(MSG_INFO, `GC collected ${before - after} bytes (${before} -> ${after})`);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
send(MSG_INFO, 'GC done');
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
send(MSG_INFO, 'GC done');
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
case '/pause':
|
|
651
|
+
if (runner?.paused) {
|
|
652
|
+
send(MSG_INFO, 'App is already paused');
|
|
653
|
+
}
|
|
654
|
+
else if (runner) {
|
|
655
|
+
runner.paused = true;
|
|
656
|
+
send(MSG_INFO, 'App paused (timers, callbacks suspended). Use /resume to continue.');
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
case '/resume':
|
|
660
|
+
if (runner && !runner.paused) {
|
|
661
|
+
send(MSG_INFO, 'App is not paused');
|
|
662
|
+
}
|
|
663
|
+
else if (runner) {
|
|
664
|
+
runner.paused = false;
|
|
665
|
+
send(MSG_INFO, 'App resumed');
|
|
666
|
+
}
|
|
667
|
+
break;
|
|
668
|
+
case '/time':
|
|
669
|
+
showTime = !showTime;
|
|
670
|
+
send(MSG_INFO, `Timing ${showTime ? 'on' : 'off'}`);
|
|
671
|
+
break;
|
|
672
|
+
case '/info': {
|
|
673
|
+
const lines = ['Chip: simulator'];
|
|
674
|
+
if (runner) {
|
|
675
|
+
try {
|
|
676
|
+
const json = runner.runtime.evalScript(`JSON.stringify(__simMemoryUsage())`);
|
|
677
|
+
if (json) {
|
|
678
|
+
const m = JSON.parse(json);
|
|
679
|
+
lines.push(`JS heap: ${m.heapUsed} / ${m.heapTotal} bytes ` +
|
|
680
|
+
`(${m.heapTotal > 0 ? ((m.heapUsed / m.heapTotal) * 100).toFixed(1) : 0}% used)`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
// skip
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
const total = fsDiskUsage(appDir);
|
|
689
|
+
lines.push(`Filesystem: ${total} bytes used`);
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
692
|
+
// skip
|
|
693
|
+
}
|
|
694
|
+
send(MSG_INFO, lines.join('\n'));
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case '/df': {
|
|
698
|
+
try {
|
|
699
|
+
const used = fsDiskUsage(appDir);
|
|
700
|
+
const limit = fsLimit ?? 1024 * 1024;
|
|
701
|
+
const free = limit > used ? limit - used : 0;
|
|
702
|
+
send(MSG_INFO, `Filesystem: ${used} / ${limit} bytes used ` +
|
|
703
|
+
`(${limit > 0 ? ((used / limit) * 100).toFixed(1) : 0}%), ${free} bytes free`);
|
|
704
|
+
}
|
|
705
|
+
catch {
|
|
706
|
+
send(MSG_INFO, 'Filesystem info unavailable');
|
|
707
|
+
}
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
case '/du': {
|
|
711
|
+
const target = arg || '/';
|
|
712
|
+
const resolved = resolveSandboxPath(fsRoot, target);
|
|
713
|
+
try {
|
|
714
|
+
const st = statSync(resolved);
|
|
715
|
+
if (!st.isDirectory()) {
|
|
716
|
+
send(MSG_INFO, ` ${st.size}\t${target}`);
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
let total = 0;
|
|
720
|
+
const lines = [];
|
|
721
|
+
for (const entry of readdirSync(resolved, { withFileTypes: true })) {
|
|
722
|
+
if (!entry.isFile())
|
|
723
|
+
continue;
|
|
724
|
+
const size = statSync(pathlib.join(resolved, entry.name)).size;
|
|
725
|
+
const trailing = target.endsWith('/') ? '' : '/';
|
|
726
|
+
lines.push(` ${size}\t${target}${trailing}${entry.name}`);
|
|
727
|
+
total += size;
|
|
728
|
+
}
|
|
729
|
+
lines.push(` ${total}\ttotal`);
|
|
730
|
+
send(MSG_INFO, lines.join('\n'));
|
|
731
|
+
}
|
|
732
|
+
catch {
|
|
733
|
+
send(MSG_INFO, `Cannot stat '${target}'`);
|
|
734
|
+
}
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
case '/ls': {
|
|
738
|
+
const target = arg || '/';
|
|
739
|
+
const resolved = resolveSandboxPath(fsRoot, target);
|
|
740
|
+
try {
|
|
741
|
+
const entries = readdirSync(resolved, { withFileTypes: true });
|
|
742
|
+
const lines = [];
|
|
743
|
+
for (const entry of entries) {
|
|
744
|
+
const size = entry.isFile() ? statSync(pathlib.join(resolved, entry.name)).size : 0;
|
|
745
|
+
const suffix = entry.isDirectory() ? '/' : '';
|
|
746
|
+
lines.push(entry.isFile() ? ` ${size}\t${entry.name}` : ` -\t${entry.name}${suffix}`);
|
|
747
|
+
}
|
|
748
|
+
if (lines.length === 0)
|
|
749
|
+
lines.push(' (empty)');
|
|
750
|
+
send(MSG_INFO, lines.join('\n'));
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
send(MSG_INFO, `Cannot open '${target}'`);
|
|
754
|
+
}
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
case '/cat': {
|
|
758
|
+
if (!arg) {
|
|
759
|
+
send(MSG_INFO, 'Usage: /cat FILE');
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
const resolved = resolveSandboxPath(fsRoot, arg);
|
|
763
|
+
try {
|
|
764
|
+
const content = readFileSync(resolved, 'utf-8');
|
|
765
|
+
send(MSG_INFO, content);
|
|
766
|
+
}
|
|
767
|
+
catch {
|
|
768
|
+
send(MSG_INFO, `Cannot open '${arg}'`);
|
|
769
|
+
}
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
case '/rm': {
|
|
773
|
+
if (!arg) {
|
|
774
|
+
send(MSG_INFO, 'Usage: /rm FILE');
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
const resolved = resolveSandboxPath(fsRoot, arg);
|
|
778
|
+
try {
|
|
779
|
+
rmSync(resolved);
|
|
780
|
+
send(MSG_INFO, `Removed '${arg}'`);
|
|
781
|
+
}
|
|
782
|
+
catch {
|
|
783
|
+
send(MSG_INFO, `Cannot remove '${arg}'`);
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
default:
|
|
788
|
+
send(MSG_INFO, `Unknown directive: ${line}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
function fsDiskUsage(dir) {
|
|
792
|
+
if (!existsSync(dir))
|
|
793
|
+
return 0;
|
|
794
|
+
let total = 0;
|
|
795
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
796
|
+
const full = pathlib.join(dir, entry.name);
|
|
797
|
+
if (entry.isDirectory()) {
|
|
798
|
+
total += fsDiskUsage(full);
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
total += statSync(full).size;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return total;
|
|
805
|
+
}
|
|
806
|
+
// ── Frame dispatcher ────────────────────────────────────────────────
|
|
807
|
+
async function handleFrame(frame) {
|
|
808
|
+
const { type, payload } = frame;
|
|
809
|
+
// Deploy commands (0x20-0x27)
|
|
810
|
+
if (type >= 0x20 && type <= 0x27) {
|
|
811
|
+
switch (type) {
|
|
812
|
+
case CMD_DEPLOY_PUT:
|
|
813
|
+
handleDeployPut(payload);
|
|
814
|
+
break;
|
|
815
|
+
case CMD_DEPLOY_PUT_CHUNK:
|
|
816
|
+
handleDeployPutChunk(payload);
|
|
817
|
+
break;
|
|
818
|
+
case CMD_DEPLOY_DONE:
|
|
819
|
+
handleDeployDone();
|
|
820
|
+
break;
|
|
821
|
+
case CMD_DEPLOY_ABORT:
|
|
822
|
+
handleDeployAbort();
|
|
823
|
+
break;
|
|
824
|
+
case CMD_DEPLOY_ERASE:
|
|
825
|
+
handleDeployErase();
|
|
826
|
+
break;
|
|
827
|
+
case CMD_DEPLOY_KEEP:
|
|
828
|
+
handleDeployKeep(payload);
|
|
829
|
+
break;
|
|
830
|
+
case CMD_DEPLOY_CHECKSUM:
|
|
831
|
+
handleDeployChecksum(payload);
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
// Config commands (0x40-0x42)
|
|
837
|
+
if (type >= 0x40 && type <= 0x42) {
|
|
838
|
+
switch (type) {
|
|
839
|
+
case CMD_CONFIG_LIST:
|
|
840
|
+
handleConfigList();
|
|
841
|
+
break;
|
|
842
|
+
case CMD_CONFIG_SET:
|
|
843
|
+
handleConfigSet(payload);
|
|
844
|
+
break;
|
|
845
|
+
case CMD_CONFIG_DELETE:
|
|
846
|
+
handleConfigDelete(payload);
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
// REPL commands (0x10-0x14)
|
|
852
|
+
switch (type) {
|
|
853
|
+
case CMD_EVAL:
|
|
854
|
+
handleEval(payload);
|
|
855
|
+
break;
|
|
856
|
+
case CMD_COMPLETE:
|
|
857
|
+
handleComplete(payload);
|
|
858
|
+
break;
|
|
859
|
+
case CMD_DIRECTIVE:
|
|
860
|
+
handleDirective(payload);
|
|
861
|
+
break;
|
|
862
|
+
case CMD_EXIT:
|
|
863
|
+
process.exit(0);
|
|
864
|
+
break;
|
|
865
|
+
case CMD_RESTART:
|
|
866
|
+
sendReady();
|
|
867
|
+
await bootOrRunManifest();
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
/** Post-boot orchestration: if the deployed app carries a `tests` array
|
|
872
|
+
* in package.json, run the manifest loop (fresh runtime per file, emit
|
|
873
|
+
* MSG_MANIFEST_DONE at the end, then fall through to an idle runtime so
|
|
874
|
+
* the CLI can still issue REPL/deploy commands). Otherwise, boot the
|
|
875
|
+
* main app normally. */
|
|
876
|
+
async function bootOrRunManifest() {
|
|
877
|
+
const tests = findTestManifest();
|
|
878
|
+
if (tests.length > 0) {
|
|
879
|
+
try {
|
|
880
|
+
await runTestManifest(tests);
|
|
881
|
+
}
|
|
882
|
+
catch (err) {
|
|
883
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
884
|
+
process.stderr.write(`simProcess: manifest run failed: ${msg}\n`);
|
|
885
|
+
}
|
|
886
|
+
// Idle runtime for post-test REPL / deploy traffic.
|
|
887
|
+
await bootRuntime();
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
await bootRuntime();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// ── Ready message ───────────────────────────────────────────────────
|
|
894
|
+
function sendReady() {
|
|
895
|
+
const cbor = Buffer.from(encodeCbor({ chip: 'simulator', id: null, v: '0.0.0' }));
|
|
896
|
+
send(MSG_READY, cbor);
|
|
897
|
+
}
|
|
898
|
+
// ── Main ────────────────────────────────────────────────────────────
|
|
899
|
+
async function main() {
|
|
900
|
+
ensureDir(fsRoot);
|
|
901
|
+
// Boot runtime (runs deployed app, or iterates through a tests manifest
|
|
902
|
+
// if one is present — matching the firmware supervisor flow).
|
|
903
|
+
await bootOrRunManifest();
|
|
904
|
+
// Profile mode: runtime has booted and the full static import graph has
|
|
905
|
+
// been loaded. Write the profile JSON and exit. We don't start the TLV
|
|
906
|
+
// listener because profile runs are one-shot.
|
|
907
|
+
if (profileOutput !== undefined) {
|
|
908
|
+
if (!runner) {
|
|
909
|
+
process.stderr.write('simProcess: profile requested but no app deployed\n');
|
|
910
|
+
process.exit(1);
|
|
911
|
+
}
|
|
912
|
+
const entries = runner.runtime.getProfile();
|
|
913
|
+
const baseline = runner.runtime.getProfileBaseline();
|
|
914
|
+
writeFileSync(profileOutput, JSON.stringify({ entries, baseline }, null, 2) + '\n');
|
|
915
|
+
process.exit(0);
|
|
916
|
+
}
|
|
917
|
+
// Send ready to CLI
|
|
918
|
+
sendReady();
|
|
919
|
+
// Set up stdin frame parser (raw TLV, no type filtering like FrameParser does).
|
|
920
|
+
// Frames are queued and processed sequentially to avoid races when async
|
|
921
|
+
// handlers (CMD_RESTART) overlap with subsequent frames.
|
|
922
|
+
let buf = Buffer.alloc(0);
|
|
923
|
+
const frameQueue = [];
|
|
924
|
+
let processing = false;
|
|
925
|
+
async function drainFrameQueue() {
|
|
926
|
+
if (processing)
|
|
927
|
+
return;
|
|
928
|
+
processing = true;
|
|
929
|
+
while (frameQueue.length > 0) {
|
|
930
|
+
const frame = frameQueue.shift();
|
|
931
|
+
try {
|
|
932
|
+
await handleFrame(frame);
|
|
933
|
+
}
|
|
934
|
+
catch (err) {
|
|
935
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
936
|
+
sendErr(msg);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
processing = false;
|
|
940
|
+
}
|
|
941
|
+
process.stdin.on('data', (chunk) => {
|
|
942
|
+
buf = buf.length > 0 ? Buffer.concat([buf, chunk]) : Buffer.from(chunk);
|
|
943
|
+
let offset = 0;
|
|
944
|
+
while (offset + HEADER_SIZE <= buf.length) {
|
|
945
|
+
const result = parseFrame(buf, offset);
|
|
946
|
+
if (!result)
|
|
947
|
+
break;
|
|
948
|
+
offset += result.bytesConsumed;
|
|
949
|
+
frameQueue.push(result.frame);
|
|
950
|
+
}
|
|
951
|
+
// Keep unconsumed bytes
|
|
952
|
+
buf = offset > 0 ? Buffer.from(buf.subarray(offset)) : buf;
|
|
953
|
+
void drainFrameQueue();
|
|
954
|
+
});
|
|
955
|
+
process.stdin.on('end', () => {
|
|
956
|
+
process.exit(0);
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
main().catch((err) => {
|
|
960
|
+
process.stderr.write(`simProcess fatal: ${err}\n`);
|
|
961
|
+
process.exit(1);
|
|
962
|
+
});
|
|
963
|
+
//# sourceMappingURL=simProcess.js.map
|