amalgm 0.1.245 → 0.1.246
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 +12 -0
- package/lib/cli.js +146 -8
- package/lib/layout.js +89 -4
- package/lib/shared-realtime-tunnel.js +384 -43
- package/lib/supervisor.js +0 -3
- package/package.json +2 -2
- package/runtime/scripts/amalgm-mcp/adapters/contract.js +197 -0
- package/runtime/scripts/amalgm-mcp/adapters/filesystem.js +135 -0
- package/runtime/scripts/amalgm-mcp/adapters/git.js +84 -0
- package/runtime/scripts/amalgm-mcp/adapters/index.js +43 -0
- package/runtime/scripts/amalgm-mcp/adapters/reference.js +31 -0
- package/runtime/scripts/amalgm-mcp/adapters/truth.js +19 -0
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +20 -68
- package/runtime/scripts/amalgm-mcp/index.js +4 -0
- package/runtime/scripts/amalgm-mcp/lib/layout.js +89 -4
- package/runtime/scripts/amalgm-mcp/observer/README.md +259 -0
- package/runtime/scripts/amalgm-mcp/observer/apply.js +150 -0
- package/runtime/scripts/amalgm-mcp/observer/continuity.js +111 -0
- package/runtime/scripts/amalgm-mcp/observer/edges.js +111 -0
- package/runtime/scripts/amalgm-mcp/observer/index.js +1146 -0
- package/runtime/scripts/amalgm-mcp/observer/scan.js +387 -0
- package/runtime/scripts/amalgm-mcp/observer/store.js +262 -0
- package/runtime/scripts/amalgm-mcp/observer/verify.js +200 -0
- package/runtime/scripts/amalgm-mcp/observer/watch.js +62 -0
- package/runtime/scripts/amalgm-mcp/project-context/store.js +19 -87
- package/runtime/scripts/amalgm-mcp/registration/classify.js +33 -0
- package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +456 -0
- package/runtime/scripts/amalgm-mcp/registration/entity-content.js +345 -0
- package/runtime/scripts/amalgm-mcp/registration/index.js +752 -0
- package/runtime/scripts/amalgm-mcp/registration/refusal.js +42 -0
- package/runtime/scripts/amalgm-mcp/registration/repo-followers.js +127 -0
- package/runtime/scripts/amalgm-mcp/registration/repo-states.js +109 -0
- package/runtime/scripts/amalgm-mcp/registration/service.js +310 -0
- package/runtime/scripts/amalgm-mcp/registration/tree.js +178 -0
- package/runtime/scripts/amalgm-mcp/registry/evidence.js +826 -0
- package/runtime/scripts/amalgm-mcp/registry/index.js +666 -0
- package/runtime/scripts/amalgm-mcp/registry/store.js +290 -0
- package/runtime/scripts/amalgm-mcp/repocard/README.md +103 -0
- package/runtime/scripts/amalgm-mcp/repocard/apply.js +111 -0
- package/runtime/scripts/amalgm-mcp/repocard/capture-worker.js +94 -0
- package/runtime/scripts/amalgm-mcp/repocard/capture.js +149 -0
- package/runtime/scripts/amalgm-mcp/repocard/follow.js +310 -0
- package/runtime/scripts/amalgm-mcp/repocard/git.js +48 -0
- package/runtime/scripts/amalgm-mcp/repocard/index.js +48 -0
- package/runtime/scripts/amalgm-mcp/server/local-service-router.js +2 -0
- package/runtime/scripts/amalgm-mcp/server/routes/entities.js +287 -0
- package/runtime/scripts/amalgm-mcp/server/routes/state.js +0 -13
- package/runtime/scripts/amalgm-mcp/state/attachments.js +11 -45
- package/runtime/scripts/amalgm-mcp/state/db.js +46 -215
- package/runtime/scripts/amalgm-mcp/state/docs.js +301 -1101
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +0 -99
- package/runtime/scripts/amalgm-mcp/state/mutations.js +130 -507
- package/runtime/scripts/amalgm-mcp/state/promotions.js +1 -21
- package/runtime/scripts/amalgm-mcp/state/replicas.js +13 -134
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +3 -46
- package/runtime/scripts/amalgm-mcp/tests/adapters.test.js +379 -0
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +0 -193
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.life.test.js +449 -0
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.rig.js +107 -0
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.watch.test.js +213 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/adapter.js +72 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/assert.js +497 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/bind.js +91 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/graph.js +1031 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/ledger.js +234 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/referee.js +118 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.oracle.test.js +1365 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.registry.test.js +967 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.rig.js +239 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.storm.test.js +486 -0
- package/runtime/scripts/amalgm-mcp/tests/fake-watch.js +35 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.links.test.js +289 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.rig.js +103 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.roots.test.js +505 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.storm.test.js +389 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.territory.test.js +845 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.test.js +657 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +17 -32
- package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +256 -0
- package/runtime/scripts/amalgm-mcp/tests/registration.test.js +931 -0
- package/runtime/scripts/amalgm-mcp/tests/repocard.storm.test.js +284 -0
- package/runtime/scripts/amalgm-mcp/tests/repocard.test.js +371 -0
- package/runtime/scripts/amalgm-mcp/tests/repofollow.storm.test.js +435 -0
- package/runtime/scripts/amalgm-mcp/tests/repofollow.test.js +675 -0
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +0 -84
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +13 -376
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +0 -309
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +30 -758
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +3 -72
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +1 -89
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +24 -55
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +141 -424
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +3 -171
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +7 -220
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +236 -179
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +326 -2228
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +3 -5319
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +1 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +17 -3140
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +740 -137
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +1 -5
- package/runtime/scripts/amalgm-mcp/workspace/card.js +11 -35
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +14 -39
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +147 -587
- package/runtime/scripts/amalgm-mcp/workspace/git.js +34 -406
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +181 -102
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +56 -221
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +538 -1964
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +28 -49
- package/runtime/scripts/amalgm-mcp/workspace/store.js +4 -77
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +192 -1734
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +1 -7
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +61 -73
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +6 -51
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +8 -79
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +146 -3461
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +71 -899
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +538 -370
- package/runtime/scripts/local-gateway.js +1 -0
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +0 -550
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +0 -399
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +0 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +0 -24
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +0 -1999
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +0 -364
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +0 -185
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +0 -26
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +0 -119
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +0 -1446
- package/runtime/scripts/amalgm-mcp/workspace/content.js +0 -32
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +0 -45
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +0 -535
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +0 -31
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +0 -72
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +0 -165
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +0 -5
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +0 -2
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +0 -2201
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +0 -19
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +0 -18
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +0 -18
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +0 -33
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +0 -47
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +0 -43
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +0 -114
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +0 -84
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +0 -771
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +0 -240
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +0 -240
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +0 -100
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +0 -127
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +0 -12
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +0 -125
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +0 -76
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +0 -3766
|
@@ -52,11 +52,7 @@ function resolveAccess(nodeId, userIdInput, options = {}) {
|
|
|
52
52
|
const database = options.database || openLocalDb();
|
|
53
53
|
const userId = cleanUserId(userIdInput);
|
|
54
54
|
const target = nodeRow(database, nodeId);
|
|
55
|
-
|
|
56
|
-
// it does not erase the UUID or its inherited authority. Reconciliation must
|
|
57
|
-
// still be able to authorize and journal that offline deletion. Purge alone
|
|
58
|
-
// retires the identity.
|
|
59
|
-
if (!target || target.purged_at) return null;
|
|
55
|
+
if (!target || target.purged_at || target.missing_at) return null;
|
|
60
56
|
const parents = new Map(
|
|
61
57
|
database.prepare('SELECT id, parent_id FROM workspace_nodes WHERE workspace_id = ?')
|
|
62
58
|
.all(target.workspace_id)
|
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
} = require('./git');
|
|
24
24
|
const { cacheRootCommit, machineId, replicaForGitDir } = require('./identity');
|
|
25
25
|
|
|
26
|
-
const CARD_CONTRACT = 'repo-card@
|
|
26
|
+
const CARD_CONTRACT = 'repo-card@2';
|
|
27
27
|
|
|
28
28
|
// A transportable identity needs a dotted host: bare names resolve through
|
|
29
29
|
// this machine's own config (ssh aliases, LAN names) and identify nothing
|
|
@@ -60,12 +60,7 @@ function cardKind(classified) {
|
|
|
60
60
|
return classified.linked ? 'linked-worktree' : 'primary';
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function
|
|
64
|
-
if (previousOid !== currentOid) return null;
|
|
65
|
-
return source?.kind === 'bundle' && source.resourceId === resourceId ? source : null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function observeWorktree(classified, previousCard = null) {
|
|
63
|
+
async function observeWorktree(classified) {
|
|
69
64
|
const worktreeRoot = classified.toplevel;
|
|
70
65
|
const gitDir = await fs.promises.realpath(classified.gitDir);
|
|
71
66
|
const replica = replicaForGitDir(gitDir);
|
|
@@ -82,32 +77,14 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
const repoIdentity = remote === null ? null : remoteIdentity(remote);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
? require('./wall').projectForPath(worktreeRoot)?.resourceId
|
|
80
|
+
const objects = head.oid && repoIdentity
|
|
81
|
+
? await require('./objects').objectSourceFor(worktreeRoot, repoIdentity, head.oid)
|
|
88
82
|
: null;
|
|
89
|
-
const previous = previousCard?.replica === replica.replicaId
|
|
90
|
-
&& previousCard.repo?.remote === repoIdentity
|
|
91
|
-
? previousCard
|
|
92
|
-
: null;
|
|
93
|
-
const previousRefs = new Map((previous?.refs || []).map((ref) => [ref.name, ref]));
|
|
94
|
-
const objectSources = new Map();
|
|
95
|
-
const sourceFor = (oid, priorOid, priorSource) => {
|
|
96
|
-
if (!oid || !repoKey) return null;
|
|
97
|
-
if (objectSources.has(oid)) return objectSources.get(oid);
|
|
98
|
-
const source = reusableObjectSource(priorOid, oid, priorSource, resourceId)
|
|
99
|
-
|| require('./objects').objectSourceFor(worktreeRoot, repoKey, oid);
|
|
100
|
-
objectSources.set(oid, source);
|
|
101
|
-
return source;
|
|
102
|
-
};
|
|
103
|
-
const objects = await sourceFor(head.oid, previous?.head?.oid, previous?.objects);
|
|
104
83
|
const refs = await Promise.all(branches.map(async (branch) => ({
|
|
105
84
|
...branch,
|
|
106
|
-
objects:
|
|
107
|
-
branch.oid
|
|
108
|
-
|
|
109
|
-
previousRefs.get(branch.name)?.objects,
|
|
110
|
-
),
|
|
85
|
+
objects: repoIdentity
|
|
86
|
+
? await require('./objects').objectSourceFor(worktreeRoot, repoIdentity, branch.oid)
|
|
87
|
+
: null,
|
|
111
88
|
})));
|
|
112
89
|
return {
|
|
113
90
|
card: CARD_CONTRACT,
|
|
@@ -131,7 +108,7 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
131
108
|
}
|
|
132
109
|
|
|
133
110
|
/**
|
|
134
|
-
* Build the repo-card@
|
|
111
|
+
* Build the repo-card@2 for one worktree. `dirPath` may be any directory
|
|
135
112
|
* inside the worktree; the card is always for the worktree root. Throws
|
|
136
113
|
* on anything that is not a worktree — discovery filters first.
|
|
137
114
|
*
|
|
@@ -140,14 +117,14 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
140
117
|
* moved. A repo still in motion after that simply cards again on the next
|
|
141
118
|
* doorbell; every card is a true observation of some moment.
|
|
142
119
|
*/
|
|
143
|
-
async function buildRepoCard(dirPath
|
|
120
|
+
async function buildRepoCard(dirPath) {
|
|
144
121
|
const classified = await classifyDirectory(dirPath);
|
|
145
122
|
if (classified.kind !== 'worktree') {
|
|
146
123
|
throw new Error(`cannot card ${classified.path}: ${classified.kind}`);
|
|
147
124
|
}
|
|
148
|
-
const observed = await observeWorktree(classified
|
|
125
|
+
const observed = await observeWorktree(classified);
|
|
149
126
|
if (observed.head.oid === await headOid(classified.toplevel)) return observed;
|
|
150
|
-
return observeWorktree(classified
|
|
127
|
+
return observeWorktree(classified);
|
|
151
128
|
}
|
|
152
129
|
|
|
153
130
|
/**
|
|
@@ -229,7 +206,6 @@ async function discoverWorktrees(rootPath) {
|
|
|
229
206
|
}
|
|
230
207
|
|
|
231
208
|
module.exports = {
|
|
232
|
-
_test: { reusableObjectSource },
|
|
233
209
|
CARD_CONTRACT,
|
|
234
210
|
buildRepoCard,
|
|
235
211
|
cardFingerprint,
|
|
@@ -37,7 +37,7 @@ const service = {
|
|
|
37
37
|
reconcileTimer: null,
|
|
38
38
|
workspaceTimer: null,
|
|
39
39
|
unsubscribe: null,
|
|
40
|
-
doorbells: new Map(), // replicaId → {
|
|
40
|
+
doorbells: new Map(), // replicaId → { watchers, timer }
|
|
41
41
|
counters: { refreshes: 0, doorbellRings: 0 },
|
|
42
42
|
lastRefresh: null,
|
|
43
43
|
};
|
|
@@ -171,7 +171,6 @@ async function runRefresh() {
|
|
|
171
171
|
service.counters.refreshes += 1;
|
|
172
172
|
|
|
173
173
|
const roots = trackedRoots();
|
|
174
|
-
const previousByPath = new Map(listRepoCards().map((card) => [card.path, card]));
|
|
175
174
|
const targets = new Map(); // worktree root → registry root that reached it
|
|
176
175
|
const skipped = [];
|
|
177
176
|
const rootErrors = [];
|
|
@@ -190,7 +189,7 @@ async function runRefresh() {
|
|
|
190
189
|
const built = [];
|
|
191
190
|
for (const [target, root] of targets) {
|
|
192
191
|
try {
|
|
193
|
-
built.push({ card: await buildRepoCard(target
|
|
192
|
+
built.push({ card: await buildRepoCard(target), root });
|
|
194
193
|
} catch (error) {
|
|
195
194
|
skipped.push({ path: target, reason: 'observation-failed', error: error.message });
|
|
196
195
|
}
|
|
@@ -322,7 +321,7 @@ async function refreshRepoCard(replicaId) {
|
|
|
322
321
|
const row = db.prepare('SELECT * FROM repo_cards WHERE replica_id = ?').get(replicaId);
|
|
323
322
|
if (!row) return null;
|
|
324
323
|
|
|
325
|
-
const observedCard = await buildRepoCard(row.path
|
|
324
|
+
const observedCard = await buildRepoCard(row.path);
|
|
326
325
|
if (observedCard.replica !== replicaId) return refreshRepoCards();
|
|
327
326
|
const card = observedCard;
|
|
328
327
|
const fingerprint = cardFingerprint(card);
|
|
@@ -443,7 +442,7 @@ function nestedDirectories(root) {
|
|
|
443
442
|
}
|
|
444
443
|
|
|
445
444
|
function watchGitDir(card) {
|
|
446
|
-
const
|
|
445
|
+
const watchers = [];
|
|
447
446
|
const ring = () => ringDoorbell(card.replica);
|
|
448
447
|
// The git dir itself carries HEAD, index, and operation markers; logs/
|
|
449
448
|
// carries the reflog, which moves on every commit, checkout, and reset.
|
|
@@ -459,13 +458,9 @@ function watchGitDir(card) {
|
|
|
459
458
|
for (const dir of new Set([...direct, ...refDirectories])) {
|
|
460
459
|
try {
|
|
461
460
|
const watcher = fs.watch(dir, ring);
|
|
462
|
-
watcher.on('error', () =>
|
|
463
|
-
|
|
464
|
-
watcher.close();
|
|
465
|
-
});
|
|
466
|
-
watchSet.handles.push(watcher);
|
|
461
|
+
watcher.on('error', () => watcher.close());
|
|
462
|
+
watchers.push(watcher);
|
|
467
463
|
} catch {
|
|
468
|
-
watchSet.complete = false;
|
|
469
464
|
// The directory vanished between observation and watch; the next
|
|
470
465
|
// reconcile pass re-syncs the bells.
|
|
471
466
|
}
|
|
@@ -476,31 +471,27 @@ function watchGitDir(card) {
|
|
|
476
471
|
if (relative === '.git' || relative.startsWith('.git/')) return;
|
|
477
472
|
ring();
|
|
478
473
|
});
|
|
479
|
-
watcher.on('error', () =>
|
|
480
|
-
|
|
481
|
-
watcher.close();
|
|
482
|
-
});
|
|
483
|
-
watchSet.handles.push(watcher);
|
|
474
|
+
watcher.on('error', () => watcher.close());
|
|
475
|
+
watchers.push(watcher);
|
|
484
476
|
} catch {
|
|
485
|
-
watchSet.complete = false;
|
|
486
477
|
// The periodic card refresh remains the fallback on filesystems that
|
|
487
478
|
// cannot watch a checkout recursively.
|
|
488
479
|
}
|
|
489
|
-
return
|
|
480
|
+
return watchers;
|
|
490
481
|
}
|
|
491
482
|
|
|
492
483
|
function rebuildDoorbell(replicaId, card) {
|
|
493
484
|
const bell = service.doorbells.get(replicaId);
|
|
494
485
|
if (!bell) return;
|
|
495
|
-
const previous = bell.
|
|
496
|
-
|
|
497
|
-
|
|
486
|
+
const previous = bell.watchers;
|
|
487
|
+
bell.watchers = watchGitDir(card);
|
|
488
|
+
for (const watcher of previous) watcher.close();
|
|
498
489
|
}
|
|
499
490
|
|
|
500
491
|
function dropDoorbell(replicaId) {
|
|
501
492
|
const bell = service.doorbells.get(replicaId);
|
|
502
493
|
if (!bell) return;
|
|
503
|
-
for (const watcher of bell.
|
|
494
|
+
for (const watcher of bell.watchers) watcher.close();
|
|
504
495
|
if (bell.timer) clearTimeout(bell.timer);
|
|
505
496
|
service.doorbells.delete(replicaId);
|
|
506
497
|
}
|
|
@@ -515,9 +506,7 @@ function syncDoorbells(cards) {
|
|
|
515
506
|
}
|
|
516
507
|
for (const [replicaId, card] of healthy) {
|
|
517
508
|
if (!service.doorbells.has(replicaId)) {
|
|
518
|
-
service.doorbells.set(replicaId, {
|
|
519
|
-
} else if (!service.doorbells.get(replicaId).watchSet.complete) {
|
|
520
|
-
rebuildDoorbell(replicaId, card);
|
|
509
|
+
service.doorbells.set(replicaId, { watchers: watchGitDir(card), timer: null });
|
|
521
510
|
}
|
|
522
511
|
}
|
|
523
512
|
}
|
|
@@ -562,8 +551,6 @@ function repoCardServiceStats() {
|
|
|
562
551
|
refreshes: service.counters.refreshes,
|
|
563
552
|
doorbellRings: service.counters.doorbellRings,
|
|
564
553
|
doorbells: service.doorbells.size,
|
|
565
|
-
incompleteDoorbells: Array.from(service.doorbells.values())
|
|
566
|
-
.filter((bell) => !bell.watchSet.complete).length,
|
|
567
554
|
lastRefresh: service.lastRefresh,
|
|
568
555
|
};
|
|
569
556
|
}
|
|
@@ -575,16 +562,4 @@ module.exports = {
|
|
|
575
562
|
repoCardServiceStats,
|
|
576
563
|
startRepoCardService,
|
|
577
564
|
stopRepoCardService,
|
|
578
|
-
// Test hooks only. Filesystem delivery itself belongs to the host; these
|
|
579
|
-
// expose the deterministic doorbell boundary behind that latency hint.
|
|
580
|
-
_private: {
|
|
581
|
-
rebuildDoorbell,
|
|
582
|
-
ringDoorbell,
|
|
583
|
-
watcherCount(replicaId) {
|
|
584
|
-
return service.doorbells.get(replicaId)?.watchSet.handles.length || 0;
|
|
585
|
-
},
|
|
586
|
-
watcherComplete(replicaId) {
|
|
587
|
-
return service.doorbells.get(replicaId)?.watchSet.complete === true;
|
|
588
|
-
},
|
|
589
|
-
},
|
|
590
565
|
};
|