@amalgm/shell 0.1.75 → 0.1.76
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/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/entity-apply-host.js +44 -3
- package/dist/entity-apply-host.js.map +1 -1
- package/dist/git-repository-host.js +19 -1
- package/dist/git-repository-host.js.map +1 -1
- package/dist/user-ground-host.d.ts +7 -2
- package/dist/user-ground-host.js +110 -60
- package/dist/user-ground-host.js.map +1 -1
- package/dist/wire-client.d.ts +9 -0
- package/dist/wire-client.js +79 -20
- package/dist/wire-client.js.map +1 -1
- package/package.json +2 -2
package/dist/user-ground-host.js
CHANGED
|
@@ -333,66 +333,85 @@ export class UserGroundHost {
|
|
|
333
333
|
return { register, add };
|
|
334
334
|
}
|
|
335
335
|
/** Resume the same host effect used by the SDK command before readiness.
|
|
336
|
-
* Staged ground has no binding, so recovery cannot race Watch or Detect.
|
|
336
|
+
* Staged ground has no binding, so recovery cannot race Watch or Detect.
|
|
337
|
+
* Each intent is one workspace's durable responsibility, not the machine's:
|
|
338
|
+
* an intent that cannot be satisfied now is reported and stays durable for
|
|
339
|
+
* the next start or an explicit re-Add, while readiness proceeds. */
|
|
337
340
|
async resumeWorkspaceAdds(identity) {
|
|
338
|
-
const
|
|
339
|
-
const userRoot = this.userRoot(identity);
|
|
340
|
-
const bindingDir = workspaceBindingDir(userRoot, identity.deviceId);
|
|
341
|
-
for (const intent of readWorkspaceAddIntents(database)) {
|
|
342
|
-
const release = await this.acquireGroundScope([
|
|
343
|
-
intent.destinationPath,
|
|
344
|
-
intent.stagingPath,
|
|
345
|
-
userRoot,
|
|
346
|
-
]);
|
|
347
|
-
const visibleAcrossBlindInterval = pathExists(intent.destinationPath);
|
|
341
|
+
for (const intent of readWorkspaceAddIntents(this.databasePath(identity))) {
|
|
348
342
|
try {
|
|
349
|
-
|
|
350
|
-
const stagedAcrossBlindInterval = pathExists(intent.stagingPath);
|
|
351
|
-
if (stagedAcrossBlindInterval && hasRootRows(database, intent.resourceId, intent.workspaceId)) {
|
|
352
|
-
// Rows prove that staging was verified before the crash, not that its
|
|
353
|
-
// bytes remained unchanged while no watcher existed. Return this
|
|
354
|
-
// private install to the intent-only stage and reproduce it from the
|
|
355
|
-
// verified immutable cache before making it visible.
|
|
356
|
-
deleteRootRows(database, intent.resourceId, intent.workspaceId);
|
|
357
|
-
}
|
|
358
|
-
await installCloudWorkspace({
|
|
359
|
-
identity,
|
|
360
|
-
userRoot,
|
|
361
|
-
database,
|
|
362
|
-
cacheDir: this.cacheDir(identity),
|
|
363
|
-
bindingDir,
|
|
364
|
-
resourceId: intent.resourceId,
|
|
365
|
-
workspace: selection.workspace,
|
|
366
|
-
reference: selection.reference,
|
|
367
|
-
references: intent.references,
|
|
368
|
-
records: intent.records,
|
|
369
|
-
destinationParent: dirname(intent.destinationPath),
|
|
370
|
-
cloudHead: intent.cloudHead,
|
|
371
|
-
readContent: (artifact) => this.downloadContent(intent.resourceId, this.cacheDir(identity), artifact, 10, { journey: "add", workspaceId: intent.workspaceId }),
|
|
372
|
-
onStage: (stage) => this.options.onWorkspaceAddStage?.({
|
|
373
|
-
workspaceId: intent.workspaceId,
|
|
374
|
-
stage,
|
|
375
|
-
}),
|
|
376
|
-
});
|
|
377
|
-
this.ensureWatchers(identity, visibleAcrossBlindInterval ? [] : [intent.workspaceId]);
|
|
378
|
-
assertHealthyWatch(this.watchHost.evidence(), intent.workspaceId);
|
|
343
|
+
await this.resumeWorkspaceAdd(identity, intent);
|
|
379
344
|
}
|
|
380
|
-
|
|
381
|
-
|
|
345
|
+
catch (error) {
|
|
346
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
347
|
+
this.stage({
|
|
348
|
+
primitive: "download",
|
|
349
|
+
stage: "add-resume",
|
|
350
|
+
status: "failed",
|
|
351
|
+
workspaceId: intent.workspaceId,
|
|
352
|
+
measurements: { error: message },
|
|
353
|
+
});
|
|
354
|
+
this.options.onApplyError?.(new Error(`workspace Add ${intent.workspaceId} could not resume and stays pending: ${message}`, { cause: error }));
|
|
382
355
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async resumeWorkspaceAdd(identity, intent) {
|
|
359
|
+
const database = this.databasePath(identity);
|
|
360
|
+
const userRoot = this.userRoot(identity);
|
|
361
|
+
const bindingDir = workspaceBindingDir(userRoot, identity.deviceId);
|
|
362
|
+
const release = await this.acquireGroundScope([
|
|
363
|
+
intent.destinationPath,
|
|
364
|
+
intent.stagingPath,
|
|
365
|
+
userRoot,
|
|
366
|
+
]);
|
|
367
|
+
const visibleAcrossBlindInterval = pathExists(intent.destinationPath);
|
|
368
|
+
try {
|
|
369
|
+
const selection = workspaceAddSelection(intent);
|
|
370
|
+
const stagedAcrossBlindInterval = pathExists(intent.stagingPath);
|
|
371
|
+
if (stagedAcrossBlindInterval && hasRootRows(database, intent.resourceId, intent.workspaceId)) {
|
|
372
|
+
// Rows prove that staging was verified before the crash, not that its
|
|
373
|
+
// bytes remained unchanged while no watcher existed. Return this
|
|
374
|
+
// private install to the intent-only stage and reproduce it from the
|
|
375
|
+
// verified immutable cache before making it visible.
|
|
376
|
+
deleteRootRows(database, intent.resourceId, intent.workspaceId);
|
|
389
377
|
}
|
|
390
|
-
await
|
|
391
|
-
|
|
392
|
-
|
|
378
|
+
await installCloudWorkspace({
|
|
379
|
+
identity,
|
|
380
|
+
userRoot,
|
|
381
|
+
database,
|
|
382
|
+
cacheDir: this.cacheDir(identity),
|
|
383
|
+
bindingDir,
|
|
384
|
+
resourceId: intent.resourceId,
|
|
385
|
+
workspace: selection.workspace,
|
|
386
|
+
reference: selection.reference,
|
|
387
|
+
references: intent.references,
|
|
388
|
+
records: intent.records,
|
|
389
|
+
destinationParent: dirname(intent.destinationPath),
|
|
390
|
+
cloudHead: intent.cloudHead,
|
|
391
|
+
readContent: (artifact) => this.downloadContent(intent.resourceId, this.cacheDir(identity), artifact, 10, { journey: "add", workspaceId: intent.workspaceId }),
|
|
392
|
+
onStage: (stage) => this.options.onWorkspaceAddStage?.({
|
|
393
|
+
workspaceId: intent.workspaceId,
|
|
394
|
+
stage,
|
|
395
|
+
}),
|
|
393
396
|
});
|
|
394
|
-
|
|
397
|
+
this.ensureWatchers(identity, visibleAcrossBlindInterval ? [] : [intent.workspaceId]);
|
|
398
|
+
assertHealthyWatch(this.watchHost.evidence(), intent.workspaceId);
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
release();
|
|
395
402
|
}
|
|
403
|
+
if (visibleAcrossBlindInterval) {
|
|
404
|
+
// Ground that was visible while the runtime was absent may have been
|
|
405
|
+
// edited after its verified reveal. It is an ordinary startup blind
|
|
406
|
+
// interval, so Watch must retain complete suspicion and Detect must
|
|
407
|
+
// settle it before the Add responsibility can be cleared.
|
|
408
|
+
await this.flushObservedChanges();
|
|
409
|
+
}
|
|
410
|
+
await this.options.onWorkspaceAddStage?.({
|
|
411
|
+
workspaceId: intent.workspaceId,
|
|
412
|
+
stage: "watching",
|
|
413
|
+
});
|
|
414
|
+
deleteWorkspaceAddIntent(database, intent.workspaceId);
|
|
396
415
|
}
|
|
397
416
|
/** Finish the user-visible register journey inside the one persistent Files
|
|
398
417
|
* owner. The declaration remains durable if any later stage fails, and a
|
|
@@ -746,7 +765,18 @@ export class UserGroundHost {
|
|
|
746
765
|
},
|
|
747
766
|
sha256Hex,
|
|
748
767
|
now: Date.now,
|
|
749
|
-
|
|
768
|
+
// A failed Apply lane is one entity's outcome; the rail keeps every
|
|
769
|
+
// other lane moving and retries this one. Surface it as evidence so a
|
|
770
|
+
// persistently failing entity is visible rather than silent.
|
|
771
|
+
onBackgroundError: (error) => {
|
|
772
|
+
this.options.onApplyError?.(error);
|
|
773
|
+
this.stage({
|
|
774
|
+
primitive: "apply",
|
|
775
|
+
stage: "lane",
|
|
776
|
+
status: "failed",
|
|
777
|
+
measurements: { error: error instanceof Error ? error.message : String(error) },
|
|
778
|
+
});
|
|
779
|
+
},
|
|
750
780
|
// The host now coordinates by address cone, so Apply's final guard waits
|
|
751
781
|
// only for effects on its own addresses. The rail itself still drains
|
|
752
782
|
// one entity at a time: parallel private preparation multiplies content
|
|
@@ -2949,14 +2979,21 @@ async function reconcileGround(input) {
|
|
|
2949
2979
|
}
|
|
2950
2980
|
async function reconcileRoot(input) {
|
|
2951
2981
|
const { identity, resourceId, rootUUID, rootName, rootPath, rootType, database, cacheDir, policy, bindingDir, suspects = null, evidence, } = input;
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2982
|
+
// Prior rows reach this scan from every binding this root encloses. A row's
|
|
2983
|
+
// stored relativePath is relative to the root that last committed it, which
|
|
2984
|
+
// is a different frame when an enclosing boundary was just registered
|
|
2985
|
+
// (child/README.md is stored as "README.md" under the child root). Address
|
|
2986
|
+
// every prior row in this scan's frame before anything compares it with an
|
|
2987
|
+
// observed entry; absolutePath is the only coordinate both frames share.
|
|
2988
|
+
const existingRows = (input.existingRows
|
|
2989
|
+
?? readRows(database).filter((row) => row.resourceId === resourceId && row.rootUUID === rootUUID))
|
|
2990
|
+
.flatMap((row) => {
|
|
2955
2991
|
if (!pathWithin(rootPath, row.absolutePath))
|
|
2956
2992
|
return [];
|
|
2957
|
-
const
|
|
2958
|
-
return [
|
|
2959
|
-
})
|
|
2993
|
+
const relativePath = slashPath(relative(rootPath, row.absolutePath));
|
|
2994
|
+
return [relativePath === row.relativePath ? row : { ...row, relativePath }];
|
|
2995
|
+
});
|
|
2996
|
+
const existingByPath = new Map(existingRows.map((row) => [row.relativePath, row]));
|
|
2960
2997
|
const registeredBoundaries = input.registeredBoundaries ?? new Map();
|
|
2961
2998
|
if (suspects !== null
|
|
2962
2999
|
&& suspects.length === 0
|
|
@@ -4126,6 +4163,19 @@ async function installCloudWorkspace(input) {
|
|
|
4126
4163
|
alreadyMaterialized: pending === null,
|
|
4127
4164
|
};
|
|
4128
4165
|
}
|
|
4166
|
+
// One machine materializes each UUID in exactly one place. The selected
|
|
4167
|
+
// closure may enclose a boundary this machine already added elsewhere; a
|
|
4168
|
+
// fresh install would re-root those rows under the new destination while
|
|
4169
|
+
// the old binding stayed bound and watched over ground with no rows, and
|
|
4170
|
+
// the next Detect pass there would mint a second identity for every file.
|
|
4171
|
+
const selectedUUIDs = new Set(records.map((record) => record.uuid));
|
|
4172
|
+
const enclosedElsewhere = allLocalRows.filter((row) => row.uuid !== workspace.uuid && selectedUUIDs.has(row.uuid));
|
|
4173
|
+
if (enclosedElsewhere.length > 0) {
|
|
4174
|
+
const roots = [...new Set(enclosedElsewhere.map((row) => row.rootUUID))]
|
|
4175
|
+
.map((rootUUID) => allLocalRows.find((row) => row.uuid === rootUUID)?.absolutePath ?? rootUUID);
|
|
4176
|
+
throw new Error(`files add: workspace ${workspace.uuid} encloses ${enclosedElsewhere.length} entities already `
|
|
4177
|
+
+ `materialized on this machine under ${roots.join(", ")}; remove that materialization first`);
|
|
4178
|
+
}
|
|
4129
4179
|
const parent = realpathSync(resolve(destinationParent));
|
|
4130
4180
|
if (!statSync(parent).isDirectory())
|
|
4131
4181
|
throw new Error("destination must be a directory");
|