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.
Files changed (157) hide show
  1. package/README.md +12 -0
  2. package/lib/cli.js +146 -8
  3. package/lib/layout.js +89 -4
  4. package/lib/shared-realtime-tunnel.js +384 -43
  5. package/lib/supervisor.js +0 -3
  6. package/package.json +2 -2
  7. package/runtime/scripts/amalgm-mcp/adapters/contract.js +197 -0
  8. package/runtime/scripts/amalgm-mcp/adapters/filesystem.js +135 -0
  9. package/runtime/scripts/amalgm-mcp/adapters/git.js +84 -0
  10. package/runtime/scripts/amalgm-mcp/adapters/index.js +43 -0
  11. package/runtime/scripts/amalgm-mcp/adapters/reference.js +31 -0
  12. package/runtime/scripts/amalgm-mcp/adapters/truth.js +19 -0
  13. package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +20 -68
  14. package/runtime/scripts/amalgm-mcp/index.js +4 -0
  15. package/runtime/scripts/amalgm-mcp/lib/layout.js +89 -4
  16. package/runtime/scripts/amalgm-mcp/observer/README.md +259 -0
  17. package/runtime/scripts/amalgm-mcp/observer/apply.js +150 -0
  18. package/runtime/scripts/amalgm-mcp/observer/continuity.js +111 -0
  19. package/runtime/scripts/amalgm-mcp/observer/edges.js +111 -0
  20. package/runtime/scripts/amalgm-mcp/observer/index.js +1146 -0
  21. package/runtime/scripts/amalgm-mcp/observer/scan.js +387 -0
  22. package/runtime/scripts/amalgm-mcp/observer/store.js +262 -0
  23. package/runtime/scripts/amalgm-mcp/observer/verify.js +200 -0
  24. package/runtime/scripts/amalgm-mcp/observer/watch.js +62 -0
  25. package/runtime/scripts/amalgm-mcp/project-context/store.js +19 -87
  26. package/runtime/scripts/amalgm-mcp/registration/classify.js +33 -0
  27. package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +456 -0
  28. package/runtime/scripts/amalgm-mcp/registration/entity-content.js +345 -0
  29. package/runtime/scripts/amalgm-mcp/registration/index.js +752 -0
  30. package/runtime/scripts/amalgm-mcp/registration/refusal.js +42 -0
  31. package/runtime/scripts/amalgm-mcp/registration/repo-followers.js +127 -0
  32. package/runtime/scripts/amalgm-mcp/registration/repo-states.js +109 -0
  33. package/runtime/scripts/amalgm-mcp/registration/service.js +310 -0
  34. package/runtime/scripts/amalgm-mcp/registration/tree.js +178 -0
  35. package/runtime/scripts/amalgm-mcp/registry/evidence.js +826 -0
  36. package/runtime/scripts/amalgm-mcp/registry/index.js +666 -0
  37. package/runtime/scripts/amalgm-mcp/registry/store.js +290 -0
  38. package/runtime/scripts/amalgm-mcp/repocard/README.md +103 -0
  39. package/runtime/scripts/amalgm-mcp/repocard/apply.js +111 -0
  40. package/runtime/scripts/amalgm-mcp/repocard/capture-worker.js +94 -0
  41. package/runtime/scripts/amalgm-mcp/repocard/capture.js +149 -0
  42. package/runtime/scripts/amalgm-mcp/repocard/follow.js +310 -0
  43. package/runtime/scripts/amalgm-mcp/repocard/git.js +48 -0
  44. package/runtime/scripts/amalgm-mcp/repocard/index.js +48 -0
  45. package/runtime/scripts/amalgm-mcp/server/local-service-router.js +2 -0
  46. package/runtime/scripts/amalgm-mcp/server/routes/entities.js +287 -0
  47. package/runtime/scripts/amalgm-mcp/server/routes/state.js +0 -13
  48. package/runtime/scripts/amalgm-mcp/state/attachments.js +11 -45
  49. package/runtime/scripts/amalgm-mcp/state/db.js +46 -215
  50. package/runtime/scripts/amalgm-mcp/state/docs.js +301 -1101
  51. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +0 -99
  52. package/runtime/scripts/amalgm-mcp/state/mutations.js +130 -507
  53. package/runtime/scripts/amalgm-mcp/state/promotions.js +1 -21
  54. package/runtime/scripts/amalgm-mcp/state/replicas.js +13 -134
  55. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +3 -46
  56. package/runtime/scripts/amalgm-mcp/tests/adapters.test.js +379 -0
  57. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +0 -193
  58. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.life.test.js +449 -0
  59. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.rig.js +107 -0
  60. package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.watch.test.js +213 -0
  61. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/adapter.js +72 -0
  62. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/assert.js +497 -0
  63. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/bind.js +91 -0
  64. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/graph.js +1031 -0
  65. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/ledger.js +234 -0
  66. package/runtime/scripts/amalgm-mcp/tests/entity-oracle/referee.js +118 -0
  67. package/runtime/scripts/amalgm-mcp/tests/entity.oracle.test.js +1365 -0
  68. package/runtime/scripts/amalgm-mcp/tests/entity.registry.test.js +967 -0
  69. package/runtime/scripts/amalgm-mcp/tests/entity.rig.js +239 -0
  70. package/runtime/scripts/amalgm-mcp/tests/entity.storm.test.js +486 -0
  71. package/runtime/scripts/amalgm-mcp/tests/fake-watch.js +35 -0
  72. package/runtime/scripts/amalgm-mcp/tests/observer.links.test.js +289 -0
  73. package/runtime/scripts/amalgm-mcp/tests/observer.rig.js +103 -0
  74. package/runtime/scripts/amalgm-mcp/tests/observer.roots.test.js +505 -0
  75. package/runtime/scripts/amalgm-mcp/tests/observer.storm.test.js +389 -0
  76. package/runtime/scripts/amalgm-mcp/tests/observer.territory.test.js +845 -0
  77. package/runtime/scripts/amalgm-mcp/tests/observer.test.js +657 -0
  78. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +17 -32
  79. package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +256 -0
  80. package/runtime/scripts/amalgm-mcp/tests/registration.test.js +931 -0
  81. package/runtime/scripts/amalgm-mcp/tests/repocard.storm.test.js +284 -0
  82. package/runtime/scripts/amalgm-mcp/tests/repocard.test.js +371 -0
  83. package/runtime/scripts/amalgm-mcp/tests/repofollow.storm.test.js +435 -0
  84. package/runtime/scripts/amalgm-mcp/tests/repofollow.test.js +675 -0
  85. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +0 -84
  86. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +13 -376
  87. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +0 -309
  88. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +30 -758
  89. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +3 -72
  90. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +1 -89
  91. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +24 -55
  92. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +141 -424
  93. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +3 -171
  94. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +7 -220
  95. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +236 -179
  96. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +326 -2228
  97. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +3 -5319
  98. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +1 -191
  99. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +17 -3140
  100. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +740 -137
  101. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +1 -5
  102. package/runtime/scripts/amalgm-mcp/workspace/card.js +11 -35
  103. package/runtime/scripts/amalgm-mcp/workspace/cards.js +14 -39
  104. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +147 -587
  105. package/runtime/scripts/amalgm-mcp/workspace/git.js +34 -406
  106. package/runtime/scripts/amalgm-mcp/workspace/merge.js +181 -102
  107. package/runtime/scripts/amalgm-mcp/workspace/objects.js +56 -221
  108. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +538 -1964
  109. package/runtime/scripts/amalgm-mcp/workspace/rest.js +28 -49
  110. package/runtime/scripts/amalgm-mcp/workspace/store.js +4 -77
  111. package/runtime/scripts/amalgm-mcp/workspace/transition.js +192 -1734
  112. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +1 -7
  113. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +61 -73
  114. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +6 -51
  115. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +8 -79
  116. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +146 -3461
  117. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +71 -899
  118. package/runtime/scripts/amalgm-mcp/workspace/wall.js +538 -370
  119. package/runtime/scripts/local-gateway.js +1 -0
  120. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +0 -550
  121. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +0 -399
  122. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +0 -1
  123. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +0 -24
  124. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +0 -1999
  125. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +0 -364
  126. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +0 -185
  127. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +0 -26
  128. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +0 -119
  129. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +0 -1446
  130. package/runtime/scripts/amalgm-mcp/workspace/content.js +0 -32
  131. package/runtime/scripts/amalgm-mcp/workspace/durable.js +0 -45
  132. package/runtime/scripts/amalgm-mcp/workspace/intent.js +0 -535
  133. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +0 -31
  134. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  135. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  136. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +0 -72
  137. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +0 -165
  138. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +0 -5
  139. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +0 -2
  140. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +0 -2201
  141. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +0 -19
  142. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +0 -18
  143. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +0 -18
  144. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +0 -33
  145. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +0 -47
  146. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +0 -43
  147. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +0 -114
  148. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +0 -84
  149. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +0 -771
  150. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +0 -240
  151. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +0 -240
  152. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +0 -100
  153. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +0 -127
  154. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +0 -12
  155. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +0 -125
  156. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +0 -76
  157. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +0 -3766
