OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop commits / f89027f

remove browser tab worker execution path
f89027fe21dd8abd25bf5a5bd5bf1305f8d4f40f
by bigdestiny2 <33146784+bigdestiny2@users.noreply.github.com> · 2026-07-22T23:36:22.000Z

Changed files (4) +15 -46

backend/tab-runtime.js +6 -36
……@@ -19,10 +19,9 @@
1919 * pear-request client (everything else streams from the worker)
2020 * - a bare-ws server bridges each tab's WebSocket <-> the worker's duplex pipe
2121 *
22 * Worker source is pluggable:
23 * - 'demo' -> an in-process pear-request router (rock-solid, no network)
24 * - pear://|file:// -> a real headless worker via pear-run (the production path,
25 * the in-tab sibling of CMD_LAUNCH_PEAR_LINK's window spawn)
22 * The only worker is the in-process demo router. This runtime is a local UI
23 * proof surface, never an application loader: native app code must arrive in a
24 * verified package outside the browser process.
2625 */
2726const http = require('bare-http1')
2827const ws = require('bare-ws')
……@@ -74,7 +73,6 @@ function listenWsServer (port, onSocket) {
7473
7574class TabRuntime {
7675 constructor (opts = {}) {
77 this.pearRun = opts.pearRun || null // (link) => duplex worker pipe
7876 this.tabs = new Map() // tabId -> { source }
7977 this._seq = 0
8078 this.httpPort = 0
……@@ -108,29 +106,14 @@ class TabRuntime {
108106 // A stable demo tab so the headless run-in-tab path is reachable without UI
109107 // (GET /tab/demo) — the in-process router; the "Headless Demo" card opens it.
110108 this.tabs.set('demo', { source: 'demo' })
111 // Optional: a stable tab that exercises the real pear-run worker path
112 // (/tab/demo-worker). Enabled by PEAR_TAB_DEMO_WORKER, or by the local demo
113 // app at ~/Desktop/pear-request-demo if present (statSync-guarded, so it's a
114 // no-op on machines without it). This is the same app the "Headless Demo
115 // (worker)" card launches.
116 const env = (globalThis.Bare && Bare.env) || (globalThis.process && process.env) || {}
117 let demoWorker = env.PEAR_TAB_DEMO_WORKER || null
118 if (!demoWorker && env.HOME) {
119 const p = env.HOME + '/Desktop/pear-request-demo'
120 try { require('bare-fs').statSync(p); demoWorker = 'file://' + p } catch {}
121 }
122 if (demoWorker) {
123 this.tabs.set('demo-worker', { source: demoWorker })
124 console.log('[tab-runtime] demo-worker (pear-run) -> ' + demoWorker)
125 }
126109 console.log(`[tab-runtime] http :${this.httpPort} ws :${this.wsPort} (demo: /tab/demo)`)
127110 return { httpPort: this.httpPort, wsPort: this.wsPort }
128111 }
129112
130113 // Register a tab and return the wrapper URL the UI should load in an iframe.
131 open (link) {
114 open (source = 'demo') {
115 if (source !== 'demo') throw new Error('Only the in-process demo is available in a browser tab')
132116 const tabId = 'tab' + (++this._seq)
133 const source = (!link || link === 'demo') ? 'demo' : String(link)
134117 const tab = { source, contextToken: this._newContextToken() }
135118 this.tabs.set(tabId, tab)
136119 return {
……@@ -188,7 +171,7 @@ class TabRuntime {
188171 const tab = this.tabs.get(tabId)
189172 if (!tab) { try { sock.end() } catch {} ; return }
190173 try {
191 worker = tab.source === 'demo' ? this._spawnInProc(sock) : this._spawnWorker(tab.source, sock)
174 worker = this._spawnInProc(sock)
192175 } catch (err) {
193176 console.error('[tab-runtime] spawn failed:', err && err.message)
194177 try { sock.end() } catch {}
……@@ -216,19 +199,6 @@ class TabRuntime {
216199 }
217200 }
218201
219 // Production: spawn the app as a real headless worker; bridge its pipe <-> WS.
220 _spawnWorker (link, sock) {
221 if (!this.pearRun) throw new Error('pear-run not available')
222 const pipe = this.pearRun(link)
223 pipe.on('data', (d) => { try { sock.write(d) } catch {} }) // worker -> browser
224 try { pipe.on('error', (e) => console.error('[tab-runtime] worker error:', e && e.message)) } catch {}
225 try { pipe.on('crash', (i) => console.error('[tab-runtime] worker crash:', i)) } catch {}
226 return {
227 toWorker: (data) => { try { pipe.write(data) } catch (e) { console.error('[tab-runtime] write:', e && e.message) } },
228 close: () => { try { pipe.end ? pipe.end() : pipe.destroy && pipe.destroy() } catch {} }
229 }
230 }
231
232202 async stop () {
233203 try { this._ws && this._ws.close() } catch {}
234204 try { this._http && this._http.close() } catch {}
ui/boot.js +4 -7
……@@ -418,16 +418,13 @@ export async function startBackend () {
418418 // pure port-scan failure means the main process itself didn't
419419 // start (pear-electron handoff issue, NOT a backend crash).
420420 //
421 // Most common cause: stale partial download from a prior release.
422 // Pear's cache may have a half-written app bundle. Clear and retry:
423 // rm -rf "$HOME/Library/Application Support/pear/by-dkey/00f61fc1473b9d01a199833fc96e76d5e99000c603ec697bc842f8d978538f4d"
424 // pear run pear://tco5...
421 // Most common cause: an interrupted local installation. Do not revive a
422 // remote app reference; reinstall the verified signed native package.
425423 throw new Error(
426424 `Could not reach backend on any port ${RPC_PORT_BASE}-${RPC_PORT_BASE + RPC_PORT_COUNT - 1} ` +
427425 `(${errors.join('; ')}). The Bare main process appears not to be running. ` +
428 `Most likely cause: stale partial download. Clear the cache at ` +
429 `~/Library/Application Support/pear/by-dkey/00f61fc1473b… and relaunch ` +
430 `pear run pear://tco5...`
426 `Most likely cause: an interrupted installation. Reinstall the verified ` +
427 `signed native package and relaunch it.`
431428 )
432429 }
433430 const rpc = new RpcClient(pipe)
ui/main.js +1 -3
……@@ -44,9 +44,7 @@ try {
4444 data?.message || '(no message)',
4545 data?.code ? '\nCode: ' + data.code : '',
4646 data?.stack ? '\n\n' + data.stack : '',
47 '\n\nLikely fix: clear the local app cache and relaunch:',
48 '\n rm -rf "$HOME/Library/Application Support/pear/by-dkey/00f61fc1473b9d01a199833fc96e76d5e99000c603ec697bc842f8d978538f4d"',
49 '\n pear run pear://tco5k7h38uoxatedp1wongdbhjxow1x7jiwm3t1i9cujbebhsbty'
47 '\n\nLikely fix: reinstall the verified signed native package, then relaunch it.'
5048 ].join('')
5149 root.render(html`<${Splash}
5250 message="Backend failed to boot"
ui/shell.js +4 -0
……@@ -2854,6 +2854,10 @@ function Apps ({ rpc, C, onLaunch }) {
28542854 setErr(''); setBusy('run-in-tab'); setLaunched('')
28552855 try {
28562856 const res = await rpc.request(C.CMD_RUN_APP_IN_TAB, { link: app.link }, 30000)
2857 if (res?.action === 'legacy-migration-required') {
2858 setErr(res.message || 'A verified native v3 package is required.')
2859 return
2860 }
28572861 onLaunch?.(res.url)
28582862 setLaunched(`Running ${app.name} headless in a tab.`)
28592863 setTimeout(() => setLaunched(''), 4000)