| 74 | } |
| 75 | |
| 76 | async function sync(fullPath, remotePath) { |
| 77 | const info = await stat(fullPath).catch(() => null); |
| 78 | if (!info?.isFile()) return; |
| 79 | if (info.size > 200_000) { |
| 80 | console.error(`sync skip ${remotePath} too-large`); |
| 81 | return; |
| 82 | } |
| 83 | const bytes = await readFile(fullPath); |
| 84 | if (bytes.includes(0)) { |
| 85 | console.error(`sync skip ${remotePath} binary`); |
| 86 | return; |
| 87 | } |
| 88 | let content; |
| 89 | try { |
| 90 | content = new TextDecoder("utf-8", { fatal: true }).decode(bytes); |
| 91 | } catch { |
| 92 | console.error(`sync skip ${remotePath} non-utf8`); |
| 93 | return; |
| 94 | } |
| 95 | const response = await fetch(`${url}/api/runs/${encodeURIComponent(runId)}/write`, { |
| 96 | method: "POST", |
| 97 | headers, |
| 98 | body: JSON.stringify({ path: remotePath, content }), |
| 99 | }); |
| 100 | const body = await response.json().catch(() => null); |
| 101 | if (!response.ok || body?.ok !== true) throw new Error(`${response.status} ${JSON.stringify(body)}`); |
| 102 | console.log(`sync ok ${remotePath}`); |
| 103 | } |
| 104 | |
| 105 | function stop() { |
| 106 | watcher.close(); |