@amalgm/shell 0.1.55 → 0.1.57
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/PURPOSE.md +3 -1
- package/dist/detection/runtime.js +16 -25
- package/dist/detection/runtime.js.map +1 -1
- package/dist/entity-apply-host.js +8 -17
- package/dist/entity-apply-host.js.map +1 -1
- package/dist/ground-root-index.d.ts +12 -0
- package/dist/ground-root-index.js +26 -0
- package/dist/ground-root-index.js.map +1 -0
- package/dist/user-ground-host.js +28 -27
- package/dist/user-ground-host.js.map +1 -1
- package/package.json +2 -2
package/dist/user-ground-host.js
CHANGED
|
@@ -3,11 +3,12 @@ import { existsSync, lstatSync, realpathSync, readFileSync, readdirSync, readlin
|
|
|
3
3
|
import { open } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { buildUserHomeManifest, buildUserManifest, liveMachineStateDir, scopedAmalgmDir, shippedUserHomeDeclaration, } from "@amalgm/core/identity";
|
|
6
|
-
import { CHUNK_BYTES, CONTENT_CONTRACT, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, download as downloadArtifact, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, parseSnapshot, pathIsSuspect, privateEntityResourceId, sameRecords, snapshotFromRecords, stableJson, travelingRecords, upload as uploadArtifact, userGroundRecords, } from "@amalgm/live";
|
|
6
|
+
import { CHUNK_BYTES, CONTENT_CONTRACT, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, download as downloadArtifact, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, parseSnapshot, pathIsSuspect, privateEntityResourceId, sameRecords, snapshotFromRecords, stableJson, travelingRecords, upload as uploadArtifact, userGroundRecords, } from "@amalgm/live";
|
|
7
7
|
import Database from "better-sqlite3";
|
|
8
8
|
import { atomicCopy, atomicWrite, ensurePrivateDir } from "./filesystem.js";
|
|
9
9
|
import { EntityApplyHost } from "./entity-apply-host.js";
|
|
10
10
|
import { EntityRecordSqliteStore } from "./entity-record-store.js";
|
|
11
|
+
import { indexRowsByOutermostRoot } from "./ground-root-index.js";
|
|
11
12
|
import { ContentCacheDownload, captureContentFile, hashContentFile, sealCapturedArtifact, } from "./content-cache-host.js";
|
|
12
13
|
import { decodeContentWireBytes, encodeContentWireBytes } from "./content-wire-codec.js";
|
|
13
14
|
import { exactTextReplay, planGroundDetection, reconcileGroundUUIDs, } from "./detection/portable.js";
|
|
@@ -1983,6 +1984,14 @@ async function reconcileGround(input) {
|
|
|
1983
1984
|
? notebook.filter((row) => materializedUUIDs.has(row.uuid))
|
|
1984
1985
|
: materialized;
|
|
1985
1986
|
const materializedByUuid = new Map(materializedBaseline.map((row) => [row.uuid, row]));
|
|
1987
|
+
const materializedByRoot = new Map();
|
|
1988
|
+
for (const row of materializedBaseline) {
|
|
1989
|
+
if (row.resourceId !== resourceId)
|
|
1990
|
+
continue;
|
|
1991
|
+
const rows = materializedByRoot.get(row.rootUUID) || [];
|
|
1992
|
+
rows.push(row);
|
|
1993
|
+
materializedByRoot.set(row.rootUUID, rows);
|
|
1994
|
+
}
|
|
1986
1995
|
const ignoreFile = join(userRoot, ".amalgmignore");
|
|
1987
1996
|
const policy = createUserGroundEnrollmentPolicy(existsSync(ignoreFile) ? readFileSync(ignoreFile, "utf8") : "");
|
|
1988
1997
|
const bindingDir = workspaceBindingDir(userRoot, identity.deviceId);
|
|
@@ -1991,6 +2000,8 @@ async function reconcileGround(input) {
|
|
|
1991
2000
|
.map(({ workspaceId, directory }) => [workspaceId, directory]));
|
|
1992
2001
|
const outerBoundRoots = boundRootEntries.filter((candidate) => !boundRootEntries.some((other) => other.workspaceId !== candidate.workspaceId
|
|
1993
2002
|
&& pathWithin(other.directory, candidate.directory)));
|
|
2003
|
+
const existingByUuid = new Map(existing.map((row) => [row.uuid, row]));
|
|
2004
|
+
const existingByOutermostRoot = indexRowsByOutermostRoot(outerBoundRoots.map(({ workspaceId, directory }) => ({ rootId: workspaceId, directory })), existing.filter((row) => row.resourceId === resourceId));
|
|
1994
2005
|
const registeredBoundaries = new Map(boundRootEntries
|
|
1995
2006
|
.map(({ workspaceId, directory }) => [resolve(directory), workspaceId]));
|
|
1996
2007
|
const suspicionFor = (root) => {
|
|
@@ -2029,7 +2040,7 @@ async function reconcileGround(input) {
|
|
|
2029
2040
|
notebookByUuid.delete(uuid);
|
|
2030
2041
|
}
|
|
2031
2042
|
const currentIds = new Set(result.rows.map((row) => row.record.uuid));
|
|
2032
|
-
for (const prior of
|
|
2043
|
+
for (const prior of materializedByRoot.get(parameters.rootUUID) || []) {
|
|
2033
2044
|
if (!currentIds.has(prior.uuid))
|
|
2034
2045
|
materializedRemovals.add(prior.uuid);
|
|
2035
2046
|
}
|
|
@@ -2081,8 +2092,8 @@ async function reconcileGround(input) {
|
|
|
2081
2092
|
suspicion: observationFor(userRoot),
|
|
2082
2093
|
});
|
|
2083
2094
|
for (const { workspaceId, directory: rootPath } of outerBoundRoots) {
|
|
2084
|
-
const existingRoot =
|
|
2085
|
-
const existingWorkspaceRows =
|
|
2095
|
+
const existingRoot = existingByUuid.get(workspaceId);
|
|
2096
|
+
const existingWorkspaceRows = existingByOutermostRoot.get(workspaceId) || [];
|
|
2086
2097
|
await scan({
|
|
2087
2098
|
identity,
|
|
2088
2099
|
resourceId,
|
|
@@ -2446,20 +2457,20 @@ async function reconcileRoot(input) {
|
|
|
2446
2457
|
// children first so one capture pass is already the fixed point.
|
|
2447
2458
|
const repositories = entries.filter((entry) => entry.type === "repo.git")
|
|
2448
2459
|
.sort((left, right) => right.relativePath.length - left.relativePath.length);
|
|
2460
|
+
const territory = indexRepositoryTerritory(entries);
|
|
2461
|
+
const previousEntries = [...existingByPath.values()];
|
|
2462
|
+
const previousTerritory = indexRepositoryTerritory(previousEntries);
|
|
2449
2463
|
const replayByUuid = new Map();
|
|
2450
|
-
const properDescendantOf = (candidate, ancestor) => ancestor === "" ? candidate !== "" : candidate.startsWith(`${ancestor}/`);
|
|
2451
|
-
const repositoryOwner = (entry) => repositories
|
|
2452
|
-
.filter((repository) => repository.uuid !== entry.uuid
|
|
2453
|
-
&& properDescendantOf(entry.relativePath, repository.relativePath))
|
|
2454
|
-
.sort((left, right) => right.relativePath.length - left.relativePath.length)[0] ?? null;
|
|
2455
2464
|
try {
|
|
2456
2465
|
for (const repository of repositories) {
|
|
2457
|
-
|
|
2458
|
-
|
|
2466
|
+
const ownedEntries = territory.ownedBy(repository.uuid);
|
|
2467
|
+
const repositoryPath = (entry) => repository.relativePath
|
|
2468
|
+
? entry.relativePath.slice(repository.relativePath.length + 1)
|
|
2469
|
+
: entry.relativePath;
|
|
2470
|
+
const ownedByPath = new Map(ownedEntries.map((entry) => [repositoryPath(entry), entry]));
|
|
2471
|
+
let repoIdentity = ownedEntries
|
|
2459
2472
|
.map((entry) => ({
|
|
2460
|
-
path:
|
|
2461
|
-
? entry.relativePath.slice(repository.relativePath.length + 1)
|
|
2462
|
-
: entry.relativePath,
|
|
2473
|
+
path: repositoryPath(entry),
|
|
2463
2474
|
uuid: entry.uuid,
|
|
2464
2475
|
type: entry.type,
|
|
2465
2476
|
payloadVersion: entry.payloadVersion,
|
|
@@ -2497,15 +2508,8 @@ async function reconcileRoot(input) {
|
|
|
2497
2508
|
}
|
|
2498
2509
|
}
|
|
2499
2510
|
}
|
|
2500
|
-
const previousRepositories = [...existingByPath.values()]
|
|
2501
|
-
.filter((row) => row.type === "repo.git");
|
|
2502
|
-
const previousOwner = (row) => previousRepositories
|
|
2503
|
-
.filter((candidate) => candidate.uuid !== row.uuid
|
|
2504
|
-
&& properDescendantOf(row.relativePath, candidate.relativePath))
|
|
2505
|
-
.sort((left, right) => right.relativePath.length - left.relativePath.length)[0] ?? null;
|
|
2506
2511
|
const previousIdentity = prior && priorRow
|
|
2507
|
-
?
|
|
2508
|
-
.filter((row) => previousOwner(row)?.uuid === priorRow.uuid)
|
|
2512
|
+
? previousTerritory.ownedBy(priorRow.uuid)
|
|
2509
2513
|
.map((row) => ({
|
|
2510
2514
|
path: priorRow.relativePath
|
|
2511
2515
|
? row.relativePath.slice(priorRow.relativePath.length + 1)
|
|
@@ -2532,10 +2536,7 @@ async function reconcileRoot(input) {
|
|
|
2532
2536
|
throw new Error(`repository did not settle during capture: ${repository.absolutePath}`);
|
|
2533
2537
|
}
|
|
2534
2538
|
for (const identityEntry of repoIdentity) {
|
|
2535
|
-
const entry =
|
|
2536
|
-
&& (repository.relativePath
|
|
2537
|
-
? candidate.relativePath.slice(repository.relativePath.length + 1)
|
|
2538
|
-
: candidate.relativePath) === identityEntry.path);
|
|
2539
|
+
const entry = ownedByPath.get(identityEntry.path);
|
|
2539
2540
|
if (!entry)
|
|
2540
2541
|
continue;
|
|
2541
2542
|
entry.type = identityEntry.type;
|
|
@@ -2625,7 +2626,7 @@ async function reconcileRoot(input) {
|
|
|
2625
2626
|
const detected = rows.map((row) => {
|
|
2626
2627
|
const base = beforeByUuid.get(row.record.uuid) ?? null;
|
|
2627
2628
|
const entry = entryByUuid.get(row.record.uuid);
|
|
2628
|
-
if (row.record.type !== "repo.git" && entry &&
|
|
2629
|
+
if (row.record.type !== "repo.git" && entry && territory.ownerOf(entry)) {
|
|
2629
2630
|
return {
|
|
2630
2631
|
record: row.record,
|
|
2631
2632
|
relativePath: row.relativePath,
|