mainflow 0.44.0-alpha.1 → 0.44.0-alpha.2
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/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { P as UNTIL_FORMS, X as parseUntil, Y as parseRef, Z as refExample, ct as stampMs, it as untilProblem, n as CLOSED_STATUS } from "./dist-CXrUfRQp.js";
|
|
3
3
|
import { $t as realPath, Bt as notRegisteredMessage, Ft as requireSlug, Gt as resolveHub, Ht as registerHub, It as hubAtRoot, Jt as nodeStorage, Kt as unregisterHub, Lt as hubHoldingFile, Mt as MAINLINE_FILE, O as loadSessions, Pt as TASK_FILE, Q as HubError, S as sessionBriefing, Ut as registeredName, Vt as readRegistry, Wt as registryFile, X as applyOperation, Xt as dataFile, Z as checkHubName, b as untilState, d as migrateHubData, f as migrateHub, ft as nowMinute, g as RETRY_RULES, gt as BLOB_HASH, h as receiverFromEnv, k as readSession, lt as remoteMembers, m as retroReportFromReceiver, nn as DATA_DIR, p as retroReport, qt as userDataDir, rn as WriteError, rt as sectionText, st as memberOfGroup, tt as loadHub, u as groupPlan, ut as unavailableHubs, vt as MAX_CHANGE_PAGE, x as duplicateClaimLines, y as initHub, yt as MAX_FILE_BYTES, zt as listHubs } from "./host-C74j2Qox.js";
|
|
4
4
|
import { t as listProgress } from "./progress-1BfPg2zN.js";
|
|
5
|
-
import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-
|
|
5
|
+
import { A as hubName, C as readRemote, D as Unreachable, E as ServerError, F as syncPaths, M as SETTINGS_FILE, O as ENV, T as ServerApi, _ as DaemonUnavailable, a as versionGate, b as readDaemon, c as oversizedFiles, d as planAnnounce, f as planFetch, g as DaemonRunning, h as startDaemon, i as requireWritableVersion, j as syncSettings, k as earlierToken, l as fetchDenial, m as toolCallOf, n as hubWriter, o as queuedCount, p as planFlush, r as writtenLines, s as remoteMode, t as CONFIRM_MS, u as hookAnswer, v as callDaemon, w as HubRewritingError, x as silentHint, y as probeDaemon } from "./operation-CUeOFEaf.js";
|
|
6
6
|
import { COMMAND_SPECS, OPTIONS } from "./facts.js";
|
|
7
7
|
import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
|
|
8
8
|
import { hookFallback, parsePayload, runHook } from "./hooks.js";
|
package/dist/grant-check.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { D as SESSION_ID, I as configSchema, ft as describeRequest, gt as hasControl, tt as repoPaths } from "./dist-CXrUfRQp.js";
|
|
2
2
|
import { $t as realPath, Gt as resolveHub, Jt as nodeStorage, M as matchGrants, Ot as formatMismatch, Yt as currentBranch, Zt as gitCheckout, j as grantScope, k as readSession, kt as hubFormatVersion, mt as readYaml } from "./host-C74j2Qox.js";
|
|
3
|
-
import { n as hubWriter, r as writtenLines } from "./operation-
|
|
3
|
+
import { n as hubWriter, r as writtenLines } from "./operation-CUeOFEaf.js";
|
|
4
4
|
import { OWNER_REPO, githubOrigin, quote, readManifest, resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
|
|
5
5
|
import { claudeCodeSessionId, parsePayload } from "./hooks.js";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
package/dist/hooks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { I as configSchema, Y as parseRef, ft as describeRequest, rt as taskSchema, tt as repoPaths } from "./dist-CXrUfRQp.js";
|
|
2
2
|
import { $ as isOpenMainline, $t as realPath, Bt as notRegisteredMessage, C as turnReminder, F as headerLine, Gt as resolveHub, I as mayDecorate, Jt as nodeStorage, Lt as hubHoldingFile, M as matchGrants, N as decorateReply, Nt as SESSIONS_DIR, O as loadSessions, Ot as formatMismatch, P as endsWithMarker, Q as HubError, Qt as hubPath, Rt as inRepositoryHubAt, S as sessionBriefing, Xt as dataFile, Yt as currentBranch, dt as taskFile, en as worktreesOf, i as hostName, j as grantScope, k as readSession, kt as hubFormatVersion, mt as readYaml, nn as DATA_DIR, nt as loadMainlines, pt as readFrontMatter, tt as loadHub } from "./host-C74j2Qox.js";
|
|
3
|
-
import { n as hubWriter, r as writtenLines } from "./operation-
|
|
3
|
+
import { n as hubWriter, r as writtenLines } from "./operation-CUeOFEaf.js";
|
|
4
4
|
import { SCENARIO_LINES, SCENARIO_SKIP_KINDS } from "./facts.js";
|
|
5
5
|
import { resolveFixed, scanCommand, scopedCwd } from "./grant-commands.js";
|
|
6
6
|
import { createHash } from "node:crypto";
|
package/dist/launch.js
CHANGED
|
@@ -273,7 +273,7 @@ async function launchUi(dirs, hub, configured, options) {
|
|
|
273
273
|
}
|
|
274
274
|
let server;
|
|
275
275
|
if (!reuse) {
|
|
276
|
-
const { serveUi } = await import("./ui-
|
|
276
|
+
const { serveUi } = await import("./ui-qqVCewFz.js");
|
|
277
277
|
server = await serveUi(dirs, choice.port);
|
|
278
278
|
writeUiRecord(dirs, {
|
|
279
279
|
port: choice.port,
|
|
@@ -920,6 +920,11 @@ const readKnownDirs = (hub) => {
|
|
|
920
920
|
const dirs = readJson(syncPaths(hub).known, {}).dirs;
|
|
921
921
|
return Array.isArray(dirs) ? dirs.filter((dir) => typeof dir === "string") : [];
|
|
922
922
|
};
|
|
923
|
+
/** The hub's seq known.json names (KnownFile.seq); undefined when it names none, or none that is a seq. */
|
|
924
|
+
const readKnownSeq = (hub) => {
|
|
925
|
+
const seq = readJson(syncPaths(hub).known, {}).seq;
|
|
926
|
+
return Number.isInteger(seq) && seq >= 0 ? seq : void 0;
|
|
927
|
+
};
|
|
923
928
|
/** What known.json writes before a binary file's sha256, apart from a text's content hash. */
|
|
924
929
|
const BLOB_PREFIX = "blob:";
|
|
925
930
|
/** The text of the version known.json names by `hash`; undefined when it was not kept, as by a sync client before known/. */
|
|
@@ -936,9 +941,9 @@ function readKnownText(dirs, hash) {
|
|
|
936
941
|
* there yet; the texts of versions it no longer names are removed. A version whose text the client does not have keeps
|
|
937
942
|
* the text known/ holds, if any. The new texts go first, then known.json, then the removals, so a client killed at any
|
|
938
943
|
* step leaves a known.json whose every text is in known/. A binary file is given as its sha256 with BLOB_PREFIX, and no text.
|
|
939
|
-
* `dirs` are the directories it names.
|
|
944
|
+
* `dirs` are the directories it names, and `seq` the hub's seq they are as of, written with them (KnownFile.seq).
|
|
940
945
|
*/
|
|
941
|
-
function writeKnown(hub, files, dirs = []) {
|
|
946
|
+
function writeKnown(hub, files, dirs = [], seq) {
|
|
942
947
|
const dir = syncPaths(hub).knownTexts;
|
|
943
948
|
mkdirSync(dir, { recursive: true });
|
|
944
949
|
const named = new Set(Object.values(files).map((file) => file.hash));
|
|
@@ -951,7 +956,8 @@ function writeKnown(hub, files, dirs = []) {
|
|
|
951
956
|
}
|
|
952
957
|
const known = {
|
|
953
958
|
files: Object.fromEntries(Object.entries(files).map(([path, file]) => [path, file.hash])),
|
|
954
|
-
...dirs.length > 0 ? { dirs } : {}
|
|
959
|
+
...dirs.length > 0 ? { dirs } : {},
|
|
960
|
+
...seq === void 0 ? {} : { seq }
|
|
955
961
|
};
|
|
956
962
|
writeJson(syncPaths(hub).known, known);
|
|
957
963
|
for (const name of held) if (!named.has(name)) rmSync(join(dir, name), { force: true });
|
|
@@ -1656,7 +1662,8 @@ var SyncClient = class extends EventEmitter {
|
|
|
1656
1662
|
* every directory, but for the paths changed after it: a whole snapshot with binary files was taken in this run, and
|
|
1657
1663
|
* nothing since made the client unsure of what it holds, as a server that stopped keeping binary files, or a hub
|
|
1658
1664
|
* reloaded. Only then is a snapshot asked for what changed since hubSeq (`since`), as a path the answer does not name
|
|
1659
|
-
* holds what the client takes the server to hold.
|
|
1665
|
+
* holds what the client takes the server to hold. A run after it starts from known.json instead, which names the seq
|
|
1666
|
+
* (knownSeq, resumeSeq).
|
|
1660
1667
|
*/
|
|
1661
1668
|
holdsHub = false;
|
|
1662
1669
|
/**
|
|
@@ -1664,6 +1671,20 @@ var SyncClient = class extends EventEmitter {
|
|
|
1664
1671
|
* not leave the client holding the hub (holdsHub), whatever it took in, so the next one is whole.
|
|
1665
1672
|
*/
|
|
1666
1673
|
reloads = 0;
|
|
1674
|
+
/**
|
|
1675
|
+
* The hub's seq known.json named when this run started (readKnownSeq): the files it names, its directories and
|
|
1676
|
+
* remote.json are the hub as of it, so the run's first snapshot asks for what changed since, as one after a reconnect
|
|
1677
|
+
* does (holdsHub). Undefined once a snapshot was taken, or when known.json names none or the run cannot take it, as
|
|
1678
|
+
* when a text it names is not in known/.
|
|
1679
|
+
*/
|
|
1680
|
+
resumeSeq;
|
|
1681
|
+
/**
|
|
1682
|
+
* For each path, a seq at or before which the hub held another version than the one the client knows of now (in
|
|
1683
|
+
* `cloud`, or `remote`): a broadcast's own seq, or, for a snapshot's, one after the seq of the version before it. A
|
|
1684
|
+
* path whose known.json version lags behind is changed on the server after the seq known.json is written with
|
|
1685
|
+
* (knownSeq), so what changed since names it.
|
|
1686
|
+
*/
|
|
1687
|
+
changedAt = /* @__PURE__ */ new Map();
|
|
1667
1688
|
/** The seqs of the broadcasts on the current connection above hubSeq with a change between that never arrived */
|
|
1668
1689
|
ahead = /* @__PURE__ */ new Set();
|
|
1669
1690
|
waiters = /* @__PURE__ */ new Set();
|
|
@@ -1861,6 +1882,7 @@ var SyncClient = class extends EventEmitter {
|
|
|
1861
1882
|
async restoreKnown() {
|
|
1862
1883
|
this.copied = !existsSync(this.paths.known);
|
|
1863
1884
|
this.blobsCompared = !this.copied && !existsSync(this.paths.copyBlobs);
|
|
1885
|
+
this.resumeSeq = this.blobsCompared ? readKnownSeq(this.dirs) : void 0;
|
|
1864
1886
|
for (const dir of readKnownDirs(this.dirs)) this.syncedDirs.add(dir);
|
|
1865
1887
|
for (const [path, hash] of Object.entries(readKnown(this.dirs))) {
|
|
1866
1888
|
if (hash.startsWith("blob:")) {
|
|
@@ -1884,7 +1906,9 @@ var SyncClient = class extends EventEmitter {
|
|
|
1884
1906
|
text,
|
|
1885
1907
|
server: text
|
|
1886
1908
|
});
|
|
1909
|
+
else if (this.syncs(path)) this.resumeSeq = void 0;
|
|
1887
1910
|
}
|
|
1911
|
+
if (this.resumeSeq !== void 0) this.log(`known.json is of the hub's seq ${this.resumeSeq}: the first snapshot asks for what changed since`);
|
|
1888
1912
|
}
|
|
1889
1913
|
/**
|
|
1890
1914
|
* The refusals a run killed before it settled them left in the outbox (src/state.ts): each text not saved yet is saved,
|
|
@@ -2118,12 +2142,13 @@ var SyncClient = class extends EventEmitter {
|
|
|
2118
2142
|
if ("reload" in message && message.reload) {
|
|
2119
2143
|
this.reloads++;
|
|
2120
2144
|
this.holdsHub = false;
|
|
2145
|
+
this.resumeSeq = void 0;
|
|
2121
2146
|
this.track(this.resync());
|
|
2122
2147
|
return;
|
|
2123
2148
|
}
|
|
2124
2149
|
for (const event of message.files) if (event.kind === "dir") this.receiveDir(event.path, seq, true);
|
|
2125
2150
|
else if (event.kind === "dirRemoved") this.receiveDirRemoved(event.path, seq);
|
|
2126
|
-
else this.receive(event, seq);
|
|
2151
|
+
else this.receive(event, seq, true);
|
|
2127
2152
|
if (this.ahead.size > 0 && this.synced && this.resyncing === null) {
|
|
2128
2153
|
this.log(`missed the hub's changes ${this.hubSeq + 1} to ${Math.min(...this.ahead) - 1}; reloading the snapshot`);
|
|
2129
2154
|
this.track(this.resync());
|
|
@@ -2144,7 +2169,7 @@ var SyncClient = class extends EventEmitter {
|
|
|
2144
2169
|
* that may stay on the server, as a new screenshot of a closed mainline, goes into `remote` instead of being downloaded,
|
|
2145
2170
|
* as a listed text does in a snapshot.
|
|
2146
2171
|
*/
|
|
2147
|
-
receive(event, seq) {
|
|
2172
|
+
receive(event, seq, live = false) {
|
|
2148
2173
|
if (!this.syncs(event.path)) return;
|
|
2149
2174
|
const file = event.kind === "file" ? event.text : event.kind === "blob" ? {
|
|
2150
2175
|
kind: "blob",
|
|
@@ -2157,6 +2182,7 @@ var SyncClient = class extends EventEmitter {
|
|
|
2157
2182
|
this.trace(`older ${event.path} ${label} seq ${seq}, holding seq ${held}`);
|
|
2158
2183
|
return;
|
|
2159
2184
|
}
|
|
2185
|
+
this.noteChange(event.path, file === null ? null : serverHash(file), live ? seq : held + 1);
|
|
2160
2186
|
this.fileSeq.set(event.path, seq);
|
|
2161
2187
|
if (this.downloadErrors.has(event.path)) {
|
|
2162
2188
|
const before = this.cloud.get(event.path);
|
|
@@ -2238,6 +2264,16 @@ var SyncClient = class extends EventEmitter {
|
|
|
2238
2264
|
forgetDir(path) {
|
|
2239
2265
|
if (this.syncedDirs.delete(path)) this.saveKnown();
|
|
2240
2266
|
}
|
|
2267
|
+
/** The hash the client knows the server's file at `path` by: of its version in `cloud`, or `remote`; null for none. */
|
|
2268
|
+
serverView(path) {
|
|
2269
|
+
const file = this.cloud.get(path);
|
|
2270
|
+
if (file !== void 0 && file !== null) return serverHash(file);
|
|
2271
|
+
return this.remote.get(path) ?? null;
|
|
2272
|
+
}
|
|
2273
|
+
/** The server's file at `path` is to be known by `hash` (null for none), a change of the hub at seq `at` or later (changedAt). */
|
|
2274
|
+
noteChange(path, hash, at) {
|
|
2275
|
+
if (this.serverView(path) !== hash) this.changedAt.set(path, at);
|
|
2276
|
+
}
|
|
2241
2277
|
/** Whether the client holds `path` as of the hub's `seq` or later. */
|
|
2242
2278
|
reached(path, seq) {
|
|
2243
2279
|
return this.hubSeq >= seq || (this.fileSeq.get(path) ?? -1) >= seq;
|
|
@@ -2381,7 +2417,8 @@ var SyncClient = class extends EventEmitter {
|
|
|
2381
2417
|
* since hubSeq alone (`since`). A server whose change log tells answers with the paths changed (SnapshotDelta): taken
|
|
2382
2418
|
* in over what the client holds once they make the hub's digest (changesHold, takeChanges), and else dropped for the
|
|
2383
2419
|
* whole snapshot. A server that cannot tell, or one from before it, answers the first batch, and the snapshot goes on
|
|
2384
|
-
* whole as ever.
|
|
2420
|
+
* whole as ever. A run's first snapshot asks the same way from the seq known.json names (resumeSeq), taking known.json,
|
|
2421
|
+
* remote.json and the directories known.json names for what the server held as of it (knownCopy).
|
|
2385
2422
|
*/
|
|
2386
2423
|
takeSnapshot() {
|
|
2387
2424
|
return this.exclusive(() => this.snapshotNow());
|
|
@@ -2406,13 +2443,15 @@ var SyncClient = class extends EventEmitter {
|
|
|
2406
2443
|
/** The reload broadcasts that came before this snapshot: one that comes while it runs leaves the next one whole */
|
|
2407
2444
|
const reloads = this.reloads;
|
|
2408
2445
|
/**
|
|
2409
|
-
* Whether the snapshot may ask for what changed since hubSeq
|
|
2446
|
+
* Whether the snapshot may ask for what changed since a seq: hubSeq while the client holds the hub, or the seq
|
|
2447
|
+
* known.json names before the run's first snapshot (resumeSeq). Read once the probe told whether the server keeps
|
|
2410
2448
|
* binary files; not once an answer of it failed the digest
|
|
2411
2449
|
*/
|
|
2412
|
-
let askChanges = this.holdsHub;
|
|
2450
|
+
let askChanges = this.holdsHub || this.resumeSeq !== void 0;
|
|
2413
2451
|
for (;;) {
|
|
2414
2452
|
before = this.hubSeq;
|
|
2415
|
-
const
|
|
2453
|
+
const from = !askChanges || this.blobSupport !== true ? null : this.holdsHub ? before >= 0 ? before : null : this.resumeSeq ?? null;
|
|
2454
|
+
const base = from === null ? null : this.holdsHub ? this.serverCopy() : this.knownCopy();
|
|
2416
2455
|
({texts: held, blobs: heldBlobs} = await this.holdings());
|
|
2417
2456
|
let dropped = false;
|
|
2418
2457
|
for (const path of this.remote.keys()) {
|
|
@@ -2433,7 +2472,7 @@ var SyncClient = class extends EventEmitter {
|
|
|
2433
2472
|
value: hubDigest(have, dirs),
|
|
2434
2473
|
dirs
|
|
2435
2474
|
} : void 0;
|
|
2436
|
-
since = base !== null && withBlobs && !dropped ?
|
|
2475
|
+
since = base !== null && from !== null && withBlobs && !dropped ? from : void 0;
|
|
2437
2476
|
const taken = await this.api.snapshot(have, {
|
|
2438
2477
|
listOnly: this.listOnly(this.copied && !this.synced),
|
|
2439
2478
|
blobs: withBlobs,
|
|
@@ -2583,6 +2622,8 @@ var SyncClient = class extends EventEmitter {
|
|
|
2583
2622
|
this.takenUpTo(seq);
|
|
2584
2623
|
this.synced = true;
|
|
2585
2624
|
this.holdsHub = withBlobs && this.reloads === reloads;
|
|
2625
|
+
this.resumeSeq = void 0;
|
|
2626
|
+
this.saveKnown();
|
|
2586
2627
|
if (withBlobs && !this.blobsCompared) {
|
|
2587
2628
|
this.blobsCompared = true;
|
|
2588
2629
|
rmSync(this.paths.copyBlobs, { force: true });
|
|
@@ -2597,6 +2638,32 @@ var SyncClient = class extends EventEmitter {
|
|
|
2597
2638
|
await this.queueRemovedDirs();
|
|
2598
2639
|
}
|
|
2599
2640
|
/**
|
|
2641
|
+
* What the client takes the server to hold as of the seq known.json names, before the run's first snapshot
|
|
2642
|
+
* (resumeSeq): the version known.json names of each file, but one only on the server (remote.json), and the
|
|
2643
|
+
* directories it names; with what a broadcast taken in since the run started holds, which is of a later seq.
|
|
2644
|
+
*/
|
|
2645
|
+
knownCopy() {
|
|
2646
|
+
const files = /* @__PURE__ */ new Map();
|
|
2647
|
+
for (const [path, disk] of this.confirmed) {
|
|
2648
|
+
if (!disk || !this.syncs(path) || this.remote.has(path)) continue;
|
|
2649
|
+
if (disk.blob) files.set(path, {
|
|
2650
|
+
kind: "blob",
|
|
2651
|
+
hash: disk.hash
|
|
2652
|
+
});
|
|
2653
|
+
else if (disk.text !== void 0) files.set(path, disk.text);
|
|
2654
|
+
}
|
|
2655
|
+
for (const [path, file] of this.cloud) {
|
|
2656
|
+
if (!this.syncs(path)) continue;
|
|
2657
|
+
if (file === null) files.delete(path);
|
|
2658
|
+
else files.set(path, file);
|
|
2659
|
+
}
|
|
2660
|
+
return {
|
|
2661
|
+
files,
|
|
2662
|
+
remote: new Map(this.remote),
|
|
2663
|
+
dirs: new Set(this.syncedDirs)
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2600
2667
|
* What the client takes the server to hold, while it holds the hub (holdsHub): the server's version of each file it
|
|
2601
2668
|
* took in, a binary file's included, the hash of each file only on the server, and the directories the server holds.
|
|
2602
2669
|
* A file here the server never had is not among them, though a snapshot request names it.
|
|
@@ -3237,7 +3304,9 @@ var SyncClient = class extends EventEmitter {
|
|
|
3237
3304
|
}
|
|
3238
3305
|
/** Note `path` as only on the server, with the hash of the server's text as of `seq`, unless a newer version came. */
|
|
3239
3306
|
keepRemote(path, hash, seq) {
|
|
3240
|
-
|
|
3307
|
+
const held = this.fileSeq.get(path) ?? -1;
|
|
3308
|
+
if (seq <= held) return;
|
|
3309
|
+
this.noteChange(path, hash, held + 1);
|
|
3241
3310
|
this.fileSeq.set(path, seq);
|
|
3242
3311
|
this.remote.set(path, hash);
|
|
3243
3312
|
this.cloud.delete(path);
|
|
@@ -5688,12 +5757,39 @@ var SyncClient = class extends EventEmitter {
|
|
|
5688
5757
|
this.knownTimer = void 0;
|
|
5689
5758
|
const files = this.knownFiles();
|
|
5690
5759
|
const dirs = this.knownDirs();
|
|
5760
|
+
const seq = this.knownSeq();
|
|
5761
|
+
if (seq !== void 0) this.saveRemoteNow(final);
|
|
5691
5762
|
try {
|
|
5692
|
-
underLock(this.dirs, () => writeKnown(this.dirs, files, dirs));
|
|
5763
|
+
underLock(this.dirs, () => writeKnown(this.dirs, files, dirs, seq));
|
|
5693
5764
|
} catch (error) {
|
|
5694
5765
|
this.log(`could not save local/sync/known.json: ${error.message}`);
|
|
5695
5766
|
}
|
|
5696
5767
|
}
|
|
5768
|
+
/**
|
|
5769
|
+
* The hub's seq known.json is written with (KnownFile.seq): one as of which the files it names, its directories and
|
|
5770
|
+
* remote.json are the hub's, but for paths changed on the server after it, so that what changed since names every
|
|
5771
|
+
* path whose version there differs. Only while the client holds the hub (holdsHub) and no snapshot is under way, as
|
|
5772
|
+
* the files it is taking in are of later seqs; and none while a listed file is being downloaded. It is hubSeq, or
|
|
5773
|
+
* lower when a file's version known.json names is not the server's the client knows of, as one the outbox holds a
|
|
5774
|
+
* write about while the server changed it: one before that change (changedAt). Undefined when there is none.
|
|
5775
|
+
*/
|
|
5776
|
+
knownSeq() {
|
|
5777
|
+
if (!this.holdsHub || this.hubSeq < 0 || this.resyncing !== null || this.awaiting.size > 0) return void 0;
|
|
5778
|
+
const listed = this.listedRemote();
|
|
5779
|
+
let seq = this.hubSeq;
|
|
5780
|
+
for (const path of /* @__PURE__ */ new Set([
|
|
5781
|
+
...this.cloud.keys(),
|
|
5782
|
+
...this.confirmed.keys(),
|
|
5783
|
+
...this.remote.keys()
|
|
5784
|
+
])) {
|
|
5785
|
+
if (!this.syncs(path)) continue;
|
|
5786
|
+
const disk = this.confirmed.get(path);
|
|
5787
|
+
if ((listed.get(path) ?? (disk ? disk.hash : null)) === this.serverView(path)) continue;
|
|
5788
|
+
seq = Math.min(seq, (this.changedAt.get(path) ?? 0) - 1);
|
|
5789
|
+
if (seq < 0) return void 0;
|
|
5790
|
+
}
|
|
5791
|
+
return seq;
|
|
5792
|
+
}
|
|
5697
5793
|
};
|
|
5698
5794
|
//#endregion
|
|
5699
5795
|
//#region ../sync/dist/ipc.js
|
|
@@ -5724,30 +5820,75 @@ var DaemonUnavailable = class extends Error {
|
|
|
5724
5820
|
};
|
|
5725
5821
|
/** Another sync client already runs for the hub. */
|
|
5726
5822
|
var DaemonRunning = class extends Error {};
|
|
5823
|
+
/** The answer's body could not be read to its end, after its status came. */
|
|
5824
|
+
var BodyUnread = class extends Error {};
|
|
5825
|
+
/**
|
|
5826
|
+
* One request to the sync client on the loopback interface, with the status and the whole body of its answer. It uses
|
|
5827
|
+
* node:http, taken when first called, rather than fetch: fetch loads undici and with it tls, and a process with
|
|
5828
|
+
* NODE_USE_SYSTEM_CA set reads the system's certificates when tls loads, which costs every hook that writes about 140 ms.
|
|
5829
|
+
* `agent: false` opens a connection for this request alone, so no kept-alive socket holds a short-lived command open.
|
|
5830
|
+
* No answer within `timeoutMs` rejects with an error named TimeoutError, which callDaemon takes as a silent sync client;
|
|
5831
|
+
* the time running out while the body is read rejects with BodyUnread, as does any failure reading it.
|
|
5832
|
+
*/
|
|
5833
|
+
function loopbackRequest(port, path, headers, body, timeoutMs) {
|
|
5834
|
+
return new Promise((resolve, reject) => {
|
|
5835
|
+
let answered = false;
|
|
5836
|
+
const request = process.getBuiltinModule("node:http").request({
|
|
5837
|
+
host: "127.0.0.1",
|
|
5838
|
+
port,
|
|
5839
|
+
path,
|
|
5840
|
+
method: body === void 0 ? "GET" : "POST",
|
|
5841
|
+
headers,
|
|
5842
|
+
agent: false
|
|
5843
|
+
}, (response) => {
|
|
5844
|
+
answered = true;
|
|
5845
|
+
const chunks = [];
|
|
5846
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
5847
|
+
response.on("end", () => {
|
|
5848
|
+
clearTimeout(timer);
|
|
5849
|
+
resolve({
|
|
5850
|
+
status: response.statusCode ?? 0,
|
|
5851
|
+
text: Buffer.concat(chunks).toString("utf8")
|
|
5852
|
+
});
|
|
5853
|
+
});
|
|
5854
|
+
response.on("error", (error) => {
|
|
5855
|
+
clearTimeout(timer);
|
|
5856
|
+
reject(new BodyUnread(error.message));
|
|
5857
|
+
});
|
|
5858
|
+
});
|
|
5859
|
+
const timer = setTimeout(() => {
|
|
5860
|
+
const error = answered ? new BodyUnread("the answer did not end in time") : Object.assign(/* @__PURE__ */ new Error("The operation was aborted due to timeout"), { name: "TimeoutError" });
|
|
5861
|
+
request.destroy(error);
|
|
5862
|
+
reject(error);
|
|
5863
|
+
}, timeoutMs);
|
|
5864
|
+
request.on("error", (error) => {
|
|
5865
|
+
clearTimeout(timer);
|
|
5866
|
+
reject(answered ? new BodyUnread(error.message) : error);
|
|
5867
|
+
});
|
|
5868
|
+
request.end(body);
|
|
5869
|
+
});
|
|
5870
|
+
}
|
|
5727
5871
|
async function callDaemon(daemon, route, body, timeoutMs = 6e4) {
|
|
5872
|
+
const notSync = () => new DaemonUnavailable(`what answers on port ${daemon.port} is not mainflow sync (pid ${daemon.pid})`);
|
|
5728
5873
|
let response;
|
|
5729
5874
|
try {
|
|
5730
|
-
response = await
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
"content-type": "application/json"
|
|
5735
|
-
},
|
|
5736
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
5737
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
5738
|
-
});
|
|
5875
|
+
response = await loopbackRequest(daemon.port, route, {
|
|
5876
|
+
[SECRET_HEADER]: daemon.secret,
|
|
5877
|
+
"content-type": "application/json"
|
|
5878
|
+
}, body === void 0 ? void 0 : JSON.stringify(body), timeoutMs);
|
|
5739
5879
|
} catch (error) {
|
|
5880
|
+
if (error instanceof BodyUnread) throw notSync();
|
|
5740
5881
|
const silent = error.name === "TimeoutError";
|
|
5741
5882
|
throw new DaemonUnavailable(`mainflow sync (pid ${daemon.pid}) does not answer: ${error.message}`, silent);
|
|
5742
5883
|
}
|
|
5743
5884
|
let parsed;
|
|
5744
5885
|
try {
|
|
5745
|
-
parsed =
|
|
5886
|
+
parsed = JSON.parse(response.text);
|
|
5746
5887
|
} catch {
|
|
5747
|
-
throw
|
|
5888
|
+
throw notSync();
|
|
5748
5889
|
}
|
|
5749
5890
|
if (response.status === 403) throw new DaemonUnavailable(`what answers on port ${daemon.port} is not this hub's mainflow sync (pid ${daemon.pid})`);
|
|
5750
|
-
if (
|
|
5891
|
+
if (response.status < 200 || response.status > 299) throw new Error(`mainflow sync failed: ${parsed.error}`);
|
|
5751
5892
|
return parsed;
|
|
5752
5893
|
}
|
|
5753
5894
|
/** How long a probe waits for the sync client's answer; a client holding the write lock answers after it. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { I as configSchema, tt as repoPaths } from "./dist-CXrUfRQp.js";
|
|
2
2
|
import { A as transcriptPath, D as readNotices$1, E as parseWarn, It as hubAtRoot, L as attributeTrace, O as loadSessions, Q as HubError, Qt as hubPath, R as traceTranscript, T as markNoticesHandled, Xt as dataFile, _ as ARCHIVE_KINDS, a as openInApp, at as mainlineEdges, ct as refIndex, et as loadConfig, it as dependencyGraph, k as readSession, lt as remoteMembers, mt as readYaml, nn as DATA_DIR, ot as linksInto, rn as WriteError, rt as sectionText, tn as ConflictError, tt as loadHub, u as groupPlan, v as setArchivedMany, w as HANDLED_FILE, z as transcriptTitle } from "./host-C74j2Qox.js";
|
|
3
3
|
import { t as listProgress } from "./progress-1BfPg2zN.js";
|
|
4
|
-
import { C as readRemote, F as syncPaths, N as SYNC_DIR, P as isSynced, S as watchTree, _ as DaemonUnavailable, b as readDaemon, n as hubWriter, r as writtenLines, s as remoteMode, v as callDaemon } from "./operation-
|
|
4
|
+
import { C as readRemote, F as syncPaths, N as SYNC_DIR, P as isSynced, S as watchTree, _ as DaemonUnavailable, b as readDaemon, n as hubWriter, r as writtenLines, s as remoteMode, v as callDaemon } from "./operation-CUeOFEaf.js";
|
|
5
5
|
import { t as CLI_VERSION } from "./version-Dvv4jwF1.js";
|
|
6
6
|
import { readUiRecord, realRoot } from "./launch.js";
|
|
7
7
|
import { createRequire } from "node:module";
|