@intentic/machine 1.344.0 → 1.346.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -154
- package/dist/autostart/autostart.d.ts +0 -1
- package/dist/autostart/autostart.d.ts.map +1 -1
- package/dist/autostart/autostart.js +3 -9
- package/dist/autostart/autostart.js.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +4 -21
- package/dist/commands.js.map +1 -1
- package/dist/device/commands.d.ts +8 -2
- package/dist/device/commands.d.ts.map +1 -1
- package/dist/device/commands.js +23 -9
- package/dist/device/commands.js.map +1 -1
- package/dist/device/connection.d.ts.map +1 -1
- package/dist/device/connection.js +27 -9
- package/dist/device/connection.js.map +1 -1
- package/dist/device/grant.d.ts +244 -1
- package/dist/device/grant.d.ts.map +1 -1
- package/dist/device/mcp.d.ts.map +1 -1
- package/dist/device/mcp.js +25 -8
- package/dist/device/mcp.js.map +1 -1
- package/dist/device/router.d.ts +469 -226
- package/dist/device/router.d.ts.map +1 -1
- package/dist/device/router.js +18 -10
- package/dist/device/router.js.map +1 -1
- package/dist/device/sandbox-rounds/keeper.d.ts +4 -1
- package/dist/device/sandbox-rounds/keeper.d.ts.map +1 -1
- package/dist/device/sandbox-rounds/keeper.js +13 -7
- package/dist/device/sandbox-rounds/keeper.js.map +1 -1
- package/dist/device/sandbox-rounds/sleep.d.ts +15 -2
- package/dist/device/sandbox-rounds/sleep.d.ts.map +1 -1
- package/dist/device/sandbox-rounds/sleep.js +68 -13
- package/dist/device/sandbox-rounds/sleep.js.map +1 -1
- package/dist/device/tools/agent.d.ts +15 -0
- package/dist/device/tools/agent.d.ts.map +1 -1
- package/dist/device/tools/agent.js +75 -19
- package/dist/device/tools/agent.js.map +1 -1
- package/dist/device/tools/android.d.ts +1 -1
- package/dist/device/tools/android.d.ts.map +1 -1
- package/dist/device/tools/android.js +17 -8
- package/dist/device/tools/android.js.map +1 -1
- package/dist/device/tools/browser.d.ts.map +1 -1
- package/dist/device/tools/browser.js +3 -5
- package/dist/device/tools/browser.js.map +1 -1
- package/dist/device/tools/ic-binary.d.ts +2 -1
- package/dist/device/tools/ic-binary.d.ts.map +1 -1
- package/dist/device/tools/ic-binary.js +34 -4
- package/dist/device/tools/ic-binary.js.map +1 -1
- package/dist/device/tools/os/settings.js +3 -3
- package/dist/device/tools/os/settings.js.map +1 -1
- package/dist/device/tools/programs/app-wait-tools.d.ts +12 -0
- package/dist/device/tools/programs/app-wait-tools.d.ts.map +1 -0
- package/dist/device/tools/programs/app-wait-tools.js +60 -0
- package/dist/device/tools/programs/app-wait-tools.js.map +1 -0
- package/dist/device/tools/programs/app-wait.d.ts +19 -0
- package/dist/device/tools/programs/app-wait.d.ts.map +1 -0
- package/dist/device/tools/programs/app-wait.js +89 -0
- package/dist/device/tools/programs/app-wait.js.map +1 -0
- package/dist/device/tools/programs/file-changes.d.ts +2 -0
- package/dist/device/tools/programs/file-changes.d.ts.map +1 -0
- package/dist/device/tools/programs/file-changes.js +19 -0
- package/dist/device/tools/programs/file-changes.js.map +1 -0
- package/dist/device/tools/programs/programs.d.ts +12 -0
- package/dist/device/tools/programs/programs.d.ts.map +1 -1
- package/dist/device/tools/programs/programs.js +13 -12
- package/dist/device/tools/programs/programs.js.map +1 -1
- package/dist/device/tools/programs/stop-all.d.ts +10 -0
- package/dist/device/tools/programs/stop-all.d.ts.map +1 -0
- package/dist/device/tools/programs/stop-all.js +52 -0
- package/dist/device/tools/programs/stop-all.js.map +1 -0
- package/dist/device/tools/sandboxes.d.ts +2 -0
- package/dist/device/tools/sandboxes.d.ts.map +1 -1
- package/dist/device/tools/sandboxes.js +11 -3
- package/dist/device/tools/sandboxes.js.map +1 -1
- package/dist/device/tools/shell.d.ts +1 -1
- package/dist/device/tools/shell.d.ts.map +1 -1
- package/dist/install.d.ts +15 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +35 -6
- package/dist/install.js.map +1 -1
- package/dist/keyring/app-commands.d.ts +15 -0
- package/dist/keyring/app-commands.d.ts.map +1 -0
- package/dist/keyring/app-commands.js +247 -0
- package/dist/keyring/app-commands.js.map +1 -0
- package/dist/keyring/commands.d.ts +1 -1
- package/dist/keyring/commands.d.ts.map +1 -1
- package/dist/keyring/commands.js +26 -32
- package/dist/keyring/commands.js.map +1 -1
- package/dist/keyring/git-helper.d.ts +8 -0
- package/dist/keyring/git-helper.d.ts.map +1 -0
- package/dist/keyring/git-helper.js +60 -0
- package/dist/keyring/git-helper.js.map +1 -0
- package/dist/keyring/keyring-status.d.ts +8 -1
- package/dist/keyring/keyring-status.d.ts.map +1 -1
- package/dist/keyring/keyring-status.js +39 -4
- package/dist/keyring/keyring-status.js.map +1 -1
- package/dist/keyring/native-host-install.d.ts +28 -0
- package/dist/keyring/native-host-install.d.ts.map +1 -0
- package/dist/keyring/native-host-install.js +99 -0
- package/dist/keyring/native-host-install.js.map +1 -0
- package/dist/keyring/relay.d.ts +5 -1
- package/dist/keyring/relay.d.ts.map +1 -1
- package/dist/keyring/relay.js +16 -0
- package/dist/keyring/relay.js.map +1 -1
- package/dist/keyring/sandbox-push.d.ts +4 -0
- package/dist/keyring/sandbox-push.d.ts.map +1 -0
- package/dist/keyring/sandbox-push.js +21 -0
- package/dist/keyring/sandbox-push.js.map +1 -0
- package/dist/keyring/service.d.ts +11 -5
- package/dist/keyring/service.d.ts.map +1 -1
- package/dist/keyring/service.js +34 -51
- package/dist/keyring/service.js.map +1 -1
- package/dist/resident/resident.d.ts +1 -0
- package/dist/resident/resident.d.ts.map +1 -1
- package/dist/resident/resident.js +9 -1
- package/dist/resident/resident.js.map +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +1 -8
- package/dist/status.js.map +1 -1
- package/dist/sync/binary/mutagen-binary.d.ts +4 -0
- package/dist/sync/binary/mutagen-binary.d.ts.map +1 -0
- package/dist/sync/binary/mutagen-binary.js +65 -0
- package/dist/sync/binary/mutagen-binary.js.map +1 -0
- package/dist/sync/commands.d.ts +1 -9
- package/dist/sync/commands.d.ts.map +1 -1
- package/dist/sync/commands.js +49 -84
- package/dist/sync/commands.js.map +1 -1
- package/dist/sync/config.d.ts +21 -25
- package/dist/sync/config.d.ts.map +1 -1
- package/dist/sync/config.js +76 -35
- package/dist/sync/config.js.map +1 -1
- package/dist/sync/endpoint.d.ts +1 -1
- package/dist/sync/endpoint.d.ts.map +1 -1
- package/dist/sync/endpoint.js +2 -3
- package/dist/sync/endpoint.js.map +1 -1
- package/dist/sync/folders.d.ts.map +1 -1
- package/dist/sync/folders.js.map +1 -1
- package/dist/sync/forget-command.js +1 -1
- package/dist/sync/forget-command.js.map +1 -1
- package/dist/sync/gone-watch.d.ts +1 -1
- package/dist/sync/gone-watch.d.ts.map +1 -1
- package/dist/sync/gone-watch.js +16 -40
- package/dist/sync/gone-watch.js.map +1 -1
- package/dist/sync/holds.d.ts +30 -0
- package/dist/sync/holds.d.ts.map +1 -0
- package/dist/sync/holds.js +117 -0
- package/dist/sync/holds.js.map +1 -0
- package/dist/sync/low-disk.d.ts +2 -9
- package/dist/sync/low-disk.d.ts.map +1 -1
- package/dist/sync/low-disk.js +10 -45
- package/dist/sync/low-disk.js.map +1 -1
- package/dist/sync/mirror.d.ts +2 -3
- package/dist/sync/mirror.d.ts.map +1 -1
- package/dist/sync/mirror.js +61 -160
- package/dist/sync/mirror.js.map +1 -1
- package/dist/sync/mutagen-env.d.ts +1 -3
- package/dist/sync/mutagen-env.d.ts.map +1 -1
- package/dist/sync/mutagen-env.js +2 -12
- package/dist/sync/mutagen-env.js.map +1 -1
- package/dist/sync/mutagen-move.d.ts +3 -1
- package/dist/sync/mutagen-move.d.ts.map +1 -1
- package/dist/sync/mutagen-move.js +31 -22
- package/dist/sync/mutagen-move.js.map +1 -1
- package/dist/sync/mutagen.d.ts +6 -12
- package/dist/sync/mutagen.d.ts.map +1 -1
- package/dist/sync/mutagen.js +17 -144
- package/dist/sync/mutagen.js.map +1 -1
- package/dist/sync/project/project-commands.d.ts +0 -3
- package/dist/sync/project/project-commands.d.ts.map +1 -1
- package/dist/sync/project/project-commands.js +5 -3
- package/dist/sync/project/project-commands.js.map +1 -1
- package/dist/sync/project/project-delivery.d.ts.map +1 -1
- package/dist/sync/project/project-delivery.js +5 -4
- package/dist/sync/project/project-delivery.js.map +1 -1
- package/dist/sync/project/project-transfer.d.ts +4 -0
- package/dist/sync/project/project-transfer.d.ts.map +1 -1
- package/dist/sync/project/project-transfer.js +9 -2
- package/dist/sync/project/project-transfer.js.map +1 -1
- package/dist/sync/report.d.ts.map +1 -1
- package/dist/sync/report.js +18 -11
- package/dist/sync/report.js.map +1 -1
- package/dist/sync/retire.d.ts.map +1 -1
- package/dist/sync/retire.js +1 -2
- package/dist/sync/retire.js.map +1 -1
- package/dist/sync/tunnel.d.ts +4 -3
- package/dist/sync/tunnel.d.ts.map +1 -1
- package/dist/sync/tunnel.js +107 -42
- package/dist/sync/tunnel.js.map +1 -1
- package/dist/sync/uninstall.d.ts.map +1 -1
- package/dist/sync/uninstall.js +4 -3
- package/dist/sync/uninstall.js.map +1 -1
- package/dist/sync/wake.d.ts +3 -0
- package/dist/sync/wake.d.ts.map +1 -0
- package/dist/sync/wake.js +43 -0
- package/dist/sync/wake.js.map +1 -0
- package/dist/uninstall/command.d.ts +3 -0
- package/dist/uninstall/command.d.ts.map +1 -0
- package/dist/uninstall/command.js +40 -0
- package/dist/uninstall/command.js.map +1 -0
- package/dist/uninstall/distros.d.ts +8 -0
- package/dist/uninstall/distros.d.ts.map +1 -0
- package/dist/uninstall/distros.js +103 -0
- package/dist/uninstall/distros.js.map +1 -0
- package/dist/uninstall/plan.d.ts +23 -0
- package/dist/uninstall/plan.d.ts.map +1 -0
- package/dist/uninstall/plan.js +71 -0
- package/dist/uninstall/plan.js.map +1 -0
- package/dist/uninstall/purge.d.ts +3 -0
- package/dist/uninstall/purge.d.ts.map +1 -0
- package/dist/uninstall/purge.js +79 -0
- package/dist/uninstall/purge.js.map +1 -0
- package/dist/uninstall/steps.d.ts +12 -0
- package/dist/uninstall/steps.d.ts.map +1 -0
- package/dist/uninstall/steps.js +200 -0
- package/dist/uninstall/steps.js.map +1 -0
- package/dist/uninstall/sweep.d.ts +12 -0
- package/dist/uninstall/sweep.d.ts.map +1 -0
- package/dist/uninstall/sweep.js +90 -0
- package/dist/uninstall/sweep.js.map +1 -0
- package/dist/uninstall/tally.d.ts +14 -0
- package/dist/uninstall/tally.d.ts.map +1 -0
- package/dist/uninstall/tally.js +25 -0
- package/dist/uninstall/tally.js.map +1 -0
- package/package.json +9 -9
- package/dist/sync/attach-commands.d.ts +0 -39
- package/dist/sync/attach-commands.d.ts.map +0 -1
- package/dist/sync/attach-commands.js +0 -163
- package/dist/sync/attach-commands.js.map +0 -1
- package/dist/sync/swap-pause.d.ts +0 -10
- package/dist/sync/swap-pause.d.ts.map +0 -1
- package/dist/sync/swap-pause.js +0 -29
- package/dist/sync/swap-pause.js.map +0 -1
package/dist/sync/mirror.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
-
import { sleep } from "@intentic/base/async";
|
|
3
2
|
import { errorMessage } from "@intentic/base/errors";
|
|
4
3
|
import { plural } from "@intentic/base/format";
|
|
5
4
|
import { writeFileAtomic } from "@intentic/base/fs";
|
|
6
5
|
import { PortsListSchema } from "@intentic/sandbox-contract/schemas/ports";
|
|
7
|
-
import {
|
|
6
|
+
import { mirrorHeartbeatPath, pairingKey, pairingTransport, readState, removeSandboxPairings, retireLegacyPairings, setSandboxGone, setSwapSince, setUnreachableSince, updateState, } from "./config.js";
|
|
8
7
|
import { pairingEndpoint } from "./endpoint.js";
|
|
9
8
|
import { runProcess, runProcessSync } from "./exec.js";
|
|
10
9
|
import { candidateBases, createDaemonBases, dialedPairings } from "../daemon-base.js";
|
|
11
10
|
import { answeredGone, goneStep, SandboxGoneError } from "./gone.js";
|
|
12
|
-
import { checkContainers, checkKeptHere, sandboxAnswered, sandboxSaidGone } from "./gone-watch.js";
|
|
11
|
+
import { checkContainers, checkKeptHere, goneLingers, sandboxAnswered, sandboxSaidGone } from "./gone-watch.js";
|
|
13
12
|
import { retireSandbox } from "./retire.js";
|
|
14
13
|
import { readSwaps } from "../device/sandbox-rounds/ic-swaps.js";
|
|
15
14
|
import { publishedPortsReader } from "./docker-ports.js";
|
|
16
15
|
import { realBridgeExec, runGitBridge } from "./git-bridge.js";
|
|
16
|
+
import { holdSessions, holdsSync, recordOf, RELEASE_FORWARDS_MS, shouldReleaseForwards, swapBegan, swapStep, syncHeld, syncHolds, UNREACHABLE_HOLD_MS } from "./holds.js";
|
|
17
17
|
import { guardDisks } from "./low-disk.js";
|
|
18
|
-
import {
|
|
18
|
+
import { ensureSyncSession, existingSyncSessions, forwardedPorts, forwardSessionName, healDerivedConflicts, MUTAGEN_CALL_TIMEOUT_MS, mutagenForwardArgs, ourForwardSessions, relabelBudget, retireOrphanSessions, runMutagenAsync, sayDaemonEventsTo, sessionLabels, startMutagenDaemon, syncMode, syncSessionNames, } from "./mutagen.js";
|
|
19
|
+
import { ensureMutagen } from "./binary/mutagen-binary.js";
|
|
19
20
|
import { moveOffSharedMutagen } from "./mutagen-move.js";
|
|
20
21
|
import { quieted } from "./repeats.js";
|
|
22
|
+
import { sleepUnlessWoken } from "./wake.js";
|
|
21
23
|
import { deviceReport, scopedReport } from "./report.js";
|
|
22
24
|
import { pairingSshConfig, writeManagedSshConfig } from "./ssh.js";
|
|
23
|
-
import { holdsSync, recordOf, RELEASE_FORWARDS_MS, shouldReleaseForwards, swapPauseStep } from "./swap-pause.js";
|
|
24
25
|
import { createTunnelPool, tunnelTargets } from "./tunnel.js";
|
|
25
26
|
const POLL_MS = 5000;
|
|
26
27
|
const PORTS_TIMEOUT_MS = 10_000;
|
|
@@ -28,8 +29,6 @@ const REPO_LIST_EVERY_TICKS = 12;
|
|
|
28
29
|
const REPORT_EVERY_TICKS = 3;
|
|
29
30
|
const REPORT_TIMEOUT_MS = 10_000;
|
|
30
31
|
const REVOKED_POLLS = 3;
|
|
31
|
-
const UNREACHABLE_PAUSE_MS = 60 * 60_000;
|
|
32
|
-
export const shouldAutoPauseFileSync = (unreachableForMs) => unreachableForMs >= UNREACHABLE_PAUSE_MS;
|
|
33
32
|
export const pollBackoffMs = (unreachableForMs) => unreachableForMs < 60_000 ? 0 : unreachableForMs < 10 * 60_000 ? 30_000 : 5 * 60_000;
|
|
34
33
|
export const pollDue = (held, now) => held === undefined || now - held.lastTried >= pollBackoffMs(now - held.since);
|
|
35
34
|
export class SyncAuthError extends Error {
|
|
@@ -225,9 +224,6 @@ const servePairing = async (context, pairing, base) => {
|
|
|
225
224
|
const { mutagen, claimedBy, holding, holders, say: log } = context;
|
|
226
225
|
const baseline = pairing.mirroredPorts ?? [];
|
|
227
226
|
const ports = pairing.syncToken === undefined ? [] : await fetchWorkspacePorts(base, pairing.syncToken);
|
|
228
|
-
if (isAttachedPairing(pairing)) {
|
|
229
|
-
return [];
|
|
230
|
-
}
|
|
231
227
|
if (pairing.mirrorOff === true) {
|
|
232
228
|
await retireSwitchedOff(mutagen, pairing, log);
|
|
233
229
|
return [];
|
|
@@ -286,7 +282,6 @@ const dropRevokedPairing = async (mutagen, sandboxId, log) => {
|
|
|
286
282
|
await removeSandboxPairings(sandboxId);
|
|
287
283
|
retireOrphanSessions(mutagen, (await readState()).pairings, log);
|
|
288
284
|
};
|
|
289
|
-
const keyOfSandbox = (key, sandboxId) => key === sandboxId || key.startsWith(`${sandboxId}~`);
|
|
290
285
|
const guard = async (log, what, step) => {
|
|
291
286
|
try {
|
|
292
287
|
await step();
|
|
@@ -308,15 +303,14 @@ const absorbRejectedPoll = async (mutagen, pairing, rejectedPolls, repos, sessio
|
|
|
308
303
|
log(`${pairing.sandboxId} rejected the sync token ${REVOKED_POLLS} polls in a row: this machine's enrollment was revoked.`);
|
|
309
304
|
rejectedPolls.delete(pairing.sandboxId);
|
|
310
305
|
repos.delete(pairing.sandboxId);
|
|
311
|
-
|
|
312
|
-
sessionsPending.delete(key);
|
|
313
|
-
}
|
|
306
|
+
sessionsPending.delete(pairingKey(pairing));
|
|
314
307
|
await dropRevokedPairing(mutagen, pairing.sandboxId, log);
|
|
315
308
|
return true;
|
|
316
309
|
};
|
|
317
310
|
const absorbUnreachablePoll = async (mutagen, pairing, unreachable, log) => {
|
|
318
311
|
const now = Date.now();
|
|
319
|
-
const
|
|
312
|
+
const before = unreachable.get(pairing.sandboxId);
|
|
313
|
+
const since = before?.since ?? pairing.unreachableSince ?? now;
|
|
320
314
|
unreachable.set(pairing.sandboxId, { since, lastTried: now });
|
|
321
315
|
if (pairing.unreachableSince === undefined) {
|
|
322
316
|
await setUnreachableSince(pairing.sandboxId, since);
|
|
@@ -326,15 +320,11 @@ const absorbUnreachablePoll = async (mutagen, pairing, unreachable, log) => {
|
|
|
326
320
|
await retirePairingMirror(mutagen, pairing.sandboxId);
|
|
327
321
|
log(` ${pairing.sandboxId}: unreachable for ${RELEASE_FORWARDS_MS / 60_000} minutes; took its ${plural(mirrored, "port")} off localhost. They come back when it answers again.`);
|
|
328
322
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (pairing.fileSyncAutoPaused === true || !shouldAutoPauseFileSync(unreachableForMs) || !pauseRunningSync(mutagen, pairing)) {
|
|
333
|
-
return false;
|
|
323
|
+
const holds = now - since >= UNREACHABLE_HOLD_MS;
|
|
324
|
+
if (holds && (before === undefined || before.lastTried - since < UNREACHABLE_HOLD_MS)) {
|
|
325
|
+
log(` ${pairing.sandboxId}: unreachable for an hour; its file sync holds still to stop permanent reconnect and scanning load, and carries on when it answers.`);
|
|
334
326
|
}
|
|
335
|
-
|
|
336
|
-
log(` ${pairingKey(pairing)}: unreachable for one hour; paused its Mutagen sessions to stop permanent reconnect/scanning load. They resume automatically when it returns.`);
|
|
337
|
-
return true;
|
|
327
|
+
return holds;
|
|
338
328
|
};
|
|
339
329
|
const startBridge = (tracking, pairing, tick, say) => {
|
|
340
330
|
const id = pairing.sandboxId;
|
|
@@ -392,7 +382,7 @@ export const markPrepared = (prepared, pairing) => void prepared.add(setupOf(pai
|
|
|
392
382
|
const prepareSession = async (mutagen, pairing, pending, relabels, say) => {
|
|
393
383
|
let converged = false;
|
|
394
384
|
await guard(say, `${pairingKey(pairing)}: preparing its file sync`, async () => {
|
|
395
|
-
converged = await ensureSyncSession(mutagen, pairing, say, relabels);
|
|
385
|
+
converged = await ensureSyncSession(mutagen, pairing, say, syncHeld(pairing, Date.now()), relabels);
|
|
396
386
|
});
|
|
397
387
|
if (converged) {
|
|
398
388
|
pending.delete(pairingKey(pairing));
|
|
@@ -403,62 +393,15 @@ const prepareSession = async (mutagen, pairing, pending, relabels, say) => {
|
|
|
403
393
|
return converged;
|
|
404
394
|
};
|
|
405
395
|
const dormant = (mutagen, pairing) => pairing.goneSince !== undefined ||
|
|
406
|
-
pairing.
|
|
407
|
-
(pairing.
|
|
408
|
-
|
|
409
|
-
const told = new Map();
|
|
410
|
-
const ready = [];
|
|
411
|
-
for (const pairing of setups) {
|
|
412
|
-
if (isAttachedPairing(pairing) && pairing.mode === "sync" && pairing.localDir !== undefined && firstCopyAhead(pairing)) {
|
|
413
|
-
const said = told.get(pairing.sandboxId) ?? (await announce(pairing));
|
|
414
|
-
told.set(pairing.sandboxId, said);
|
|
415
|
-
if (!said) {
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
ready.push(pairing);
|
|
420
|
-
}
|
|
421
|
-
return ready;
|
|
422
|
-
};
|
|
423
|
-
const announcer = (mutagen, bases, unsupported, unreachable, gone, say) => async (pairing) => {
|
|
424
|
-
if (pairing.goneSince !== undefined) {
|
|
425
|
-
return false;
|
|
426
|
-
}
|
|
427
|
-
if (pairing.syncToken === undefined || unsupported.has(pairing.sandboxId)) {
|
|
428
|
-
return true;
|
|
429
|
-
}
|
|
430
|
-
if (!pollDue(unreachable.get(pairing.sandboxId), Date.now())) {
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
try {
|
|
434
|
-
const response = await sendReport(await bases.resolve(pairing), pairing, await deviceReport(mutagen));
|
|
435
|
-
if (response.status === 404) {
|
|
436
|
-
unsupported.add(pairing.sandboxId);
|
|
437
|
-
say(` ${pairing.sandboxId}: this sandbox is running a daemon without machine reports, its Devices view will stay empty.`);
|
|
438
|
-
return true;
|
|
439
|
-
}
|
|
440
|
-
if (answeredGone(response)) {
|
|
441
|
-
await gone(pairing);
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
|
-
if (!response.ok) {
|
|
445
|
-
say(` ${pairingKey(pairing)}: ${pairing.sandboxId} did not take the report naming this folder (${response.status}); its first copy waits for it.`);
|
|
446
|
-
}
|
|
447
|
-
return response.ok;
|
|
448
|
-
}
|
|
449
|
-
catch (error) {
|
|
450
|
-
say(` ${pairingKey(pairing)}: ${pairing.sandboxId} could not be told of this folder (${errorMessage(error)}); its first copy waits until it can.`);
|
|
451
|
-
return false;
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
const prepareSessions = async (mutagen, pairings, prepared, pending, announce, passes, say) => {
|
|
455
|
-
const firstCopyAhead = (pairing) => (existingSyncSessions(mutagen, syncSessionNames(pairing)) ?? []).length === 0;
|
|
396
|
+
pairing.containerAway !== undefined ||
|
|
397
|
+
(syncHolds(pairing, Date.now()).includes("unreachable") && (existingSyncSessions(mutagen, syncSessionNames(pairing)) ?? [undefined]).length > 0);
|
|
398
|
+
const prepareSessions = async (mutagen, pairings, prepared, pending, passes, say) => {
|
|
456
399
|
const unprepared = unpreparedSetups(pairings, prepared);
|
|
457
400
|
const docker = unprepared.filter((pairing) => pairingTransport(pairing) === "docker");
|
|
458
401
|
if (docker.length > 0) {
|
|
459
402
|
await guard(say, "asking after the containers docker pairings reach", async () => await checkContainers(passes.fate, docker, answeringOf(passes.tracking)));
|
|
460
403
|
}
|
|
461
|
-
for (const pairing of
|
|
404
|
+
for (const pairing of unprepared) {
|
|
462
405
|
markPrepared(prepared, pairing);
|
|
463
406
|
if (dormant(mutagen, pairing)) {
|
|
464
407
|
pending.add(pairingKey(pairing));
|
|
@@ -478,25 +421,23 @@ const retryPendingSessions = async (mutagen, pairings, sessionsPending, tick, re
|
|
|
478
421
|
}
|
|
479
422
|
}
|
|
480
423
|
};
|
|
481
|
-
const holdSyncDuringSwaps = async (
|
|
482
|
-
const records = await
|
|
424
|
+
export const holdSyncDuringSwaps = async (pairings, unreachable, say, read = readSwaps, record = setSwapSince) => {
|
|
425
|
+
const records = await read();
|
|
483
426
|
const now = Date.now();
|
|
484
427
|
const held = new Set();
|
|
485
428
|
for (const pairing of pairings) {
|
|
486
|
-
const
|
|
487
|
-
const holds = holdsSync(
|
|
429
|
+
const swap = recordOf(pairing, records);
|
|
430
|
+
const holds = holdsSync(swap, pairing.swapSince, now, !unreachable.has(pairing.sandboxId));
|
|
488
431
|
if (holds) {
|
|
489
432
|
held.add(pairing.sandboxId);
|
|
490
433
|
}
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
await setFileSyncSwapPaused(pairingKey(pairing), false);
|
|
499
|
-
say(` ${pairingKey(pairing)}: its sandbox's swap is settled; file sync resumed.`);
|
|
434
|
+
const began = swapBegan(swap, pairing.swapSince, now);
|
|
435
|
+
const step = swapStep(pairing, holds, began);
|
|
436
|
+
if (step !== undefined) {
|
|
437
|
+
await record(pairingKey(pairing), step === "began" ? began : undefined);
|
|
438
|
+
say(step === "began"
|
|
439
|
+
? ` ${pairingKey(pairing)}: its sandbox is being swapped on this machine; file sync holds still until the new version has held.`
|
|
440
|
+
: ` ${pairingKey(pairing)}: its sandbox's swap is settled; file sync carries on.`);
|
|
500
441
|
}
|
|
501
442
|
}
|
|
502
443
|
return held;
|
|
@@ -506,29 +447,23 @@ const runPairingPass = async (context, pairing, base) => {
|
|
|
506
447
|
const { rejectedPolls, unreachable } = tracking;
|
|
507
448
|
const swapping = swapHeld.has(pairing.sandboxId);
|
|
508
449
|
const short = context.diskHeld.has(pairingKey(pairing));
|
|
509
|
-
let
|
|
450
|
+
let heldThisPass = false;
|
|
510
451
|
try {
|
|
511
452
|
const mirrored = await servePairing(context, pairing, base);
|
|
512
453
|
rejectedPolls.delete(pairing.sandboxId);
|
|
513
454
|
unreachable.delete(pairing.sandboxId);
|
|
514
|
-
if (context.pairings
|
|
455
|
+
if (goneLingers(context.pairings, pairing.sandboxId)) {
|
|
515
456
|
await sandboxAnswered(fate, pairing.sandboxId);
|
|
516
457
|
}
|
|
517
458
|
if (context.pairings.some((held) => held.sandboxId === pairing.sandboxId && held.unreachableSince !== undefined)) {
|
|
518
459
|
await setUnreachableSince(pairing.sandboxId, undefined);
|
|
519
460
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
say(` ${pairingKey(pairing)}: reachable again; resumed its automatically paused file sync`);
|
|
461
|
+
for (const port of mirrored) {
|
|
462
|
+
claimedBy.set(port.port, pairing.sandboxId);
|
|
523
463
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
handOver(holding, pairing.sandboxId, mirrored);
|
|
529
|
-
if (tick % STRANDED_SWEEP_EVERY_TICKS === 0) {
|
|
530
|
-
sweepStrandedForwards(mutagen, pairing.sandboxId, mirrored, say);
|
|
531
|
-
}
|
|
464
|
+
handOver(holding, pairing.sandboxId, mirrored);
|
|
465
|
+
if (tick % STRANDED_SWEEP_EVERY_TICKS === 0) {
|
|
466
|
+
sweepStrandedForwards(mutagen, pairing.sandboxId, mirrored, say);
|
|
532
467
|
}
|
|
533
468
|
}
|
|
534
469
|
catch (error) {
|
|
@@ -537,49 +472,16 @@ const runPairingPass = async (context, pairing, base) => {
|
|
|
537
472
|
if (outcome.drop) {
|
|
538
473
|
return;
|
|
539
474
|
}
|
|
540
|
-
|
|
475
|
+
heldThisPass = outcome.paused;
|
|
541
476
|
say(` ${pairingKey(pairing)}: reconcile skipped: ${errorMessage(error)}`);
|
|
542
477
|
}
|
|
543
|
-
if (pairing
|
|
478
|
+
if (syncHeld({ ...pairing, unreachableSince: undefined }, Date.now()) || heldThisPass || swapping || short) {
|
|
544
479
|
stopBridge(tracking, pairing.sandboxId);
|
|
545
480
|
return;
|
|
546
481
|
}
|
|
547
482
|
await healPairing(mutagen, pairing, tick, say);
|
|
548
483
|
startBridge(tracking, pairing, tick, say);
|
|
549
484
|
};
|
|
550
|
-
export const pollingPairings = (pairings) => {
|
|
551
|
-
const polling = new Set();
|
|
552
|
-
for (const sandboxId of new Set(pairings.map((pairing) => pairing.sandboxId))) {
|
|
553
|
-
const own = pairings.filter((pairing) => pairing.sandboxId === sandboxId);
|
|
554
|
-
const poller = own.find((pairing) => !isAttachedPairing(pairing)) ?? own[0];
|
|
555
|
-
if (poller !== undefined) {
|
|
556
|
-
polling.add(pairingKey(poller));
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
return polling;
|
|
560
|
-
};
|
|
561
|
-
const followSandbox = async (context, pairing) => {
|
|
562
|
-
const { mutagen, tick, tracking, swapHeld, say } = context;
|
|
563
|
-
if (pairing.goneSince !== undefined || pairing.fileSyncPausedFor !== undefined) {
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
const down = tracking.unreachable.get(pairing.sandboxId)?.since ?? pairing.unreachableSince;
|
|
567
|
-
const swapping = swapHeld.has(pairing.sandboxId);
|
|
568
|
-
const short = context.diskHeld.has(pairingKey(pairing));
|
|
569
|
-
if (down === undefined) {
|
|
570
|
-
if (!swapping && !short && resumeAutoPausedSync(mutagen, pairing)) {
|
|
571
|
-
await setFileSyncAutoPaused(pairingKey(pairing), false);
|
|
572
|
-
say(` ${pairingKey(pairing)}: reachable again; resumed its automatically paused file sync`);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
else if (await pauseIfLongUnreachable(mutagen, pairing, Date.now() - down, say)) {
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (pairing.fileSyncAutoPaused === true || swapping || short) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
await healPairing(mutagen, pairing, tick, say);
|
|
582
|
-
};
|
|
583
485
|
const passGone = async (context, pairing) => {
|
|
584
486
|
const now = Date.now();
|
|
585
487
|
const step = goneStep(pairing, now);
|
|
@@ -600,15 +502,12 @@ const passGone = async (context, pairing) => {
|
|
|
600
502
|
tracking.unreachable.delete(pairing.sandboxId);
|
|
601
503
|
tracking.rejectedPolls.delete(pairing.sandboxId);
|
|
602
504
|
tracking.repos.delete(pairing.sandboxId);
|
|
603
|
-
|
|
604
|
-
tracking.sessionsPending.delete(key);
|
|
605
|
-
}
|
|
505
|
+
tracking.sessionsPending.delete(pairingKey(pairing));
|
|
606
506
|
}
|
|
607
507
|
return "skip";
|
|
608
508
|
};
|
|
609
|
-
const runPasses = async (context, dialed,
|
|
610
|
-
const
|
|
611
|
-
for (const { pairing, base } of dialed.filter((held) => polling.has(pairingKey(held.pairing)))) {
|
|
509
|
+
const runPasses = async (context, dialed, live) => {
|
|
510
|
+
for (const { pairing, base } of dialed) {
|
|
612
511
|
if (!live()) {
|
|
613
512
|
return;
|
|
614
513
|
}
|
|
@@ -622,12 +521,6 @@ const runPasses = async (context, dialed, pairings, live) => {
|
|
|
622
521
|
await runPairingPass(context, pairing, base);
|
|
623
522
|
context.progress(`${pairingKey(pairing)}'s pass`);
|
|
624
523
|
}
|
|
625
|
-
for (const { pairing } of dialed.filter((held) => !polling.has(pairingKey(held.pairing)))) {
|
|
626
|
-
if (!live()) {
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
await guard(context.say, `${pairingKey(pairing)}: following its sandbox`, async () => await followSandbox(context, pairing));
|
|
630
|
-
}
|
|
631
524
|
};
|
|
632
525
|
const ORPHAN_SWEEP_EVERY_TICKS = 12;
|
|
633
526
|
const CONTAINER_CHECK_EVERY_TICKS = 12;
|
|
@@ -695,7 +588,7 @@ const watchLoop = async (log, generation) => {
|
|
|
695
588
|
if ((await watchTick(watch, tick)) === "stop") {
|
|
696
589
|
return;
|
|
697
590
|
}
|
|
698
|
-
await
|
|
591
|
+
await sleepUnlessWoken(POLL_MS, generation.signal);
|
|
699
592
|
}
|
|
700
593
|
};
|
|
701
594
|
const startWatch = async (log, generation) => {
|
|
@@ -703,8 +596,15 @@ const startWatch = async (log, generation) => {
|
|
|
703
596
|
const say = quieted(log);
|
|
704
597
|
sayDaemonEventsTo(say);
|
|
705
598
|
await guard(say, "moving off Mutagen's shared data directory", async () => await moveOffSharedMutagen(mutagen, say));
|
|
599
|
+
let retired = 0;
|
|
600
|
+
await guard(say, "retiring the pairings of this computer's old sandbox for folders", async () => {
|
|
601
|
+
retired = (await retireLegacyPairings(log)).length;
|
|
602
|
+
});
|
|
706
603
|
const initial = await readState();
|
|
707
604
|
if (initial.pairings.length === 0) {
|
|
605
|
+
if (retired > 0 && startMutagenDaemon(mutagen)) {
|
|
606
|
+
await guard(say, "retiring orphaned sessions", () => retireOrphanSessions(mutagen, [], say));
|
|
607
|
+
}
|
|
708
608
|
log("no sandboxes are paired: nothing to mirror. Enable it from a sandbox's Desktop sync card.");
|
|
709
609
|
return undefined;
|
|
710
610
|
}
|
|
@@ -713,9 +613,9 @@ const startWatch = async (log, generation) => {
|
|
|
713
613
|
}
|
|
714
614
|
const sshConfig = pairingSshConfig(initial.pairings);
|
|
715
615
|
await guard(say, "refreshing the ssh configuration", async () => await writeManagedSshConfig(sshConfig));
|
|
716
|
-
const tunnels = createTunnelPool(say, generation.signal);
|
|
717
|
-
generation.stopTransports = tunnels.stopAll;
|
|
718
616
|
const bases = createDaemonBases(say);
|
|
617
|
+
const tunnels = createTunnelPool(say, generation.signal, (sandboxId) => bases.failed(sandboxId));
|
|
618
|
+
generation.stopTransports = tunnels.stopAll;
|
|
719
619
|
await guard(say, "opening the sync transports", async () => await tunnels.reconcile(tunnelTargets(await dialedPairings(initial.pairings, bases))));
|
|
720
620
|
const tracking = {
|
|
721
621
|
rejectedPolls: new Map(),
|
|
@@ -726,7 +626,7 @@ const startWatch = async (log, generation) => {
|
|
|
726
626
|
};
|
|
727
627
|
generation.signal.addEventListener("abort", () => stopBridges(tracking), { once: true });
|
|
728
628
|
const reportUnsupported = new Set();
|
|
729
|
-
const fate = {
|
|
629
|
+
const fate = { releaseForwards: async (sandboxId) => await retirePairingMirror(mutagen, sandboxId), say };
|
|
730
630
|
const gone = async (pairing) => await sandboxSaidGone(fate, pairing.sandboxId, "edge");
|
|
731
631
|
const watch = {
|
|
732
632
|
log,
|
|
@@ -745,11 +645,10 @@ const startWatch = async (log, generation) => {
|
|
|
745
645
|
reportUnsupported,
|
|
746
646
|
fate,
|
|
747
647
|
gone,
|
|
748
|
-
announce: announcer(mutagen, bases, reportUnsupported, tracking.unreachable, gone, say),
|
|
749
648
|
holders: { dockerPorts: publishedPortsReader(say), freeSince: new Map() },
|
|
750
649
|
sshConfig,
|
|
751
650
|
};
|
|
752
|
-
await prepareSessions(mutagen, initial.pairings, watch.sessionsPrepared, tracking.sessionsPending,
|
|
651
|
+
await prepareSessions(mutagen, initial.pairings, watch.sessionsPrepared, tracking.sessionsPending, { fate, relabels: relabelBudget(1), tracking }, say);
|
|
753
652
|
await guard(say, "retiring orphaned sessions", () => retireOrphanSessions(mutagen, initial.pairings, say));
|
|
754
653
|
log(`sync started; polling ${plural(initial.pairings.length, "paired sandbox")} every ${POLL_MS / 1000}s`);
|
|
755
654
|
return watch;
|
|
@@ -779,7 +678,7 @@ const watchTick = async (watch, tick) => {
|
|
|
779
678
|
await guard(say, "reconciling the sync transports", async () => await watch.tunnels.reconcile(tunnelTargets(dialed)));
|
|
780
679
|
progress("the sync transports");
|
|
781
680
|
const relabels = relabelBudget(1);
|
|
782
|
-
await prepareSessions(mutagen, state.pairings, watch.sessionsPrepared, tracking.sessionsPending,
|
|
681
|
+
await prepareSessions(mutagen, state.pairings, watch.sessionsPrepared, tracking.sessionsPending, { fate, relabels, tracking }, say);
|
|
783
682
|
await retryPendingSessions(mutagen, state.pairings, tracking.sessionsPending, tick, relabels, say);
|
|
784
683
|
progress("preparing file sync");
|
|
785
684
|
if (!watch.live()) {
|
|
@@ -787,11 +686,11 @@ const watchTick = async (watch, tick) => {
|
|
|
787
686
|
}
|
|
788
687
|
let swapHeld = new Set();
|
|
789
688
|
await guard(say, "holding file sync still during swaps", async () => {
|
|
790
|
-
swapHeld = await holdSyncDuringSwaps(
|
|
689
|
+
swapHeld = await holdSyncDuringSwaps(state.pairings, tracking.unreachable, say);
|
|
791
690
|
});
|
|
792
691
|
let diskHeld = new Set();
|
|
793
692
|
await guard(say, "measuring the drives synced folders are on", async () => {
|
|
794
|
-
diskHeld = await guardDisks(
|
|
693
|
+
diskHeld = await guardDisks(state.pairings, say);
|
|
795
694
|
});
|
|
796
695
|
stopBridges(tracking, state.pairings);
|
|
797
696
|
const context = {
|
|
@@ -810,10 +709,12 @@ const watchTick = async (watch, tick) => {
|
|
|
810
709
|
pairings: state.pairings,
|
|
811
710
|
say,
|
|
812
711
|
};
|
|
813
|
-
await runPasses(context, dialed,
|
|
712
|
+
await runPasses(context, dialed, watch.live);
|
|
814
713
|
if (!watch.live()) {
|
|
815
714
|
return "stop";
|
|
816
715
|
}
|
|
716
|
+
await guard(say, "holding file sync still where something holds it", async () => void (await holdSessions(mutagen, (await readState()).pairings, say)));
|
|
717
|
+
progress("holding file sync");
|
|
817
718
|
if (tick % REPORT_EVERY_TICKS === 0) {
|
|
818
719
|
await guard(say, "posting this machine's reports", async () => await postReports(dialed, mutagen, watch.reportUnsupported, watch.gone, say));
|
|
819
720
|
}
|
|
@@ -831,7 +732,7 @@ const teardownForwards = async (mutagen, sandboxId) => {
|
|
|
831
732
|
await runProcess(mutagen, ["forward", "terminate", ...names], { timeoutMs: MUTAGEN_CALL_TIMEOUT_MS });
|
|
832
733
|
}
|
|
833
734
|
await updateState((state) => ({
|
|
834
|
-
pairings: state.pairings.map((held) =>
|
|
735
|
+
pairings: state.pairings.map((held) => sandboxId === undefined || held.sandboxId === sandboxId
|
|
835
736
|
? { ...held, mirroredPorts: [], skippedPorts: [] }
|
|
836
737
|
: held),
|
|
837
738
|
}));
|
|
@@ -843,7 +744,7 @@ export const retireMirroredPort = async (mutagen, sandboxId, port) => {
|
|
|
843
744
|
let wasMirrored = false;
|
|
844
745
|
await updateState((state) => ({
|
|
845
746
|
pairings: state.pairings.map((held) => {
|
|
846
|
-
if (held.sandboxId !== sandboxId
|
|
747
|
+
if (held.sandboxId !== sandboxId) {
|
|
847
748
|
return held;
|
|
848
749
|
}
|
|
849
750
|
const mirrored = held.mirroredPorts ?? [];
|