@amalgm/shell 0.1.142 → 0.1.143
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 +9 -0
- package/dist/detection/runtime.d.ts +6 -0
- package/dist/detection/runtime.js +27 -10
- package/dist/detection/runtime.js.map +1 -1
- package/dist/entity-apply-host.d.ts +3 -0
- package/dist/entity-apply-host.js +262 -9
- package/dist/entity-apply-host.js.map +1 -1
- package/dist/user-ground-host.d.ts +3 -1
- package/dist/user-ground-host.js +105 -3
- package/dist/user-ground-host.js.map +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
package/dist/user-ground-host.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, lstatSync, realpathSync, readFileSync, readdirSync, readlin
|
|
|
3
3
|
import { open, readFile } 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, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, containedRecords, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, pathIsSuspect, CONTENT_REFS_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, upload as uploadArtifact, userGroundRecords, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
|
|
6
|
+
import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, containedRecords, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, materializedSlotNames, pathIsSuspect, CONTENT_REFS_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, upload as uploadArtifact, userGroundRecords, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
|
|
7
7
|
import Database from "better-sqlite3";
|
|
8
8
|
import { atomicCopy, atomicWrite, ensurePrivateDir, ensureUserDir } from "./filesystem.js";
|
|
9
9
|
import { emitFilesStage, } from "./files-observability.js";
|
|
@@ -1095,6 +1095,7 @@ export class UserGroundHost {
|
|
|
1095
1095
|
measurements: { type: evidence.type, phase: evidence.phase },
|
|
1096
1096
|
});
|
|
1097
1097
|
},
|
|
1098
|
+
onLawRelocationStage: this.options.onLawRelocationStage,
|
|
1098
1099
|
now: () => this.options.now?.().getTime() ?? Date.now(),
|
|
1099
1100
|
});
|
|
1100
1101
|
const settledMaterialization = host.adoptVerifiedMaterialization();
|
|
@@ -2345,6 +2346,11 @@ export class UserGroundHost {
|
|
|
2345
2346
|
if (!committed) {
|
|
2346
2347
|
throw pipelineStageError("detection", new DetectionBasisAdvancedError());
|
|
2347
2348
|
}
|
|
2349
|
+
settleMaterializedSlotAddresses(this.databasePath(identity), new Set([
|
|
2350
|
+
...notebookBasis.flatMap(({ row }) => row?.parentUUID ? [row.parentUUID] : []),
|
|
2351
|
+
...acceptedMaterializedRows.flatMap((row) => row.parentUUID ? [row.parentUUID] : []),
|
|
2352
|
+
...acceptedNotebookRows.flatMap((row) => row.parentUUID ? [row.parentUUID] : []),
|
|
2353
|
+
]));
|
|
2348
2354
|
const recordCommitDuration = performance.now() - recordCommitStarted;
|
|
2349
2355
|
if (detectedRecords.length === 0) {
|
|
2350
2356
|
this.stage({
|
|
@@ -3783,6 +3789,80 @@ function commitDetectedState(file, input) {
|
|
|
3783
3789
|
database.close();
|
|
3784
3790
|
}
|
|
3785
3791
|
}
|
|
3792
|
+
/** A local sibling-set change can alter collision-derived disk addresses
|
|
3793
|
+
* without changing any portable record. Detect commits its portable truth
|
|
3794
|
+
* first, then settles only the affected sibling groups while it still owns
|
|
3795
|
+
* their ground cone. Rows move before bytes, matching Apply's recovery law;
|
|
3796
|
+
* Watch observes the resulting renames as the same UUIDs at their newly
|
|
3797
|
+
* derived addresses and therefore emits no Record. */
|
|
3798
|
+
function settleMaterializedSlotAddresses(file, parentUUIDs) {
|
|
3799
|
+
if (parentUUIDs.size === 0)
|
|
3800
|
+
return;
|
|
3801
|
+
const entityRows = readGroundRows(file, "entities");
|
|
3802
|
+
const notebookRows = readGroundRows(file, "detection_notebook");
|
|
3803
|
+
const current = new Map(entityRows.map((row) => [row.uuid, row]));
|
|
3804
|
+
for (const row of notebookRows)
|
|
3805
|
+
current.set(row.uuid, row);
|
|
3806
|
+
const relocations = [];
|
|
3807
|
+
for (const parentUUID of parentUUIDs) {
|
|
3808
|
+
const parent = current.get(parentUUID);
|
|
3809
|
+
if (!parent)
|
|
3810
|
+
continue;
|
|
3811
|
+
const siblings = [...current.values()].filter((row) => row.parentUUID === parentUUID && row.status === "active");
|
|
3812
|
+
const derived = materializedSlotNames(siblings);
|
|
3813
|
+
for (const row of siblings) {
|
|
3814
|
+
const name = derived.get(row.uuid) ?? row.name;
|
|
3815
|
+
const destination = join(parent.absolutePath, name);
|
|
3816
|
+
if (destination === row.absolutePath)
|
|
3817
|
+
continue;
|
|
3818
|
+
relocations.push({
|
|
3819
|
+
row,
|
|
3820
|
+
destination,
|
|
3821
|
+
relativePath: [parent.relativePath, name].filter(Boolean).join("/"),
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
if (relocations.length === 0)
|
|
3826
|
+
return;
|
|
3827
|
+
const database = initializeDatabase(file);
|
|
3828
|
+
try {
|
|
3829
|
+
const update = (table) => database.prepare(`UPDATE ${table} SET relative_path = ?, absolute_path = ? WHERE uuid = ?`);
|
|
3830
|
+
const updateEntity = update("entities");
|
|
3831
|
+
const updateNotebook = update("detection_notebook");
|
|
3832
|
+
const refreshEntity = database.prepare("UPDATE entities SET changed_time_ms = ? WHERE uuid = ?");
|
|
3833
|
+
const refreshNotebook = database.prepare("UPDATE detection_notebook SET changed_time_ms = ? WHERE uuid = ?");
|
|
3834
|
+
const pending = [...relocations];
|
|
3835
|
+
while (pending.length > 0) {
|
|
3836
|
+
const sources = new Set(pending.map(({ row }) => row.absolutePath));
|
|
3837
|
+
const index = pending.findIndex(({ destination }) => !sources.has(destination) && !existsSync(destination));
|
|
3838
|
+
if (index < 0)
|
|
3839
|
+
throw new Error("derived address relocations have no free destination");
|
|
3840
|
+
const relocation = pending.splice(index, 1)[0];
|
|
3841
|
+
if (!relocation)
|
|
3842
|
+
throw new Error("derived address relocation vanished");
|
|
3843
|
+
const { row, destination, relativePath: destinationRelative } = relocation;
|
|
3844
|
+
if (!existsSync(row.absolutePath))
|
|
3845
|
+
continue;
|
|
3846
|
+
const entityDescendants = entityRows.filter((candidate) => pathWithin(row.absolutePath, candidate.absolutePath));
|
|
3847
|
+
const notebookDescendants = notebookRows.filter((candidate) => pathWithin(row.absolutePath, candidate.absolutePath));
|
|
3848
|
+
database.transaction(() => {
|
|
3849
|
+
for (const candidate of entityDescendants) {
|
|
3850
|
+
updateEntity.run(destinationRelative + candidate.relativePath.slice(row.relativePath.length), destination + candidate.absolutePath.slice(row.absolutePath.length), candidate.uuid);
|
|
3851
|
+
}
|
|
3852
|
+
for (const candidate of notebookDescendants) {
|
|
3853
|
+
updateNotebook.run(destinationRelative + candidate.relativePath.slice(row.relativePath.length), destination + candidate.absolutePath.slice(row.absolutePath.length), candidate.uuid);
|
|
3854
|
+
}
|
|
3855
|
+
})();
|
|
3856
|
+
renameSync(row.absolutePath, destination);
|
|
3857
|
+
const moved = lstatSync(destination);
|
|
3858
|
+
refreshEntity.run(moved.ctimeMs, row.uuid);
|
|
3859
|
+
refreshNotebook.run(moved.ctimeMs, row.uuid);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
finally {
|
|
3863
|
+
database.close();
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3786
3866
|
/** Journal the newest snapshot publication for its resource. A journal row
|
|
3787
3867
|
* has exactly three meanings: `pending` and never submitted — a local intent
|
|
3788
3868
|
* the authority has never seen, superseded freely (last write wins);
|
|
@@ -4877,7 +4957,11 @@ async function reconcileRoot(input) {
|
|
|
4877
4957
|
...(registeredBoundaryId ? { fixedUUID: registeredBoundaryId } : {}),
|
|
4878
4958
|
type,
|
|
4879
4959
|
parentUUID,
|
|
4880
|
-
name
|
|
4960
|
+
// The row at this exact address owns its portable name. Collision
|
|
4961
|
+
// suffixes are materialization only, so a complete walk freezes the
|
|
4962
|
+
// pre-pass address-to-identity map instead of letting an earlier
|
|
4963
|
+
// sibling deletion turn a later suffixed child into a fake rename.
|
|
4964
|
+
name: existing?.name ?? child.name,
|
|
4881
4965
|
relativePath,
|
|
4882
4966
|
absolutePath,
|
|
4883
4967
|
payloadVersion,
|
|
@@ -5465,6 +5549,23 @@ async function reconcileRoot(input) {
|
|
|
5465
5549
|
}
|
|
5466
5550
|
function portablePaths(records, boundaryUUIDs = new Set()) {
|
|
5467
5551
|
const byUuid = new Map(records.map((record) => [record.uuid, record]));
|
|
5552
|
+
const siblingsByParent = new Map();
|
|
5553
|
+
for (const record of records) {
|
|
5554
|
+
if (record.status !== "active" || record.parentUUID === null)
|
|
5555
|
+
continue;
|
|
5556
|
+
const siblings = siblingsByParent.get(record.parentUUID) ?? [];
|
|
5557
|
+
siblings.push(record);
|
|
5558
|
+
siblingsByParent.set(record.parentUUID, siblings);
|
|
5559
|
+
}
|
|
5560
|
+
const derivedNames = new Map();
|
|
5561
|
+
for (const siblings of siblingsByParent.values()) {
|
|
5562
|
+
for (const [uuid, name] of materializedSlotNames(siblings.map((record) => ({
|
|
5563
|
+
uuid: record.uuid,
|
|
5564
|
+
name: record.name,
|
|
5565
|
+
type: record.type,
|
|
5566
|
+
}))))
|
|
5567
|
+
derivedNames.set(uuid, name);
|
|
5568
|
+
}
|
|
5468
5569
|
const memo = new Map();
|
|
5469
5570
|
const resolving = new Set();
|
|
5470
5571
|
const resolve = (record) => {
|
|
@@ -5479,7 +5580,8 @@ function portablePaths(records, boundaryUUIDs = new Set()) {
|
|
|
5479
5580
|
const parent = byUuid.get(record.parentUUID);
|
|
5480
5581
|
if (!parent)
|
|
5481
5582
|
throw new Error(`cloud entity ${record.uuid} has no parent`);
|
|
5482
|
-
path = [resolve(parent), record.name]
|
|
5583
|
+
path = [resolve(parent), derivedNames.get(record.uuid) ?? record.name]
|
|
5584
|
+
.filter(Boolean).join("/");
|
|
5483
5585
|
}
|
|
5484
5586
|
resolving.delete(record.uuid);
|
|
5485
5587
|
memo.set(record.uuid, path);
|