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.
Files changed (86) hide show
  1. package/lib/shared-realtime-tunnel.js +22 -14
  2. package/package.json +1 -1
  3. package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
  4. package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
  5. package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
  6. package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
  7. package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
  8. package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
  9. package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
  10. package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
  11. package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
  12. package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
  13. package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
  14. package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
  15. package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
  16. package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
  17. package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
  18. package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
  19. package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
  20. package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
  21. package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
  22. package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
  23. package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
  24. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
  25. package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
  26. package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
  27. package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
  28. package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
  29. package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +81 -3
  30. package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +114 -8
  31. package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
  32. package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
  33. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
  34. package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
  35. package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
  36. package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
  37. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
  38. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
  39. package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
  40. package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
  41. package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
  42. package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
  43. package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
  44. package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
  45. package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
  46. package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
  47. package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
  48. package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
  49. package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
  50. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
  51. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
  52. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
  53. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
  54. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
  55. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
  56. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
  57. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
  58. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
  59. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
  60. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
  61. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
  62. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
  63. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
  64. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
  65. package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
  66. package/runtime/scripts/amalgm-mcp/workspace/objects.js +119 -54
  67. package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
  68. package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
  69. package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
  70. package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
  71. package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
  72. package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
  73. package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
  74. package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
  75. package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
  76. package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
  77. package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
  78. package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
  79. package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
  80. package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
  81. package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
  82. package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
  83. package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
  84. package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
  85. package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
  86. package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