@@ -1,240 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * The complete provenance transition relation for managed repo bytes.
5
- *
6
- * This is deliberately data, not a second reconciler. Runtime code may name
7
- * one of these transitions, and tests exhaust the whole relation.
8
- */
9
-
10
- const CLASS = Object.freeze({
11
- OFFICIAL: 'officially-applied',
12
- INTENT: 'journaled-local-intent',
13
- UNKNOWN: 'unknown-provenance',
14
- });
15
-
16
- const STATE = Object.freeze({
17
- OFFICIAL: CLASS.OFFICIAL,
18
- UNKNOWN: CLASS.UNKNOWN,
19
- CAPTURED: `${CLASS.INTENT}:captured`,
20
- RESERVED: `${CLASS.INTENT}:reserved`,
21
- NUMBERED: `${CLASS.INTENT}:numbered`,
22
- FOLDED: `${CLASS.INTENT}:folded`,
23
- });
24
-
25
- const ACTION = Object.freeze({
26
- SAVE: 'save',
27
- VERIFY: 'verify',
28
- CAPTURE: 'capture',
29
- RESERVE: 'reserve',
30
- RETRY: 'retry',
31
- NUMBER: 'number',
32
- FOLD: 'fold',
33
- APPLY: 'apply',
34
- SNAPSHOT: 'snapshot',
35
- COMPACT: 'compact',
36
- PRUNE: 'prune',
37
- RECOVER: 'recover',
38
- OBSERVE_UNKNOWN: 'observe-unknown',
39
- });
40
-
41
- const allStates = Object.freeze(Object.values(STATE));
42
-
43
- function transition(from, action, to, facts) {
44
- return Object.freeze({
45
- from,
46
- action,
47
- to,
48
- facts: Object.freeze(facts),
49
- });
50
- }
51
-
52
- const transitions = [
53
- transition(STATE.OFFICIAL, ACTION.SAVE, STATE.UNKNOWN, [
54
- 'previous-provenance-preserved',
55
- ]),
56
- ...[
57
- STATE.CAPTURED,
58
- STATE.RESERVED,
59
- STATE.NUMBERED,
60
- STATE.FOLDED,
61
- ].map((from) => transition(from, ACTION.SAVE, STATE.UNKNOWN, [
62
- 'previous-provenance-preserved',
63
- ])),
64
- transition(STATE.UNKNOWN, ACTION.OBSERVE_UNKNOWN, STATE.UNKNOWN, [
65
- 'exact-physical-observation',
66
- ]),
67
- transition(STATE.UNKNOWN, ACTION.VERIFY, STATE.OFFICIAL, [
68
- 'official-target',
69
- 'physical-verification',
70
- 'applied-cursor',
71
- ]),
72
- transition(STATE.UNKNOWN, ACTION.CAPTURE, STATE.CAPTURED, [
73
- 'verified-base',
74
- 'exact-local-state',
75
- 'physical-digest',
76
- 'intent-record',
77
- ]),
78
- transition(STATE.CAPTURED, ACTION.RESERVE, STATE.RESERVED, [
79
- 'intent-record',
80
- 'publication-id',
81
- ]),
82
- transition(STATE.RESERVED, ACTION.RESERVE, STATE.RESERVED, [
83
- 'same-publication-id',
84
- 'same-envelope',
85
- ]),
86
- transition(STATE.RESERVED, ACTION.RETRY, STATE.RESERVED, [
87
- 'same-publication-id',
88
- 'same-envelope',
89
- ]),
90
- transition(STATE.RESERVED, ACTION.NUMBER, STATE.NUMBERED, [
91
- 'publication-id',
92
- 'official-version',
93
- 'server-receipt',
94
- ]),
95
- transition(STATE.NUMBERED, ACTION.NUMBER, STATE.NUMBERED, [
96
- 'same-publication-id',
97
- 'same-official-version',
98
- ]),
99
- transition(STATE.NUMBERED, ACTION.FOLD, STATE.FOLDED, [
100
- 'predecessor-folded',
101
- 'official-event',
102
- 'official-snapshot',
103
- ]),
104
- transition(STATE.FOLDED, ACTION.FOLD, STATE.FOLDED, [
105
- 'same-official-version',
106
- 'official-snapshot',
107
- ]),
108
- transition(STATE.FOLDED, ACTION.APPLY, STATE.OFFICIAL, [
109
- 'official-target',
110
- 'apply-wal',
111
- 'physical-verification',
112
- 'applied-cursor',
113
- 'intent-retired',
114
- ]),
115
- transition(STATE.OFFICIAL, ACTION.APPLY, STATE.OFFICIAL, [
116
- 'contiguous-official-target',
117
- 'apply-wal',
118
- 'physical-verification',
119
- 'applied-cursor',
120
- ]),
121
- ...[
122
- STATE.CAPTURED,
123
- STATE.RESERVED,
124
- STATE.NUMBERED,
125
- ].map((state) => transition(state, ACTION.APPLY, state, [
126
- 'contiguous-official-target',
127
- 'apply-wal',
128
- 'pending-intent-preserved',
129
- 'physical-verification',
130
- 'applied-cursor',
131
- ])),
132
- transition(STATE.OFFICIAL, ACTION.SNAPSHOT, STATE.OFFICIAL, [
133
- 'verified-snapshot',
134
- 'apply-wal',
135
- 'physical-verification',
136
- 'applied-cursor',
137
- ]),
138
- transition(STATE.UNKNOWN, ACTION.SNAPSHOT, STATE.OFFICIAL, [
139
- 'empty-path',
140
- 'verified-snapshot',
141
- 'apply-wal',
142
- 'physical-verification',
143
- 'applied-cursor',
144
- ]),
145
- transition(STATE.UNKNOWN, ACTION.SNAPSHOT, STATE.UNKNOWN, [
146
- 'unknown-bytes-preserved',
147
- ]),
148
- ...[
149
- STATE.CAPTURED,
150
- STATE.RESERVED,
151
- ].map((state) => transition(state, ACTION.SNAPSHOT, state, [
152
- 'verified-snapshot',
153
- 'apply-wal',
154
- 'pending-intent-preserved',
155
- 'physical-verification',
156
- 'applied-cursor',
157
- ])),
158
- ...[
159
- STATE.NUMBERED,
160
- STATE.FOLDED,
161
- ].map((state) => transition(state, ACTION.SNAPSHOT, STATE.OFFICIAL, [
162
- 'numbered-event-in-snapshot',
163
- 'verified-snapshot',
164
- 'apply-wal',
165
- 'physical-verification',
166
- 'applied-cursor',
167
- 'intent-retired',
168
- ])),
169
- transition(STATE.OFFICIAL, ACTION.COMPACT, STATE.OFFICIAL, [
170
- 'retained-server-receipt',
171
- 'verified-server-snapshot',
172
- ]),
173
- transition(STATE.FOLDED, ACTION.PRUNE, STATE.FOLDED, [
174
- 'official-fold',
175
- 'durable-folded-intent',
176
- ]),
177
- transition(STATE.OFFICIAL, ACTION.PRUNE, STATE.OFFICIAL, [
178
- 'official-fold',
179
- 'physical-verification',
180
- 'applied-cursor',
181
- ]),
182
- ...allStates.map((state) => transition(state, ACTION.RECOVER, state, [
183
- 'durable-provenance-record',
184
- 'apply-wal-before-destructive-write',
185
- ])),
186
- ...[
187
- STATE.OFFICIAL,
188
- STATE.CAPTURED,
189
- STATE.RESERVED,
190
- STATE.NUMBERED,
191
- STATE.FOLDED,
192
- ].map((state) => transition(state, ACTION.RECOVER, STATE.UNKNOWN, [
193
- 'apply-wal',
194
- 'exact-third-value-observation',
195
- 'previous-provenance-preserved',
196
- ])),
197
- ];
198
-
199
- const TRANSITIONS = Object.freeze(transitions);
200
- const transitionByKey = new Map(
201
- TRANSITIONS.map((entry) => [`${entry.from}\u0000${entry.action}\u0000${entry.to}`, entry]),
202
- );
203
-
204
- function provenanceClass(state) {
205
- if (state === STATE.OFFICIAL) return CLASS.OFFICIAL;
206
- if (state === STATE.UNKNOWN) return CLASS.UNKNOWN;
207
- if (typeof state === 'string' && state.startsWith(`${CLASS.INTENT}:`)) {
208
- return CLASS.INTENT;
209
- }
210
- return null;
211
- }
212
-
213
- function publicationState(publication) {
214
- if (!publication) return STATE.CAPTURED;
215
- if (publication.foldedAt != null) return STATE.FOLDED;
216
- if (publication.numberedVersion != null) return STATE.NUMBERED;
217
- return STATE.RESERVED;
218
- }
219
-
220
- function requireTransition(from, action, to, availableFacts = []) {
221
- const entry = transitionByKey.get(`${from}\u0000${action}\u0000${to}`);
222
- if (!entry) throw new Error(`illegal provenance transition: ${from} --${action}--> ${to}`);
223
- const available = new Set(availableFacts);
224
- const missing = entry.facts.filter((fact) => !available.has(fact));
225
- if (missing.length > 0) {
226
- throw new Error(`provenance transition ${action} is missing: ${missing.join(', ')}`);
227
- }
228
- return entry;
229
- }
230
-
231
- module.exports = {
232
- ACTION,
233
- CLASS,
234
- STATE,
235
- TRANSITIONS,
236
- allStates,
237
- provenanceClass,
238
- publicationState,
239
- requireTransition,
240
- };
@@ -1,240 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Referee projection for the floor, physical verification, and local overlay.
5
- *
6
- * Referee axiom: a gate must see every active or successor intent. Proven by
7
- * workspace-sync-stats.test.js and workspace-sync-harness-core.test.mjs.
8
- */
9
-
10
- const crypto = require('crypto');
11
- const fs = require('fs');
12
- const path = require('path');
13
- const { listRepoCards, repoCardServiceStats } = require('./cards');
14
- const { repoKeyOfCard } = require('./checkpoint');
15
- const { machineId, userId } = require('./identity');
16
- const projectStream = require('./project-stream');
17
- const reducer = require('./reducer');
18
- const { listWorkspaces } = require('./store');
19
- const wall = require('./wall');
20
- const runtimeVersion = require('../../../../package.json').version;
21
- const PACKAGE_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
22
- const RUNTIME_ROOT = path.resolve(__dirname, '..');
23
-
24
- function filesUnder(root) {
25
- return fs.readdirSync(root, { withFileTypes: true })
26
- .flatMap((entry) => {
27
- const file = path.join(root, entry.name);
28
- if (entry.isDirectory()) return filesUnder(file);
29
- return entry.isFile() ? [file] : [];
30
- })
31
- .sort();
32
- }
33
-
34
- // The sync identity follows the architecture tree, not a hand-maintained
35
- // dependency list. New state/materialization modules and native workspace
36
- // helpers therefore enter the release identity automatically.
37
- const SYNC_FILES = [
38
- path.join(RUNTIME_ROOT, 'state'),
39
- path.join(RUNTIME_ROOT, 'workspace'),
40
- ].flatMap(filesUnder);
41
-
42
- function fileFingerprint(files) {
43
- const hash = crypto.createHash('sha256');
44
- for (const file of files.filter((candidate) => fs.existsSync(candidate)).sort()) {
45
- hash.update(path.relative(PACKAGE_ROOT, file));
46
- hash.update('\0');
47
- hash.update(fs.readFileSync(file));
48
- hash.update('\0');
49
- }
50
- return `sha256:${hash.digest('hex')}`;
51
- }
52
-
53
- function sharedTunnelFiles() {
54
- return [
55
- path.join(PACKAGE_ROOT, 'lib', 'shared-realtime-tunnel.js'),
56
- path.join(PACKAGE_ROOT, 'packages', 'amalgm', 'lib', 'shared-realtime-tunnel.js'),
57
- ].filter((file) => fs.existsSync(file));
58
- }
59
-
60
- const runtimeBuildFingerprint = fileFingerprint([
61
- path.join(PACKAGE_ROOT, 'package.json'),
62
- ...filesUnder(RUNTIME_ROOT),
63
- ...sharedTunnelFiles(),
64
- ]);
65
- const runtimeSyncFingerprint = fileFingerprint([
66
- ...SYNC_FILES,
67
- ...sharedTunnelFiles(),
68
- ]);
69
-
70
- function canonical(value) {
71
- if (Array.isArray(value)) return value.map(canonical);
72
- if (!value || typeof value !== 'object') return value;
73
- return Object.fromEntries(
74
- Object.keys(value).sort().map((key) => [key, canonical(value[key])]),
75
- );
76
- }
77
-
78
- function digest(value) {
79
- return crypto.createHash('sha256')
80
- .update(JSON.stringify(canonical(value)))
81
- .digest('hex');
82
- }
83
-
84
- function samePath(left, right) {
85
- return Boolean(left && right && path.resolve(left) === path.resolve(right));
86
- }
87
-
88
- function folderFor(project, cards, workspaces) {
89
- const card = cards.find((entry) => (
90
- entry.kind === 'primary'
91
- && repoKeyOfCard(entry) === project.repoKey
92
- ));
93
- const folderPath = project.path || card?.path || null;
94
- const workspace = workspaces.find((entry) => samePath(entry.path, folderPath));
95
- return {
96
- card,
97
- folderPath,
98
- folderUuid: workspace?.id || null,
99
- };
100
- }
101
-
102
- function waitingFor(state, outcome) {
103
- const waiting = [];
104
- if (outcome?.state === 'waiting' || outcome?.state === 'error') {
105
- waiting.push({
106
- state: outcome.state,
107
- reason: outcome.reason || null,
108
- });
109
- }
110
- return waiting;
111
- }
112
-
113
- function verificationFor(state, outcome, pendingIntent = null) {
114
- return {
115
- contract: state?.verificationContract || null,
116
- appliedThrough: Number.isSafeInteger(state?.appliedThrough)
117
- ? state.appliedThrough
118
- : null,
119
- materialized: state?.materialized === true,
120
- targetDigest: state?.targetDigest || null,
121
- expectedPhysicalDigest: state?.expectedPhysicalDigest || null,
122
- observedPhysicalDigest: state?.observedPhysicalDigest || null,
123
- pendingIntent,
124
- waiting: waitingFor(state, outcome),
125
- };
126
- }
127
-
128
- function pendingIntentValue(value, owned) {
129
- const successor = value?.successor || null;
130
- if (!owned && !successor) return null;
131
- const result = {
132
- baseThrough: value.base.through,
133
- semanticDigest: value.semanticDigest,
134
- mutationId: value.birthMutationId || value.publication?.mutationId || null,
135
- terminalCode: value.terminal?.code || null,
136
- };
137
- if (successor) {
138
- result.successor = {
139
- predecessorSemanticDigest: successor.predecessorSemanticDigest,
140
- semanticDigest: successor.semanticDigest,
141
- mutationId: successor.birthMutationId || null,
142
- terminalCode: successor.terminal?.code || null,
143
- };
144
- }
145
- return result;
146
- }
147
-
148
- function pendingIntentFor(project) {
149
- const intents = require('./intent');
150
- const value = intents.readIntent(project);
151
- return pendingIntentValue(
152
- value,
153
- Boolean(value && !intents.officialAcknowledgement(project, value)),
154
- );
155
- }
156
-
157
- function autoUpdateDisabled(env = process.env) {
158
- const disabled = String(env.AMALGM_DISABLE_AUTO_UPDATE || '').trim().toLowerCase();
159
- if (['1', 'true', 'yes', 'on'].includes(disabled)) return true;
160
- const enabled = String(env.AMALGM_AUTO_UPDATE || '').trim().toLowerCase();
161
- return ['0', 'false', 'no', 'off'].includes(enabled);
162
- }
163
-
164
- function projectObservation({
165
- project,
166
- cards,
167
- workspaces,
168
- reducerRepos,
169
- }) {
170
- const folder = folderFor(project, cards, workspaces);
171
- const state = reducer.readRepoState(project.repoKey, project.resourceId);
172
- const exactState = (
173
- state
174
- && folder.folderPath
175
- && samePath(state.path, folder.folderPath)
176
- && (!folder.card || state.replica === folder.card.replica)
177
- ) ? state : null;
178
- const snapshot = projectStream.readSnapshot(project.file, project.resourceId);
179
- const publication = wall.publicationStatus(project.repoKey, exactState);
180
- const outcome = reducerRepos.find((entry) => (
181
- entry.repoKey === project.repoKey
182
- && (!folder.folderPath || !entry.path || samePath(entry.path, folder.folderPath))
183
- )) || null;
184
- return {
185
- resourceId: project.resourceId,
186
- repoKey: project.repoKey,
187
- folderPath: folder.folderPath,
188
- folderUuid: folder.folderUuid,
189
- receivedThrough: publication.receivedThrough,
190
- snapshotThrough: snapshot.version,
191
- snapshotDigest: digest(snapshot),
192
- pendingOutbound: publication.pending,
193
- verification: verificationFor(exactState, outcome, pendingIntentFor(project)),
194
- };
195
- }
196
-
197
- function workspaceSyncStats() {
198
- const cards = listRepoCards();
199
- const wallState = wall.wallStats();
200
- const reducerState = reducer.reducerStats();
201
- const workspaces = listWorkspaces();
202
- const projects = wallState.projects.map((entry) => projectObservation({
203
- project: wall.projectForRepo(entry.repoKey),
204
- cards,
205
- workspaces,
206
- reducerRepos: reducerState.repos,
207
- }));
208
- const health = { healthy: 0, missing: 0, broken: 0, inaccessible: 0 };
209
- for (const card of cards) health[card.health] = (health[card.health] || 0) + 1;
210
- return {
211
- machine: machineId(),
212
- user: userId(),
213
- runtimeVersion,
214
- runtime: {
215
- buildFingerprint: runtimeBuildFingerprint,
216
- syncFingerprint: runtimeSyncFingerprint,
217
- autoUpdateDisabled: autoUpdateDisabled(),
218
- },
219
- projects,
220
- cards: { total: cards.length, ...health },
221
- service: repoCardServiceStats(),
222
- wall: wallState,
223
- checkpoints: require('./checkpoint').checkpointStats(),
224
- reducer: reducerState,
225
- };
226
- }
227
-
228
- module.exports = {
229
- _test: {
230
- digest,
231
- folderFor,
232
- autoUpdateDisabled,
233
- fileFingerprint,
234
- syncFingerprintFiles: SYNC_FILES.map((file) => path.relative(RUNTIME_ROOT, file)),
235
- pendingIntentFor,
236
- pendingIntentValue,
237
- verificationFor,
238
- },
239
- workspaceSyncStats,
240
- };
@@ -1,100 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * The official workspace-tree floor.
5
- *
6
- * `workspace_nodes` is the physical view (official floor plus durable local
7
- * overlay), so it cannot also answer what an incoming official operation is
8
- * allowed to reduce against. This table keeps that one separate fact.
9
- */
10
-
11
- const { openLocalDb } = require('../../state/db');
12
- const { normalizeTreeSnapshot } = require('./snapshot');
13
-
14
- function ensureSchema(database = openLocalDb()) {
15
- database.exec(`
16
- CREATE TABLE IF NOT EXISTS workspace_tree_floors (
17
- resource_id TEXT PRIMARY KEY,
18
- rail_version INTEGER NOT NULL CHECK (rail_version >= 0),
19
- authority_epoch INTEGER NOT NULL CHECK (authority_epoch > 0),
20
- snapshot_json TEXT NOT NULL,
21
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
22
- );
23
- `);
24
- }
25
-
26
- function snapshotRecord(snapshot) {
27
- return {
28
- schemaVersion: 1,
29
- workspaceId: snapshot.workspaceId,
30
- version: snapshot.version,
31
- nodes: Array.from(snapshot.nodes.values()),
32
- };
33
- }
34
-
35
- function normalize(row) {
36
- if (!row) return null;
37
- const snapshot = normalizeTreeSnapshot(JSON.parse(row.snapshot_json));
38
- if (
39
- snapshot.workspaceId !== row.resource_id
40
- || snapshot.version !== Number(row.rail_version)
41
- ) {
42
- throw new Error(`workspace tree floor ${row.resource_id} is inconsistent`);
43
- }
44
- return {
45
- resourceId: row.resource_id,
46
- version: Number(row.rail_version),
47
- authorityEpoch: Number(row.authority_epoch),
48
- snapshot,
49
- updatedAt: row.updated_at,
50
- };
51
- }
52
-
53
- function read(resourceId, options = {}) {
54
- const database = options.database || openLocalDb();
55
- ensureSchema(database);
56
- return normalize(database.prepare(`
57
- SELECT * FROM workspace_tree_floors WHERE resource_id = ?
58
- `).get(resourceId));
59
- }
60
-
61
- function write(snapshotInput, authorityEpochInput, options = {}) {
62
- const database = options.database || openLocalDb();
63
- ensureSchema(database);
64
- const snapshot = normalizeTreeSnapshot(snapshotRecord(snapshotInput));
65
- const authorityEpoch = Number(authorityEpochInput);
66
- if (!Number.isSafeInteger(authorityEpoch) || authorityEpoch < 1) {
67
- throw new Error('workspace tree floor authority epoch is invalid');
68
- }
69
- database.prepare(`
70
- INSERT INTO workspace_tree_floors (
71
- resource_id, rail_version, authority_epoch, snapshot_json, updated_at
72
- ) VALUES (?, ?, ?, ?, ?)
73
- ON CONFLICT(resource_id) DO UPDATE SET
74
- rail_version = excluded.rail_version,
75
- authority_epoch = excluded.authority_epoch,
76
- snapshot_json = excluded.snapshot_json,
77
- updated_at = excluded.updated_at
78
- `).run(
79
- snapshot.workspaceId,
80
- snapshot.version,
81
- authorityEpoch,
82
- JSON.stringify(snapshotRecord(snapshot)),
83
- new Date().toISOString(),
84
- );
85
- return read(snapshot.workspaceId, { database });
86
- }
87
-
88
- function ensure(snapshot, authorityEpoch, options = {}) {
89
- const database = options.database || openLocalDb();
90
- const existing = read(snapshot.workspaceId, { database });
91
- if (existing) return existing;
92
- return write(snapshot, authorityEpoch, { database });
93
- }
94
-
95
- module.exports = {
96
- ensure,
97
- read,
98
- snapshotRecord,
99
- write,
100
- };
@@ -1,127 +0,0 @@
1
- 'use strict';
2
-
3
- const crypto = require('crypto');
4
-
5
- function stableHold(input) {
6
- const operation = input?.operation || null;
7
- const operationKind = typeof operation?.kind === 'string' ? operation.kind : null;
8
- const explicitNodeId = typeof input?.nodeId === 'string' ? input.nodeId : null;
9
- const operationNodeId = (
10
- operationKind !== 'tree.create'
11
- && typeof operation?.nodeId === 'string'
12
- ) ? operation.nodeId : null;
13
- return {
14
- reason: typeof input?.reason === 'string' && input.reason
15
- ? input.reason
16
- : 'observation-pending',
17
- nodeId: explicitNodeId || operationNodeId,
18
- operationKind,
19
- paths: Array.from(new Set(
20
- Array.isArray(input?.paths)
21
- ? input.paths.filter((value) => typeof value === 'string' && value)
22
- : [],
23
- )).sort(),
24
- };
25
- }
26
-
27
- function ownerScope(options = {}) {
28
- return {
29
- ownerKind: typeof options.ownerKind === 'string' && options.ownerKind
30
- ? options.ownerKind
31
- : 'observer',
32
- ownerId: typeof options.ownerId === 'string' && options.ownerId
33
- ? options.ownerId
34
- : 'workspace-tree-observer',
35
- };
36
- }
37
-
38
- function holdKey(owner, hold) {
39
- return crypto.createHash('sha256')
40
- .update(JSON.stringify({ ...owner, hold }))
41
- .digest('hex');
42
- }
43
-
44
- function normalize(rows) {
45
- return rows.map((row) => ({
46
- id: row.hold_key,
47
- ownerKind: row.owner_kind,
48
- ownerId: row.owner_id,
49
- reason: row.reason,
50
- nodeId: row.node_id || null,
51
- operationKind: row.operation_kind || null,
52
- paths: JSON.parse(row.paths_json),
53
- firstObservedAt: row.first_observed_at,
54
- }));
55
- }
56
-
57
- function list(workspaceId, database) {
58
- return normalize(database.prepare(`
59
- SELECT *
60
- FROM workspace_tree_observation_holds
61
- WHERE workspace_id = ?
62
- ORDER BY owner_kind, owner_id, reason, hold_key
63
- `).all(workspaceId));
64
- }
65
-
66
- function replace(workspaceId, inputs, database, options = {}) {
67
- const owner = ownerScope(options);
68
- const next = (Array.isArray(inputs) ? inputs : [])
69
- .map(stableHold)
70
- .map((hold) => ({ ...hold, key: holdKey(owner, hold) }))
71
- .sort((left, right) => left.key.localeCompare(right.key));
72
- const current = database.prepare(`
73
- SELECT *
74
- FROM workspace_tree_observation_holds
75
- WHERE workspace_id = ? AND owner_kind = ? AND owner_id = ?
76
- ORDER BY hold_key
77
- `).all(workspaceId, owner.ownerKind, owner.ownerId);
78
- const currentShape = current.map((row) => ({
79
- key: row.hold_key,
80
- reason: row.reason,
81
- nodeId: row.node_id || null,
82
- operationKind: row.operation_kind || null,
83
- paths: JSON.parse(row.paths_json),
84
- }));
85
- const nextShape = next.map((hold) => ({
86
- key: hold.key,
87
- reason: hold.reason,
88
- nodeId: hold.nodeId,
89
- operationKind: hold.operationKind,
90
- paths: hold.paths,
91
- }));
92
- if (JSON.stringify(currentShape) === JSON.stringify(nextShape)) return false;
93
-
94
- const firstObservedByKey = new Map(
95
- current.map((row) => [row.hold_key, row.first_observed_at]),
96
- );
97
- const now = new Date().toISOString();
98
- database.prepare(`
99
- DELETE FROM workspace_tree_observation_holds
100
- WHERE workspace_id = ? AND owner_kind = ? AND owner_id = ?
101
- `).run(workspaceId, owner.ownerKind, owner.ownerId);
102
- const insert = database.prepare(`
103
- INSERT INTO workspace_tree_observation_holds (
104
- workspace_id, hold_key, owner_kind, owner_id, reason, node_id,
105
- operation_kind, paths_json, first_observed_at
106
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
107
- `);
108
- for (const hold of next) {
109
- insert.run(
110
- workspaceId,
111
- hold.key,
112
- owner.ownerKind,
113
- owner.ownerId,
114
- hold.reason,
115
- hold.nodeId,
116
- hold.operationKind,
117
- JSON.stringify(hold.paths),
118
- firstObservedByKey.get(hold.key) || now,
119
- );
120
- }
121
- return true;
122
- }
123
-
124
- module.exports = {
125
- list,
126
- replace,
127
- };