feat(mini-apps): live data refresh + install reliability

- watch ~/.rowboat/apps: data.json changes push into open apps (shim
  re-fetches, onData fires); installs/updates re-list the gallery live and
  reload an open app once the write settles
- atomic installs (temp->rename) so an app opened mid-install never reads a
  partial file
- serve app assets by direct disk read instead of net.fetch(file://) —
  Chromium's network service could stall and blank every open app at once
This commit is contained in:
Gagan 2026-07-04 15:59:13 +05:30
parent b548173593
commit 2c038fe518
8 changed files with 150 additions and 16 deletions

View file

@ -1527,8 +1527,15 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
const dir = path.join(WorkDir, 'apps', m.id);
const dist = path.join(dir, 'dist');
await fs.mkdir(dist, { recursive: true });
await fs.writeFile(path.join(dir, 'manifest.json'), JSON.stringify(m, null, 2));
await fs.writeFile(path.join(dist, 'index.html'), html);
// Atomic writes (temp→rename): an app opened mid-install must never
// see a partially-written manifest or index.html (blank screen).
const writeAtomic = async (p: string, content: string) => {
const tmp = `${p}.tmp`;
await fs.writeFile(tmp, content);
await fs.rename(tmp, p);
};
await writeAtomic(path.join(dist, 'index.html'), html);
await writeAtomic(path.join(dir, 'manifest.json'), JSON.stringify(m, null, 2));
if (data !== undefined) {
let payload: unknown = data;
if (typeof payload === 'string') { try { payload = JSON.parse(payload); } catch { payload = undefined; } }