@amalgm/shell 0.1.71 → 0.1.72
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 +8 -3
- package/dist/detection/runtime.js +99 -30
- package/dist/detection/runtime.js.map +1 -1
- package/dist/git-registration-host.d.ts +6 -0
- package/dist/git-registration-host.js +6 -4
- package/dist/git-registration-host.js.map +1 -1
- package/dist/user-ground-host.js +119 -22
- package/dist/user-ground-host.js.map +1 -1
- package/package.json +1 -1
package/dist/user-ground-host.js
CHANGED
|
@@ -16,7 +16,7 @@ import { AcceptedInboxResultIndex } from "./detection/accepted-result.js";
|
|
|
16
16
|
import { NamedDetectRuntime } from "./detection/runtime.js";
|
|
17
17
|
import { WORKSPACE_UUID as UUID, createFilesRegisterPorts, ensureWorkspaceBinding, ensureWorkspaceReference, pathExists, pathWithin, referenceWorkspaceId, selectKnownRegistrationId, workspaceBindingDir, } from "./files-register-host.js";
|
|
18
18
|
import { applyRepositoryFiles, captureRepository, inspectRepositoryTransportFile, hasGitMarker, readCachedRepositoryIdentity, } from "./git-repository-host.js";
|
|
19
|
-
import {
|
|
19
|
+
import { inspectGitRegistration, sameGitIdentity, } from "./git-registration-host.js";
|
|
20
20
|
import { projectMaterializedGraph } from "./materialized-graph.js";
|
|
21
21
|
import { NodeWatchHost, } from "./watching/index.js";
|
|
22
22
|
import { WireClient, WireRequestError } from "./wire-client.js";
|
|
@@ -2407,13 +2407,6 @@ async function reconcileGround(input) {
|
|
|
2407
2407
|
}
|
|
2408
2408
|
async function reconcileRoot(input) {
|
|
2409
2409
|
const { identity, resourceId, rootUUID, rootName, rootPath, rootType, database, cacheDir, policy, bindingDir, suspects = null, evidence, } = input;
|
|
2410
|
-
const scanSuspects = suspects !== null && rootType === "repo.git"
|
|
2411
|
-
? [...new Set(suspects.map((path) => {
|
|
2412
|
-
const segments = path.split("/");
|
|
2413
|
-
const marker = segments.indexOf(".git");
|
|
2414
|
-
return marker < 0 ? path : segments.slice(0, marker).join("/");
|
|
2415
|
-
}))].sort()
|
|
2416
|
-
: suspects;
|
|
2417
2410
|
const existingRows = input.existingRows
|
|
2418
2411
|
?? readRows(database).filter((row) => row.resourceId === resourceId && row.rootUUID === rootUUID);
|
|
2419
2412
|
const existingByPath = new Map(existingRows.flatMap((row) => {
|
|
@@ -2453,6 +2446,43 @@ async function reconcileRoot(input) {
|
|
|
2453
2446
|
}) : null,
|
|
2454
2447
|
};
|
|
2455
2448
|
}
|
|
2449
|
+
let rootRepositoryEvidence = null;
|
|
2450
|
+
let rootRepositoryInspectionFailed = false;
|
|
2451
|
+
if (rootType === "repo.git") {
|
|
2452
|
+
evidence && (evidence.gitInspections += 1);
|
|
2453
|
+
try {
|
|
2454
|
+
// A Git metadata ring can arrive before the corresponding worktree
|
|
2455
|
+
// ring. Git already knows every dirty tracked path, so those paths join
|
|
2456
|
+
// this observation and Card + Checkpoint cannot carry stale identity.
|
|
2457
|
+
rootRepositoryEvidence = inspectGitRegistration(rootPath);
|
|
2458
|
+
}
|
|
2459
|
+
catch {
|
|
2460
|
+
rootRepositoryInspectionFailed = true;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
const normalizedSuspects = suspects === null ? null : suspects.map((path) => {
|
|
2464
|
+
const segments = path.split("/");
|
|
2465
|
+
const marker = segments.indexOf(".git");
|
|
2466
|
+
return marker < 0 ? path : segments.slice(0, marker).join("/");
|
|
2467
|
+
});
|
|
2468
|
+
const changedTrackedPaths = rootRepositoryEvidence === null ? [] : [
|
|
2469
|
+
...[...rootRepositoryEvidence.trackedPaths]
|
|
2470
|
+
.filter((path) => !existingByPath.has(path)),
|
|
2471
|
+
...existingRows
|
|
2472
|
+
.filter((row) => row.relativePath !== ""
|
|
2473
|
+
&& row.payloadVersion?.startsWith("git:")
|
|
2474
|
+
&& !rootRepositoryEvidence.trackedPaths.has(row.relativePath))
|
|
2475
|
+
.map((row) => row.relativePath),
|
|
2476
|
+
];
|
|
2477
|
+
const scanSuspects = suspects !== null && rootType === "repo.git"
|
|
2478
|
+
? rootRepositoryInspectionFailed
|
|
2479
|
+
? null
|
|
2480
|
+
: [...new Set([
|
|
2481
|
+
...normalizedSuspects,
|
|
2482
|
+
...rootRepositoryEvidence.dirtyPaths,
|
|
2483
|
+
...changedTrackedPaths,
|
|
2484
|
+
])].sort()
|
|
2485
|
+
: suspects;
|
|
2456
2486
|
const entries = [];
|
|
2457
2487
|
const referenceWorkspaceIds = new Set();
|
|
2458
2488
|
const rootStats = lstatSync(rootPath);
|
|
@@ -2525,7 +2555,11 @@ async function reconcileRoot(input) {
|
|
|
2525
2555
|
return [...new Set(paths)].sort();
|
|
2526
2556
|
};
|
|
2527
2557
|
const visit = async (directory, parentUUID, parentType, base = "", activeRepository = parentType === "repo.git"
|
|
2528
|
-
? {
|
|
2558
|
+
? {
|
|
2559
|
+
root: directory,
|
|
2560
|
+
evidencePaths: repositoryEvidencePaths(directory),
|
|
2561
|
+
evidence: directory === rootPath ? rootRepositoryEvidence : null,
|
|
2562
|
+
}
|
|
2529
2563
|
: null, forceObserve = rootRailChanged) => {
|
|
2530
2564
|
const children = readdirSync(directory, { withFileTypes: true })
|
|
2531
2565
|
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
@@ -2557,13 +2591,15 @@ async function reconcileRoot(input) {
|
|
|
2557
2591
|
const stableDuringCompleteCatchUp = scanSuspects === null
|
|
2558
2592
|
&& existing !== undefined
|
|
2559
2593
|
&& reusableDuringCompleteCatchUp(existing, stats);
|
|
2594
|
+
const repositoryPath = activeRepository
|
|
2595
|
+
? slashPath(relative(activeRepository.root, absolutePath))
|
|
2596
|
+
: null;
|
|
2560
2597
|
const observe = forceObserve || existing === undefined
|
|
2598
|
+
|| (repositoryPath !== null
|
|
2599
|
+
&& activeRepository?.evidence?.dirtyPaths.has(repositoryPath) === true)
|
|
2561
2600
|
|| (scanSuspects === null ? !stableDuringCompleteCatchUp : touchesSuspicion(relativePath));
|
|
2562
2601
|
if (!observe && scanSuspects === null)
|
|
2563
2602
|
evidence && (evidence.metadataReused += 1);
|
|
2564
|
-
const repositoryPath = activeRepository
|
|
2565
|
-
? slashPath(relative(activeRepository.root, absolutePath))
|
|
2566
|
-
: null;
|
|
2567
2603
|
if (observe && repositoryPath && activeRepository && !activeRepository.evidence) {
|
|
2568
2604
|
evidence && (evidence.gitInspections += 1);
|
|
2569
2605
|
try {
|
|
@@ -2573,7 +2609,9 @@ async function reconcileRoot(input) {
|
|
|
2573
2609
|
// Classification already settled from the marker. If Git cannot
|
|
2574
2610
|
// provide clean-index evidence, read current worktree bytes; Card +
|
|
2575
2611
|
// Checkpoint capture below remains the transfer retry boundary.
|
|
2576
|
-
activeRepository.evidence = {
|
|
2612
|
+
activeRepository.evidence = {
|
|
2613
|
+
trackedPaths: new Set(), cleanLeaves: new Map(), dirtyPaths: new Set(),
|
|
2614
|
+
};
|
|
2577
2615
|
}
|
|
2578
2616
|
}
|
|
2579
2617
|
const indexed = observe && repositoryPath
|
|
@@ -2751,14 +2789,73 @@ async function reconcileRoot(input) {
|
|
|
2751
2789
|
? entry.relativePath.slice(repository.relativePath.length + 1)
|
|
2752
2790
|
: entry.relativePath;
|
|
2753
2791
|
const ownedByPath = new Map(ownedEntries.map((entry) => [repositoryPath(entry), entry]));
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2792
|
+
const refreshRepositoryIdentity = async () => {
|
|
2793
|
+
const registration = inspectGitRegistration(repository.absolutePath);
|
|
2794
|
+
const removed = new Set();
|
|
2795
|
+
for (const [path, clean] of registration.cleanLeaves) {
|
|
2796
|
+
const entry = ownedByPath.get(path);
|
|
2797
|
+
if (!entry) {
|
|
2798
|
+
throw new Error(`repository indexed path ${path} has no registered identity`);
|
|
2799
|
+
}
|
|
2800
|
+
entry.type = clean.type;
|
|
2801
|
+
entry.payloadVersion = clean.payloadVersion;
|
|
2802
|
+
entry.linkTarget = undefined;
|
|
2803
|
+
}
|
|
2804
|
+
for (const path of registration.dirtyPaths) {
|
|
2805
|
+
const entry = ownedByPath.get(path);
|
|
2806
|
+
const absolutePath = join(repository.absolutePath, ...path.split("/"));
|
|
2807
|
+
if (!existsSync(absolutePath)) {
|
|
2808
|
+
if (entry)
|
|
2809
|
+
removed.add(path);
|
|
2810
|
+
continue;
|
|
2811
|
+
}
|
|
2812
|
+
if (!entry) {
|
|
2813
|
+
throw new Error(`repository dirty path ${path} has no registered identity`);
|
|
2814
|
+
}
|
|
2815
|
+
const stats = lstatSync(absolutePath);
|
|
2816
|
+
if (stats.isSymbolicLink()) {
|
|
2817
|
+
const target = readlinkSync(absolutePath);
|
|
2818
|
+
entry.type = "link";
|
|
2819
|
+
entry.payloadVersion = sha256Hex(Buffer.from(target, "utf8"));
|
|
2820
|
+
entry.linkTarget = target;
|
|
2821
|
+
}
|
|
2822
|
+
else if (stats.isFile()) {
|
|
2823
|
+
const current = await captureContentFile(absolutePath, stats, null);
|
|
2824
|
+
entry.type = classifyFile({ binary: current.binary });
|
|
2825
|
+
entry.payloadVersion = current.contentHash;
|
|
2826
|
+
entry.linkTarget = undefined;
|
|
2827
|
+
}
|
|
2828
|
+
else {
|
|
2829
|
+
throw new Error(`repository dirty path ${path} is not a file or link`);
|
|
2830
|
+
}
|
|
2831
|
+
entry.contentVerifiedAtMs = Date.now();
|
|
2832
|
+
Object.assign(entry, statFingerprint(stats));
|
|
2833
|
+
evidence && (evidence.contentReads += 1);
|
|
2834
|
+
}
|
|
2835
|
+
if (removed.size > 0) {
|
|
2836
|
+
for (const path of removed) {
|
|
2837
|
+
const entry = ownedByPath.get(path);
|
|
2838
|
+
if (!entry)
|
|
2839
|
+
continue;
|
|
2840
|
+
const index = entries.indexOf(entry);
|
|
2841
|
+
if (index >= 0)
|
|
2842
|
+
entries.splice(index, 1);
|
|
2843
|
+
ownedByPath.delete(path);
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
return [...ownedByPath.entries()]
|
|
2847
|
+
.map(([path, entry]) => ({
|
|
2848
|
+
path,
|
|
2849
|
+
uuid: entry.uuid,
|
|
2850
|
+
type: entry.type,
|
|
2851
|
+
payloadVersion: entry.payloadVersion,
|
|
2852
|
+
}))
|
|
2853
|
+
.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
2854
|
+
};
|
|
2855
|
+
let repoIdentity = await refreshRepositoryIdentity();
|
|
2856
|
+
/* The rows and the transport identity are derived from the same fresh
|
|
2857
|
+
* worktree evidence. A later Watch ring for one of these dirty paths is
|
|
2858
|
+
* therefore ordinary equality, not a transport-only echo. */
|
|
2762
2859
|
const priorRow = existingByPath.get(repository.relativePath);
|
|
2763
2860
|
if (priorRow?.type === "repo.git") {
|
|
2764
2861
|
repository.payloadVersion = priorRow.payloadVersion;
|
|
@@ -2799,7 +2896,7 @@ async function reconcileRoot(input) {
|
|
|
2799
2896
|
let coherent = false;
|
|
2800
2897
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2801
2898
|
captured = captureRepository(repository.absolutePath, repoIdentity, prior, previousIdentity);
|
|
2802
|
-
const after =
|
|
2899
|
+
const after = await refreshRepositoryIdentity();
|
|
2803
2900
|
if (sameGitIdentity(after, repoIdentity)) {
|
|
2804
2901
|
coherent = true;
|
|
2805
2902
|
break;
|