@@ -0,0 +1,19 @@
1
+ {
2
+ "contract": "amalgm-native-file-transaction-build@1",
3
+ "sourceSha256": "728af934075f99e5d102edb375e0f4d1ea7fcae3e35751125d9e4fb9ec0b95f7",
4
+ "sources": [
5
+ "go.mod",
6
+ "go.sum",
7
+ "main.go",
8
+ "move_darwin.go",
9
+ "move_linux.go",
10
+ "process_darwin.go",
11
+ "process_linux.go",
12
+ "writers_darwin.go",
13
+ "writers_linux.go"
14
+ ],
15
+ "binaries": {
16
+ "darwin-arm64": "5bcef1a258b8988a56bfbe57acf3d1df4f94351d9e5e693670b47b101bd2367a",
17
+ "linux-x64": "b559ae0ac5668f075720cc1e26239c5a0bc47599ba09d91f1c14918ac60206e7"
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ //go:build darwin
2
+
3
+ package main
4
+
5
+ import "golang.org/x/sys/unix"
6
+
7
+ // moveNoReplace consumes the prepared transaction artifact only when the
8
+ // destination is still absent. The artifact's disappearance is therefore
9
+ // the operation-owned receipt that installation crossed.
10
+ func moveNoReplace(fromFD int, from string, toFD int, to string) error {
11
+ return unix.RenameatxNp(fromFD, from, toFD, to, unix.RENAME_EXCL)
12
+ }
13
+
14
+ // exchangeEntries atomically replaces a shared namespace with its guard
15
+ // marker while retaining the original namespace at the prepared path.
16
+ func exchangeEntries(leftFD int, left string, rightFD int, right string) error {
17
+ return unix.RenameatxNp(leftFD, left, rightFD, right, unix.RENAME_SWAP)
18
+ }
@@ -0,0 +1,18 @@
1
+ //go:build linux
2
+
3
+ package main
4
+
5
+ import "golang.org/x/sys/unix"
6
+
7
+ // moveNoReplace consumes the prepared transaction artifact only when the
8
+ // destination is still absent. The artifact's disappearance is therefore
9
+ // the operation-owned receipt that installation crossed.
10
+ func moveNoReplace(fromFD int, from string, toFD int, to string) error {
11
+ return unix.Renameat2(fromFD, from, toFD, to, unix.RENAME_NOREPLACE)
12
+ }
13
+
14
+ // exchangeEntries atomically replaces a shared namespace with its guard
15
+ // marker while retaining the original namespace at the prepared path.
16
+ func exchangeEntries(leftFD int, left string, rightFD int, right string) error {
17
+ return unix.Renameat2(leftFD, left, rightFD, right, unix.RENAME_EXCHANGE)
18
+ }
@@ -0,0 +1,33 @@
1
+ //go:build darwin
2
+
3
+ package main
4
+
5
+ import (
6
+ "errors"
7
+ "fmt"
8
+
9
+ "golang.org/x/sys/unix"
10
+ )
11
+
12
+ const darwinZombieState = int8(5)
13
+
14
+ func processIdentity(pid int) (string, bool, error) {
15
+ if err := unix.Kill(pid, 0); err != nil && errors.Is(err, unix.ESRCH) {
16
+ return "", false, nil
17
+ }
18
+ info, err := unix.SysctlKinfoProc("kern.proc.pid", pid)
19
+ if err != nil {
20
+ if errors.Is(err, unix.ESRCH) || errors.Is(err, unix.ENOENT) {
21
+ return "", false, nil
22
+ }
23
+ return "", false, err
24
+ }
25
+ if int(info.Proc.P_pid) != pid {
26
+ return "", false, nil
27
+ }
28
+ if info.Proc.P_stat == darwinZombieState {
29
+ return "", false, nil
30
+ }
31
+ started := info.Proc.P_starttime
32
+ return fmt.Sprintf("%d:%d:%d", pid, started.Sec, started.Usec), true, nil
33
+ }
@@ -0,0 +1,47 @@
1
+ //go:build linux
2
+
3
+ package main
4
+
5
+ import (
6
+ "fmt"
7
+ "os"
8
+ "strconv"
9
+ "strings"
10
+ )
11
+
12
+ func processIdentity(pid int) (string, bool, error) {
13
+ bootID, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
14
+ if err != nil {
15
+ return "", false, err
16
+ }
17
+ boot := strings.TrimSpace(string(bootID))
18
+ if boot == "" {
19
+ return "", false, fmt.Errorf("Linux boot identity is empty")
20
+ }
21
+ content, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
22
+ if err != nil {
23
+ if os.IsNotExist(err) {
24
+ return "", false, nil
25
+ }
26
+ return "", false, err
27
+ }
28
+ closing := strings.LastIndexByte(string(content), ')')
29
+ if closing < 0 || closing+2 >= len(content) {
30
+ return "", false, errorsNewProcessStat(pid)
31
+ }
32
+ fields := strings.Fields(string(content[closing+2:]))
33
+ if len(fields) <= 19 {
34
+ return "", false, errorsNewProcessStat(pid)
35
+ }
36
+ if fields[0] == "Z" || fields[0] == "X" || fields[0] == "x" {
37
+ return "", false, nil
38
+ }
39
+ if _, err := strconv.ParseUint(fields[19], 10, 64); err != nil {
40
+ return "", false, errorsNewProcessStat(pid)
41
+ }
42
+ return fmt.Sprintf("%s:%d:%s", boot, pid, fields[19]), true, nil
43
+ }
44
+
45
+ func errorsNewProcessStat(pid int) error {
46
+ return fmt.Errorf("process %d has an invalid stat identity", pid)
47
+ }
@@ -0,0 +1,43 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = path.dirname(fileURLToPath(import.meta.url));
7
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
8
+ if (manifest.contract !== 'amalgm-native-file-transaction-build@1') {
9
+ throw new Error('native file transaction manifest contract is invalid');
10
+ }
11
+
12
+ function digestFiles(names) {
13
+ const hash = crypto.createHash('sha256');
14
+ for (const name of names) {
15
+ hash.update(name);
16
+ hash.update('\0');
17
+ hash.update(fs.readFileSync(path.join(root, name)));
18
+ hash.update('\0');
19
+ }
20
+ return hash.digest('hex');
21
+ }
22
+
23
+ const sourceDigest = digestFiles(manifest.sources);
24
+ if (sourceDigest !== manifest.sourceSha256) {
25
+ throw new Error(
26
+ `native file transaction source is ${sourceDigest}, expected ${manifest.sourceSha256}`,
27
+ );
28
+ }
29
+ for (const [platform, expected] of Object.entries(manifest.binaries)) {
30
+ const binary = path.join(root, 'bin', platform, 'amalgm-file-transaction');
31
+ const actual = crypto.createHash('sha256').update(fs.readFileSync(binary)).digest('hex');
32
+ if (actual !== expected) {
33
+ throw new Error(`native file transaction ${platform} is ${actual}, expected ${expected}`);
34
+ }
35
+ if ((fs.statSync(binary).mode & 0o111) === 0) {
36
+ throw new Error(`native file transaction ${platform} is not executable`);
37
+ }
38
+ }
39
+
40
+ console.log(
41
+ `[native-file-transaction] verified source ${manifest.sourceSha256} `
42
+ + `and ${Object.keys(manifest.binaries).length} binaries`,
43
+ );
@@ -0,0 +1,114 @@
1
+ //go:build darwin
2
+
3
+ package main
4
+
5
+ /*
6
+ #cgo LDFLAGS: -lproc
7
+
8
+ #include <errno.h>
9
+ #include <libproc.h>
10
+ #include <stdint.h>
11
+ #include <stdlib.h>
12
+ #include <sys/fcntl.h>
13
+ #include <sys/proc_info.h>
14
+
15
+ static int amalgm_has_writable_vnode(uint64_t device, uint64_t inode) {
16
+ int pid_bytes = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
17
+ if (pid_bytes <= 0) {
18
+ return -1;
19
+ }
20
+ pid_bytes *= 2;
21
+ pid_t *pids = (pid_t *)calloc(1, (size_t)pid_bytes);
22
+ if (pids == NULL) {
23
+ return -1;
24
+ }
25
+ int listed_pid_bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, pid_bytes);
26
+ if (listed_pid_bytes < 0) {
27
+ free(pids);
28
+ return -1;
29
+ }
30
+ int pid_count = listed_pid_bytes / (int)sizeof(pid_t);
31
+ for (int pid_index = 0; pid_index < pid_count; pid_index++) {
32
+ pid_t pid = pids[pid_index];
33
+ if (pid <= 0) {
34
+ continue;
35
+ }
36
+ int fd_bytes = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
37
+ if (fd_bytes <= 0) {
38
+ continue;
39
+ }
40
+ fd_bytes *= 2;
41
+ struct proc_fdinfo *fds = (struct proc_fdinfo *)calloc(1, (size_t)fd_bytes);
42
+ if (fds == NULL) {
43
+ free(pids);
44
+ return -1;
45
+ }
46
+ int listed_fd_bytes = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds, fd_bytes);
47
+ if (listed_fd_bytes <= 0) {
48
+ free(fds);
49
+ continue;
50
+ }
51
+ int fd_count = listed_fd_bytes / PROC_PIDLISTFD_SIZE;
52
+ for (int fd_index = 0; fd_index < fd_count; fd_index++) {
53
+ if (fds[fd_index].proc_fdtype != PROX_FDTYPE_VNODE) {
54
+ continue;
55
+ }
56
+ struct vnode_fdinfo info;
57
+ int info_bytes = proc_pidfdinfo(
58
+ pid,
59
+ fds[fd_index].proc_fd,
60
+ PROC_PIDFDVNODEINFO,
61
+ &info,
62
+ PROC_PIDFDVNODEINFO_SIZE
63
+ );
64
+ if (info_bytes != PROC_PIDFDVNODEINFO_SIZE) {
65
+ continue;
66
+ }
67
+ struct vinfo_stat stat = info.pvi.vi_stat;
68
+ if (
69
+ (uint64_t)stat.vst_dev == device
70
+ && stat.vst_ino == inode
71
+ && (info.pfi.fi_openflags & FWRITE) != 0
72
+ ) {
73
+ free(fds);
74
+ free(pids);
75
+ return 1;
76
+ }
77
+ }
78
+ free(fds);
79
+ }
80
+ free(pids);
81
+ return 0;
82
+ }
83
+ */
84
+ import "C"
85
+
86
+ import (
87
+ "errors"
88
+
89
+ "golang.org/x/sys/unix"
90
+ )
91
+
92
+ // The source pathname has already been removed, so only a descriptor opened
93
+ // before extraction can still mutate its inode. Two complete libproc scans
94
+ // avoid process-spawn cost and require two independently empty observations
95
+ // before the old inode can be retired.
96
+ func hasWritableOpen(dirFD int, name, _ string) (bool, error) {
97
+ var source unix.Stat_t
98
+ if err := unix.Fstatat(dirFD, name, &source, unix.AT_SYMLINK_NOFOLLOW); err != nil {
99
+ return false, err
100
+ }
101
+ for pass := 0; pass < 2; pass++ {
102
+ result := int(C.amalgm_has_writable_vnode(
103
+ C.uint64_t(uint64(source.Dev)),
104
+ C.uint64_t(uint64(source.Ino)),
105
+ ))
106
+ if result < 0 {
107
+ return false, errors.New("libproc could not enumerate writable vnode owners")
108
+ }
109
+ if result > 0 {
110
+ return true, nil
111
+ }
112
+ }
113
+ return false, nil
114
+ }
@@ -0,0 +1,84 @@
1
+ //go:build linux
2
+
3
+ package main
4
+
5
+ import (
6
+ "errors"
7
+ "fmt"
8
+ "os"
9
+ "path/filepath"
10
+ "strconv"
11
+ "strings"
12
+
13
+ "golang.org/x/sys/unix"
14
+ )
15
+
16
+ // A successful kernel write lease proves that no other process has the inode
17
+ // open. If the filesystem cannot grant leases, /proc supplies a conservative
18
+ // fallback that treats every writable descriptor as ownership.
19
+ func hasWritableOpen(dirFD int, name, _ string) (bool, error) {
20
+ fd, err := unix.Openat(dirFD, name, unix.O_RDWR|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
21
+ if err == nil {
22
+ if _, leaseErr := unix.FcntlInt(uintptr(fd), unix.F_SETLEASE, unix.F_WRLCK); leaseErr == nil {
23
+ _, _ = unix.FcntlInt(uintptr(fd), unix.F_SETLEASE, unix.F_UNLCK)
24
+ _ = unix.Close(fd)
25
+ return false, nil
26
+ }
27
+ _ = unix.Close(fd)
28
+ }
29
+
30
+ var source unix.Stat_t
31
+ if err := unix.Fstatat(dirFD, name, &source, unix.AT_SYMLINK_NOFOLLOW); err != nil {
32
+ return false, err
33
+ }
34
+ processes, err := os.ReadDir("/proc")
35
+ if err != nil {
36
+ return false, fmt.Errorf("read /proc: %w", err)
37
+ }
38
+ for _, process := range processes {
39
+ if !process.IsDir() {
40
+ continue
41
+ }
42
+ if _, ok := parseFD(process.Name()); !ok {
43
+ continue
44
+ }
45
+ fdDirectory := filepath.Join("/proc", process.Name(), "fd")
46
+ descriptors, readErr := os.ReadDir(fdDirectory)
47
+ if readErr != nil {
48
+ if errors.Is(readErr, os.ErrPermission) || errors.Is(readErr, os.ErrNotExist) {
49
+ continue
50
+ }
51
+ return false, readErr
52
+ }
53
+ for _, descriptor := range descriptors {
54
+ fdNumber, ok := parseFD(descriptor.Name())
55
+ if !ok {
56
+ continue
57
+ }
58
+ var held unix.Stat_t
59
+ fdPath := filepath.Join(fdDirectory, descriptor.Name())
60
+ if err := unix.Stat(fdPath, &held); err != nil {
61
+ continue
62
+ }
63
+ if uint64(held.Dev) != uint64(source.Dev) || uint64(held.Ino) != uint64(source.Ino) {
64
+ continue
65
+ }
66
+ info, readErr := os.ReadFile(
67
+ filepath.Join("/proc", process.Name(), "fdinfo", strconv.Itoa(fdNumber)),
68
+ )
69
+ if readErr != nil {
70
+ continue
71
+ }
72
+ for _, line := range strings.Split(string(info), "\n") {
73
+ if !strings.HasPrefix(line, "flags:") {
74
+ continue
75
+ }
76
+ flags, parseErr := strconv.ParseUint(strings.TrimSpace(strings.TrimPrefix(line, "flags:")), 8, 64)
77
+ if parseErr == nil && int(flags)&unix.O_ACCMODE != unix.O_RDONLY {
78
+ return true, nil
79
+ }
80
+ }
81
+ }
82
+ }
83
+ return false, nil
84
+ }