| … | … | @@ -150,7 +150,7 @@ async function initPearRuntime () { |
| 150 | 150 | }) |
| 151 | 151 | |
| 152 | 152 | runtime.on('updating-delta', (data) => { |
| 153 | | console.log(`[launcher] delta: ${JSON.stringify({ key: data.key, op: data.op, bytes: data.bytesAdded })}`) |
| 153 | if (data.key) console.log(`[launcher] ${data.key}`) |
| 154 | 154 | }) |
| 155 | 155 | |
| 156 | 156 | // Mutex: only one 'updated' handler runs at a time to prevent race conditions. |
| … | … | @@ -159,60 +159,45 @@ async function initPearRuntime () { |
| 159 | 159 | let applying = false |
| 160 | 160 | |
| 161 | 161 | runtime.on('updated', async () => { |
| 162 | | if (applying || runtime.applied) { |
| 163 | | console.log(`[launcher] Update skipped (applying=${applying} applied=${runtime.applied})`) |
| 164 | | return |
| 165 | | } |
| 162 | if (applying || runtime.applied) return |
| 166 | 163 | applying = true |
| 167 | | console.log(`[launcher] Update ready — applying... (next=${runtime.next} app=${runtime.app})`) |
| 168 | 164 | |
| 169 | | try { |
| 170 | | // Log what's in the swap source directory (find the actual by-arch path) |
| 171 | | try { |
| 172 | | const nextDir = runtime.next |
| 173 | | const byArch = path.join(nextDir, 'by-arch') |
| 174 | | if (fs.existsSync(byArch)) { |
| 175 | | const hosts = fs.readdirSync(byArch) |
| 176 | | console.log(`[launcher] Swap next hosts: ${hosts.join(', ')}`) |
| 177 | | for (const h of hosts) { |
| 178 | | const appDir2 = path.join(byArch, h, 'app', 'godot-keet') |
| 179 | | try { |
| 180 | | const files = fs.readdirSync(appDir2) |
| 181 | | console.log(`[launcher] Swap src ${h}: ${files.join(', ')}`) |
| 182 | | } catch {} |
| 183 | | } |
| 184 | | } else { |
| 185 | | console.log(`[launcher] No by-arch dir in ${nextDir}`) |
| 186 | | } |
| 187 | | } catch (e) { |
| 188 | | console.log(`[launcher] Swap source check failed: ${e.message}`) |
| 189 | | } |
| 165 | console.log('[launcher] Update ready — applying...') |
| 190 | 166 | |
| 167 | try { |
| 191 | 168 | await runtime.applyUpdate() |
| 192 | 169 | |
| 193 | 170 | if (runtime.applied) { |
| 194 | | // Swap succeeded — update version files from Hyperdrive |
| 171 | // Binary swap succeeded (by-arch). Now also update ALL other app files |
| 172 | // from the Hyperdrive so index.js, daemon/, node_modules/ etc. stay current. |
| 173 | // Without this, only the Godot binary+pck get OTA'd — launcher code changes |
| 174 | // would require a full reinstall. |
| 195 | 175 | const appDir = getAppDir() |
| 196 | | const manifest = await runtime.drive.get('/package.json') |
| 197 | | if (manifest) { |
| 198 | | const remotePkg = JSON.parse(manifest) |
| 199 | | fs.writeFileSync(path.join(appDir, 'package.json'), |
| 200 | | JSON.stringify(Object.assign(pkg, { version: remotePkg.version }), null, 2) + '\n') |
| 201 | | } |
| 202 | | const remoteVersion = await runtime.drive.get('/version.txt') |
| 203 | | if (remoteVersion) { |
| 204 | | fs.writeFileSync(path.join(appDir, 'version.txt'), remoteVersion) |
| 205 | | } |
| 176 | const co = runtime.drive.checkout(runtime.length) |
| 177 | let updated = 0 |
| 206 | 178 | |
| 207 | | // Verify swap target has expected files |
| 208 | 179 | try { |
| 209 | | const targetFiles = fs.readdirSync(runtime.app) |
| 210 | | console.log(`[launcher] After swap, app dir: ${targetFiles.join(', ')}`) |
| 211 | | } catch {} |
| 180 | for await (const entry of co.list('/')) { |
| 181 | const key = entry.key || entry |
| 182 | if (typeof key !== 'string') continue |
| 183 | // Skip by-arch (already swapped atomically) and build/ (duplicate of by-arch) |
| 184 | if (key.startsWith('/by-arch/') || key.startsWith('/build/')) continue |
| 185 | |
| 186 | const data = await co.get(key) |
| 187 | if (data) { |
| 188 | const dst = path.join(appDir, key) |
| 189 | fs.mkdirSync(path.dirname(dst), { recursive: true }) |
| 190 | fs.writeFileSync(dst, data) |
| 191 | updated++ |
| 192 | } |
| 193 | } |
| 194 | console.log(`[launcher] Updated ${updated} app files from Hyperdrive`) |
| 195 | } catch (err) { |
| 196 | console.error('[launcher] App file sync partial:', err.message) |
| 197 | } |
| 212 | 198 | |
| 199 | await co.close() |
| 213 | 200 | console.log('[launcher] Update applied — restart for new version') |
| 214 | | } else { |
| 215 | | console.log('[launcher] applyUpdate was no-op (applied still false)') |
| 216 | 201 | } |
| 217 | 202 | } catch (err) { |
| 218 | 203 | console.error('[launcher] Failed to apply update:', err.message) |