OverviewFilesCommitsIssuesDocsReleasesPeers

cthuloid commits / ae75a02

android: start the controller on a phone (0.1.0 never did)
On the phone bare-kit passes exactly the args the shell gives, so Bare.argv is [storage] (BareKitModule.cc: bare_worklet_start(..., args.size(), args)); 0.1.0 required argv.length > 1, true only on a desktop, so the worklet loaded and then sat silent -- the app spun on "loading" for ever. Found on an SM-S906U (Android 16) over adb: the worklet thread initialised bare-dns, then produced no output at all. storageFromArgv(): 1 arg = phone, >= 3 = desktop, else nothing -- and the worklet now reports a fatal instead of staying silent; the shell shows an error if the worklet says nothing within 20 s. Verified on the phone: ready at http://127.0.0.1:<port>/ ~200 ms after launch, page renders. (Reaching studiolight from the phone fails at the network: the "prsm space" SSID isolates wireless clients -- the phone reaches wired tabertron/stortron but ARP to the ESP32 fails.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ae75a02bd90b4b399afa0d8c61de665c82219bdb
by plankatron <fleeky@gmail.com> · 2026-10-02T16:24:33.000Z

Changed files (4) +50 -4

android/app/App.jsx +9 -2
……@@ -18,11 +18,17 @@ export default function App () {
1818 const boot = useCallback(async () => {
1919 setError(null)
2020 const host = await startWorklet() // memoised: a retry reuses the running worklet
21 let answered = false
2122 host.onMessage((m) => {
2223 if (m.t === 'log') setStage(m.msg)
23 else if (m.t === 'ready') setUrl(m.url)
24 else if (m.t === 'fatal') setError(m.error)
24 else if (m.t === 'ready') { answered = true; setUrl(m.url) }
25 else if (m.t === 'fatal') { answered = true; setError(m.error) }
2526 })
27 // A worklet has no error or exit event: if it never speaks, say so rather
28 // than spin for ever (0.1.0 did exactly that).
29 setTimeout(() => {
30 if (!answered) setError('The light controller did not answer within 20 s.')
31 }, 20000)
2632 }, [])
2733
2834 useEffect(() => { boot().catch((e) => setError(e.message)) }, [boot])
……@@ -31,6 +37,7 @@ export default function App () {
3137 return (
3238 <Center>
3339 <Text style={s.err}>{error}</Text>
40 <Text style={s.dim}>{stage}</Text>
3441 <Pressable onPress={() => boot().catch((e) => setError(e.message))}><Text style={s.retry}>try again</Text></Pressable>
3542 </Center>
3643 )
android/lib/argv.js +13 -0
……@@ -0,0 +1,13 @@
1// Where the worklet's storage comes from.
2//
3// On the phone bare-kit passes EXACTLY the args the shell gives, so Bare.argv is
4// [storage] (react-native-bare-kit shared/BareKitModule.cc: bare_worklet_start(
5// ..., args.size(), args)). On a desktop `bare lib/worklet.js <storage>` gives
6// [runtime, script, storage]. 0.1.0 required argv.length > 1 -- true only on a
7// desktop -- so on the phone the worklet loaded and then never started.
8export function storageFromArgv (argv) {
9 if (!Array.isArray(argv)) return null
10 if (argv.length === 1) return argv[0] || null // phone
11 if (argv.length >= 3) return argv[argv.length - 1] // desktop: runtime, script, storage
12 return null // desktop, no storage given
13}
android/lib/worklet.js +6 -2
……@@ -18,6 +18,7 @@ import fs from 'bare-fs'
1818import path from 'bare-path'
1919import { createApp } from 'cthuloid-web/lib/app.js'
2020import assets from 'cthuloid-web/lib/assets.generated.js'
21import { storageFromArgv } from './argv.js'
2122
2223const IPC = typeof BareKit !== 'undefined' ? BareKit.IPC : null
2324
……@@ -62,7 +63,10 @@ export async function start (storage) {
6263 return { app, url }
6364}
6465
65if (typeof Bare !== 'undefined' && Bare.argv.length > 1 && !globalThis.CTHULOID_NO_AUTOSTART) {
66 const storage = Bare.argv[Bare.argv.length - 1]
66const storage = typeof Bare !== 'undefined' ? storageFromArgv(Bare.argv) : null
67if (storage && !globalThis.CTHULOID_NO_AUTOSTART) {
6768 start(storage).catch((err) => send({ t: 'fatal', error: (err && err.stack) || String(err) }))
69} else if (!globalThis.CTHULOID_NO_AUTOSTART) {
70 // Never silent again: say why nothing started (0.1.0 hung with no output at all).
71 send({ t: 'fatal', error: 'no storage directory in argv: ' + JSON.stringify(typeof Bare !== 'undefined' ? Bare.argv : null) })
6872}
android/test/argv.test.js +22 -0
……@@ -0,0 +1,22 @@
1// Where the worklet finds its storage. On the PHONE, bare-kit passes exactly the
2// args the shell gives -- Bare.argv is [storage] (react-native-bare-kit
3// shared/BareKitModule.cc: bare_worklet_start(..., args.size(), args)). On a
4// desktop, `bare lib/worklet.js <storage>` puts the runtime and script first.
5// 0.1.0 required argv.length > 1 and so never started on a phone.
6import test from 'brittle'
7import { storageFromArgv } from '../lib/argv.js'
8
9const SELF = '/x/android/lib/worklet.js'
10
11test('phone: the only argument is the storage dir', (t) => {
12 t.is(storageFromArgv(['/data/user/0/org.circusautomatic.cthuloid/files'], SELF), '/data/user/0/org.circusautomatic.cthuloid/files')
13})
14
15test('desktop: runtime, script, storage', (t) => {
16 t.is(storageFromArgv(['bare', SELF, '/tmp/s'], SELF), '/tmp/s')
17})
18
19test('desktop with no storage given: do not start', (t) => {
20 t.is(storageFromArgv(['bare', SELF], SELF), null)
21 t.is(storageFromArgv([], SELF), null)
22})