| … | … | @@ -1274,32 +1274,24 @@ rpc.handle(C.CMD_RESET_APP, async () => { |
| 1274 | 1274 | |
| 1275 | 1275 | rpc.handle(C.CMD_LAUNCH_PEAR_LINK, async (data) => { |
| 1276 | 1276 | const link = String(data?.link || '').trim() |
| 1277 | | if (!link) throw new Error('pear:// link required') |
| 1277 | if (!link) throw new Error('legacy app link required') |
| 1278 | 1278 | if (!/^pear:\/\/.+/.test(link) && !/^file:\/\/.+/.test(link)) { |
| 1279 | | throw new Error('Only pear:// and file:// links can be launched') |
| 1280 | | } |
| 1281 | | const keyHex = (data?.keyHex && /^[0-9a-f]{64}$/i.test(data.keyHex)) ? data.keyHex.toLowerCase() : null |
| 1282 | | // Fire-and-forget: pre-download the app bundle with progress (so the UI can |
| 1283 | | // render an inline bar driven by EVT_LAUNCH_PROGRESS instead of looking |
| 1284 | | // hung), then spawn it. Return immediately. |
| 1285 | | launchPearLinkWithProgress(link, keyHex).catch((err) => { |
| 1286 | | try { rpc.event(C.EVT_LAUNCH_PROGRESS, { key: keyHex, link, phase: 'error', error: (err && err.message) || 'launch failed' }) } catch {} |
| 1287 | | }) |
| 1288 | | return { started: true, link } |
| 1279 | throw new Error('Only legacy pear:// and file:// app links can be assessed') |
| 1280 | } |
| 1281 | return { |
| 1282 | started: false, |
| 1283 | action: 'legacy-migration-required', |
| 1284 | link, |
| 1285 | message: 'This legacy Pear app cannot be launched by PearBrowser v3. Install a verified native v3 package when its publisher provides one.' |
| 1286 | } |
| 1289 | 1287 | }) |
| 1290 | 1288 | |
| 1291 | | // Spawn a pear:// / file:// app in its own window. For pear:// links with a |
| 1292 | | // known drive key, first pull the bundle into OUR store (which also seeds the |
| 1293 | | // swarm, so the subsequent pear-run launch pulls from localhost and the window |
| 1294 | | // appears promptly) while streaming download progress to the UI. |
| 1289 | // Retained only as a compatibility seam for callers compiled against older |
| 1290 | // builds. A remote link is not a v3 worker entrypoint and must never execute. |
| 1295 | 1291 | async function launchPearLinkWithProgress (link, keyHex) { |
| 1296 | 1292 | const emit = (p) => { try { rpc.event(C.EVT_LAUNCH_PROGRESS, { key: keyHex, link, ...p }) } catch {} } |
| 1297 | 1293 | const spawn = () => { |
| 1298 | | const run = require('pear-run') |
| 1299 | | const pipe = run(link) |
| 1300 | | try { pipe.on('data', () => {}) } catch {} |
| 1301 | | try { pipe.on('crash', (info) => console.error('[pear-run] child crashed:', info)) } catch {} |
| 1302 | | try { pipe.on('error', (err) => console.error('[pear-run] child error:', err && err.message)) } catch {} |
| 1294 | throw new Error('legacy app migration required; remote links are not v3 worker entrypoints') |
| 1303 | 1295 | } |
| 1304 | 1296 | |
| 1305 | 1297 | // No drive to pre-fetch (file:// or unknown key) → straight to launch. |
| … | … | @@ -1318,7 +1310,7 @@ async function launchPearLinkWithProgress (link, keyHex) { |
| 1318 | 1310 | registerHiveRelayDrive(keyHex, drive) |
| 1319 | 1311 | await updateDriveBestEffort(drive) |
| 1320 | 1312 | } catch { |
| 1321 | | // Can't open it ourselves — let pear-run handle the download itself. |
| 1313 | // Do not fall back to a remote launcher; surface the migration boundary. |
| 1322 | 1314 | emit({ phase: 'launching', percent: 100 }) |
| 1323 | 1315 | spawn() |
| 1324 | 1316 | emit({ phase: 'done', percent: 100 }) |
| … | … | @@ -1370,8 +1362,12 @@ async function launchPearLinkWithProgress (link, keyHex) { |
| 1370 | 1362 | rpc.handle(C.CMD_RUN_APP_IN_TAB, async (data) => { |
| 1371 | 1363 | const link = String(data?.link || 'demo').trim() |
| 1372 | 1364 | if (!tabRuntime) throw new Error('tab runtime is not available') |
| 1373 | | if (link !== 'demo' && !/^pear:\/\/.+/.test(link) && !/^file:\/\/.+/.test(link)) { |
| 1374 | | throw new Error('Only the demo, or pear:// / file:// apps, can run in a tab') |
| 1365 | if (link !== 'demo') { |
| 1366 | return { |
| 1367 | action: 'legacy-migration-required', |
| 1368 | link, |
| 1369 | message: 'This legacy app needs a verified v3 package before it can run in a PearBrowser tab.' |
| 1370 | } |
| 1375 | 1371 | } |
| 1376 | 1372 | const res = tabRuntime.open(link) |
| 1377 | 1373 | console.log('[tab-runtime] run-in-tab') |
| … | … | @@ -3313,7 +3309,7 @@ async function boot () { |
| 3313 | 3309 | // bridges each tab's WebSocket to a pear-request worker pipe. Best-effort — |
| 3314 | 3310 | // a failure here just means the in-tab path is unavailable, not a boot block. |
| 3315 | 3311 | try { |
| 3316 | | tabRuntime = new TabRuntime({ pearRun: (link) => require('pear-run')(link) }) |
| 3312 | tabRuntime = new TabRuntime() |
| 3317 | 3313 | await tabRuntime.start() |
| 3318 | 3314 | } catch (err) { |
| 3319 | 3315 | console.error('[tab-runtime] failed to start:', err && err.message) |