amalgm 0.1.238 → 0.1.240
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/lib/shared-realtime-tunnel.js +22 -14
- package/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
- package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
- package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
- package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
- package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
- package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
- package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
- package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
- package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
- package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
- package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
- 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 +72 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
- package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Native workspace file transaction
|
|
2
|
+
|
|
3
|
+
This helper is the only code allowed to observe or cross an ordered file or
|
|
4
|
+
Git-metadata mutation. JavaScript cannot express
|
|
5
|
+
`openat`/`renameat`/`linkat`, so path confinement at that layer has an
|
|
6
|
+
unavoidable check/use race. The helper opens each parent directory with
|
|
7
|
+
`O_NOFOLLOW` and performs every observation and mutation relative to those
|
|
8
|
+
held descriptors.
|
|
9
|
+
|
|
10
|
+
`main.go` owns the transaction and crash ordering. `move_*.go` consumes a
|
|
11
|
+
prepared target without replacing a concurrent destination; its disappearance
|
|
12
|
+
is the crossing receipt. `writers_*.go` proves that an extracted source inode
|
|
13
|
+
has no pre-existing writer before it can be retired. The checked-in binaries
|
|
14
|
+
are required because managed runtimes do not include a Go compiler.
|
|
15
|
+
`manifest.json` binds them to the exact source bytes.
|
|
16
|
+
|
|
17
|
+
The `git-lock` backend prepares index or HEAD bytes inside the linked
|
|
18
|
+
worktree's private Git directory, hard-links them to Git's canonical lock, and
|
|
19
|
+
renames that lock over the destination. The retained hard link identifies the
|
|
20
|
+
crossing even after later Git activity changes the destination or creates a
|
|
21
|
+
new lock.
|
|
22
|
+
|
|
23
|
+
Rebuild and verify both release platforms:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
node build.mjs
|
|
27
|
+
node verify.mjs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The ordinary JavaScript transition tests execute the host binary against real
|
|
31
|
+
Git repositories, crashes, open descriptors, and directory-swap races.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const sources = fs.readdirSync(root)
|
|
9
|
+
.filter((name) => name.endsWith('.go') || name === 'go.mod' || name === 'go.sum')
|
|
10
|
+
.sort();
|
|
11
|
+
const platforms = [
|
|
12
|
+
{ directory: 'darwin-arm64', goos: 'darwin', goarch: 'arm64', cgo: '1' },
|
|
13
|
+
{ directory: 'linux-x64', goos: 'linux', goarch: 'amd64', cgo: '0' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function digestFiles(names) {
|
|
17
|
+
const hash = crypto.createHash('sha256');
|
|
18
|
+
for (const name of names) {
|
|
19
|
+
hash.update(name);
|
|
20
|
+
hash.update('\0');
|
|
21
|
+
hash.update(fs.readFileSync(path.join(root, name)));
|
|
22
|
+
hash.update('\0');
|
|
23
|
+
}
|
|
24
|
+
return hash.digest('hex');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(command, args, env = {}) {
|
|
28
|
+
const result = spawnSync(command, args, {
|
|
29
|
+
cwd: root,
|
|
30
|
+
env: { ...process.env, ...env },
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
});
|
|
33
|
+
if (result.error) throw result.error;
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
throw new Error(`${command} exited ${result.status}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
run('go', ['test', './...']);
|
|
40
|
+
const binaries = {};
|
|
41
|
+
for (const platform of platforms) {
|
|
42
|
+
const directory = path.join(root, 'bin', platform.directory);
|
|
43
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
44
|
+
const output = path.join(directory, 'amalgm-file-transaction');
|
|
45
|
+
run('go', [
|
|
46
|
+
'build',
|
|
47
|
+
'-trimpath',
|
|
48
|
+
'-ldflags=-s -w',
|
|
49
|
+
'-o',
|
|
50
|
+
output,
|
|
51
|
+
'.',
|
|
52
|
+
], {
|
|
53
|
+
GOOS: platform.goos,
|
|
54
|
+
GOARCH: platform.goarch,
|
|
55
|
+
CGO_ENABLED: platform.cgo,
|
|
56
|
+
});
|
|
57
|
+
fs.chmodSync(output, 0o755);
|
|
58
|
+
binaries[platform.directory] = crypto
|
|
59
|
+
.createHash('sha256')
|
|
60
|
+
.update(fs.readFileSync(output))
|
|
61
|
+
.digest('hex');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(
|
|
65
|
+
path.join(root, 'manifest.json'),
|
|
66
|
+
`${JSON.stringify({
|
|
67
|
+
contract: 'amalgm-native-file-transaction-build@1',
|
|
68
|
+
sourceSha256: digestFiles(sources),
|
|
69
|
+
sources,
|
|
70
|
+
binaries,
|
|
71
|
+
}, null, 2)}\n`,
|
|
72
|
+
);
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execFileSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
function executable() {
|
|
8
|
+
const platform = `${process.platform}-${process.arch}`;
|
|
9
|
+
if (!['darwin-arm64', 'linux-x64'].includes(platform)) {
|
|
10
|
+
throw new Error(`native file transactions do not support ${platform}`);
|
|
11
|
+
}
|
|
12
|
+
const file = path.join(
|
|
13
|
+
__dirname,
|
|
14
|
+
'bin',
|
|
15
|
+
platform,
|
|
16
|
+
'amalgm-file-transaction',
|
|
17
|
+
);
|
|
18
|
+
if (!fs.existsSync(file)) {
|
|
19
|
+
throw new Error(`native file transaction helper is missing for ${platform}`);
|
|
20
|
+
}
|
|
21
|
+
return file;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run(transaction, action, content = null, testHooks = {}) {
|
|
25
|
+
const target = transaction.target == null
|
|
26
|
+
? null
|
|
27
|
+
: {
|
|
28
|
+
...transaction.target,
|
|
29
|
+
...(content === null ? {} : { content: content.toString('base64') }),
|
|
30
|
+
};
|
|
31
|
+
let stdout;
|
|
32
|
+
try {
|
|
33
|
+
stdout = execFileSync(executable(), [], {
|
|
34
|
+
input: JSON.stringify({
|
|
35
|
+
action,
|
|
36
|
+
...(transaction.backend ? { backend: transaction.backend } : {}),
|
|
37
|
+
...(transaction.pid ? { pid: transaction.pid } : {}),
|
|
38
|
+
...(transaction.root ? {
|
|
39
|
+
root: transaction.root,
|
|
40
|
+
rootDev: transaction.rootDev,
|
|
41
|
+
rootIno: transaction.rootIno,
|
|
42
|
+
...(transaction.source ? { source: transaction.source } : {}),
|
|
43
|
+
...(transaction.sourceIdentity
|
|
44
|
+
? { sourceIdentity: transaction.sourceIdentity }
|
|
45
|
+
: {}),
|
|
46
|
+
...(transaction.sourceParentIdentity
|
|
47
|
+
? { sourceParentIdentity: transaction.sourceParentIdentity }
|
|
48
|
+
: {}),
|
|
49
|
+
...(transaction.targetParentIdentity
|
|
50
|
+
? { targetParentIdentity: transaction.targetParentIdentity }
|
|
51
|
+
: {}),
|
|
52
|
+
path: transaction.path,
|
|
53
|
+
id: transaction.id,
|
|
54
|
+
operation: transaction.operation,
|
|
55
|
+
expected: transaction.expected,
|
|
56
|
+
target,
|
|
57
|
+
} : {}),
|
|
58
|
+
...(testHooks.pauseAfterParentOpen > 0
|
|
59
|
+
? { testPauseAfterParentOpenMillis: testHooks.pauseAfterParentOpen }
|
|
60
|
+
: {}),
|
|
61
|
+
...(testHooks.pauseAfterExtraction > 0
|
|
62
|
+
? { testPauseAfterExtractionMillis: testHooks.pauseAfterExtraction }
|
|
63
|
+
: {}),
|
|
64
|
+
...(testHooks.pauseBeforeInstall > 0
|
|
65
|
+
? { testPauseBeforeInstallMillis: testHooks.pauseBeforeInstall }
|
|
66
|
+
: {}),
|
|
67
|
+
...(testHooks.pauseAfterTreeMove > 0
|
|
68
|
+
? { testPauseAfterTreeMoveMillis: testHooks.pauseAfterTreeMove }
|
|
69
|
+
: {}),
|
|
70
|
+
...(testHooks.crashAfterExtraction === true
|
|
71
|
+
? { testCrashAfterExtraction: true }
|
|
72
|
+
: {}),
|
|
73
|
+
...(testHooks.crashBeforeInstall === true
|
|
74
|
+
? { testCrashBeforeInstall: true }
|
|
75
|
+
: {}),
|
|
76
|
+
...(testHooks.crashAfterInstall === true
|
|
77
|
+
? { testCrashAfterInstall: true }
|
|
78
|
+
: {}),
|
|
79
|
+
...(testHooks.crashAfterTreeMove === true
|
|
80
|
+
? { testCrashAfterTreeMove: true }
|
|
81
|
+
: {}),
|
|
82
|
+
}),
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
maxBuffer: action === 'observe' ? 32 * 1024 * 1024 : 1024 * 1024,
|
|
85
|
+
timeout: transaction.backend === 'process-identity' ? 5_000 : 30_000,
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const detail = String(error.stdout || error.stderr || error.message).trim();
|
|
89
|
+
throw new Error(`native file transaction failed: ${detail}`);
|
|
90
|
+
}
|
|
91
|
+
let result;
|
|
92
|
+
try {
|
|
93
|
+
result = JSON.parse(stdout);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new Error(`native file transaction returned invalid output: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
if (![
|
|
98
|
+
'applied',
|
|
99
|
+
'changed',
|
|
100
|
+
'pending',
|
|
101
|
+
'crossed',
|
|
102
|
+
'not-crossed',
|
|
103
|
+
'released',
|
|
104
|
+
'guarded',
|
|
105
|
+
'identified',
|
|
106
|
+
'exited',
|
|
107
|
+
'observed',
|
|
108
|
+
].includes(result?.status)) {
|
|
109
|
+
throw new Error(`native file transaction returned ${result?.status || 'no status'}`);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function observe(transaction, testHooks = {}) {
|
|
115
|
+
return run({
|
|
116
|
+
root: transaction.root,
|
|
117
|
+
rootDev: transaction.rootDev,
|
|
118
|
+
rootIno: transaction.rootIno,
|
|
119
|
+
path: transaction.path,
|
|
120
|
+
target: null,
|
|
121
|
+
}, 'observe', null, testHooks);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function processLifetime(pid) {
|
|
125
|
+
try {
|
|
126
|
+
const result = run({
|
|
127
|
+
backend: 'process-identity',
|
|
128
|
+
pid,
|
|
129
|
+
target: null,
|
|
130
|
+
}, 'identity');
|
|
131
|
+
if (result.status === 'exited') return { live: false, identity: null };
|
|
132
|
+
if (result.status === 'identified' && typeof result.identity === 'string') {
|
|
133
|
+
return { live: true, identity: result.identity };
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function moveNoReplace(transaction, testHooks = {}) {
|
|
142
|
+
return run({
|
|
143
|
+
backend: 'tree-rename',
|
|
144
|
+
root: transaction.root,
|
|
145
|
+
rootDev: transaction.rootDev,
|
|
146
|
+
rootIno: transaction.rootIno,
|
|
147
|
+
source: transaction.source,
|
|
148
|
+
sourceIdentity: transaction.sourceIdentity,
|
|
149
|
+
sourceParentIdentity: transaction.sourceParentIdentity,
|
|
150
|
+
targetParentIdentity: transaction.targetParentIdentity,
|
|
151
|
+
path: transaction.path,
|
|
152
|
+
id: transaction.id,
|
|
153
|
+
operation: transaction.operation,
|
|
154
|
+
expected: null,
|
|
155
|
+
target: null,
|
|
156
|
+
}, 'move', null, testHooks);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
executable,
|
|
161
|
+
moveNoReplace,
|
|
162
|
+
observe,
|
|
163
|
+
processLifetime,
|
|
164
|
+
run,
|
|
165
|
+
};
|