agl 21.0.0 → 21.0.2
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/changelog.md +18 -0
- package/dist_serve/bundle.js +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.gitreversion.js +58 -9
- package/dist_ts/interfaces.gitreversion.d.ts +1 -1
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.gitreversion.ts +76 -11
- package/ts/interfaces.gitreversion.ts +1 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -4,7 +4,7 @@ export type TGitReversionDirection = 'undo' | 'redo';
|
|
|
4
4
|
export type TGitReversionSideName = 'before' | 'after';
|
|
5
5
|
export type TGitReversionWalPhase = 'intent' | 'preflighted' | 'applying' | 'targetVerified' | 'decision' | 'committed' | 'compensating' | 'compensated' | 'fenced';
|
|
6
6
|
export type TGitReversionWorktreeState = 'intent' | 'created' | 'registered' | 'active' | 'removing' | 'retainedDirty' | 'removed' | 'fenced';
|
|
7
|
-
export type TGitReversionFaultPoint = 'intent.before-fsync' | 'intent.after-fsync' | 'intent.before-ref-cas' | 'intent.after-ref-cas' | 'intent.before-commit-fsync' | 'intent.after-commit-fsync' | 'intent.before-remove' | 'intent.after-remove' | 'atomic.after-rename-before-directory-fsync' | 'capture.after-before-scan' | 'capture.before-finalize-record' | 'capture.after-finalize-record-fsync' | 'capture.after-staging-repository' | 'capture.after-intent-repository' | 'capture.after-state-repository' | 'capture.after-manifest-repository' | 'capture.before-store-create' | 'capture.after-store-create' | 'capture.before-generation-enrollment' | 'capture.after-generation-enrollment' | 'capture.after-publication-fsync' | 'capture.before-root-delete' | 'capture.after-root-delete' | 'capture.after-root-delete-fsync' | 'apply.after-intent' | 'apply.after-preflight' | 'apply.after-source-pack-import' | 'apply.after-source-pack-fsync' | 'apply.after-repository' | 'apply.after-target-verification' | 'apply.after-decision-repository' | 'apply.after-decision' | 'apply.after-state-repository' | 'apply.during-compensation' | 'apply.before-source-keep-remove' | 'apply.after-source-keep-quarantine' | 'apply.after-source-keep-remove' | 'apply.after-source-keep-remove-fsync' | 'worktree.after-intent' | 'worktree.after-create' | 'worktree.after-removed-record' | 'index.before-sidecar-fsync' | 'index.after-sidecar-fsync' | 'index.after-link' | 'index.after-link-fsync' | 'index.after-rename' | 'index.after-rename-fsync' | 'index.after-cleanup' | 'index.after-cleanup-fsync';
|
|
7
|
+
export type TGitReversionFaultPoint = 'intent.before-fsync' | 'intent.after-fsync' | 'intent.before-ref-cas' | 'intent.after-ref-cas' | 'intent.before-commit-fsync' | 'intent.after-commit-fsync' | 'intent.before-remove' | 'intent.after-remove' | 'atomic.after-rename-before-directory-fsync' | 'capture.after-before-scan' | 'capture.before-finalize-record' | 'capture.after-finalize-record-fsync' | 'capture.after-staging-repository' | 'capture.after-intent-repository' | 'capture.after-state-repository' | 'capture.after-manifest-repository' | 'capture.before-store-create' | 'capture.after-store-create' | 'capture.before-generation-enrollment' | 'capture.after-generation-enrollment' | 'capture.after-publication-fsync' | 'capture.before-root-delete' | 'capture.after-root-delete' | 'capture.after-root-delete-fsync' | 'apply.after-intent' | 'apply.after-preflight' | 'apply.after-source-pack-import' | 'apply.after-source-pack-fsync' | 'apply.after-repository' | 'apply.after-target-verification' | 'apply.after-decision-repository' | 'apply.after-decision' | 'apply.after-state-repository' | 'apply.during-compensation' | 'apply.before-source-keep-remove' | 'apply.after-source-keep-quarantine' | 'apply.after-source-keep-remove' | 'apply.after-source-keep-remove-fsync' | 'worktree.after-intent' | 'worktree.after-create' | 'worktree.after-removed-record' | 'worktree.record-read.after-open' | 'index.before-sidecar-fsync' | 'index.after-sidecar-fsync' | 'index.after-link' | 'index.after-link-fsync' | 'index.after-rename' | 'index.after-rename-fsync' | 'index.after-cleanup' | 'index.after-cleanup-fsync';
|
|
8
8
|
export type TGitReversionReference = {
|
|
9
9
|
schemaVersion: 1;
|
|
10
10
|
captureKey: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agl",
|
|
3
|
-
"version": "21.0.
|
|
3
|
+
"version": "21.0.2",
|
|
4
4
|
"controllerUpgradeManagementVersion": 2,
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Agent Gateway Layer for OpenCode and FlexHarness sessions",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@api.global/typedrequest": "8.0.2",
|
|
20
20
|
"@api.global/typedrequest-interfaces": "7.1.0",
|
|
21
|
-
"@api.global/typedserver": "11.
|
|
21
|
+
"@api.global/typedserver": "11.1.0",
|
|
22
22
|
"@api.global/typedsocket": "8.0.0",
|
|
23
23
|
"@design.estate/dees-catalog": "6.10.1",
|
|
24
24
|
"@design.estate/dees-element": "2.4.0",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@push.rocks/smartdb": "5.2.1",
|
|
35
35
|
"@push.rocks/smartfs": "1.7.0",
|
|
36
36
|
"@push.rocks/smartsecret": "1.6.0",
|
|
37
|
-
"@push.rocks/smartserve": "6.0.
|
|
37
|
+
"@push.rocks/smartserve": "6.0.1",
|
|
38
38
|
"@push.rocks/smartshell": "4.1.0",
|
|
39
39
|
"@simplewebauthn/browser": "13.3.0",
|
|
40
40
|
"@simplewebauthn/server": "13.3.2",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -665,6 +665,13 @@ class GitReversionInjectedFaultError extends Error {
|
|
|
665
665
|
}
|
|
666
666
|
}
|
|
667
667
|
|
|
668
|
+
class GitReversionConcurrentRecordReadError extends Error {
|
|
669
|
+
constructor(messageArg: string) {
|
|
670
|
+
super(messageArg);
|
|
671
|
+
this.name = 'GitReversionConcurrentRecordReadError';
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
668
675
|
class GitReversionSourceKeepOwnershipError extends GitReversionFencedError {
|
|
669
676
|
constructor(messageArg: string, optionsArg?: ErrorOptions) {
|
|
670
677
|
super(messageArg, optionsArg);
|
|
@@ -3182,6 +3189,8 @@ export class ControllerGitReversion {
|
|
|
3182
3189
|
descriptionArg: string,
|
|
3183
3190
|
signalArg?: AbortSignal,
|
|
3184
3191
|
requirePrivateModeArg = false,
|
|
3192
|
+
detectConcurrentReplacementArg = false,
|
|
3193
|
+
afterOpenArg?: () => Promise<void>,
|
|
3185
3194
|
): Promise<Buffer> {
|
|
3186
3195
|
let handle: plugins.fs.promises.FileHandle;
|
|
3187
3196
|
try {
|
|
@@ -3196,7 +3205,13 @@ export class ControllerGitReversion {
|
|
|
3196
3205
|
throw new Error(`${descriptionArg} could not be opened safely.`, { cause: errorArg });
|
|
3197
3206
|
}
|
|
3198
3207
|
try {
|
|
3208
|
+
await afterOpenArg?.();
|
|
3199
3209
|
const before = await handle.stat();
|
|
3210
|
+
if (detectConcurrentReplacementArg && before.nlink === 0) {
|
|
3211
|
+
throw new GitReversionConcurrentRecordReadError(
|
|
3212
|
+
`${descriptionArg} was replaced while it was opened.`,
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3200
3215
|
if (
|
|
3201
3216
|
!before.isFile()
|
|
3202
3217
|
|| (requirePrivateModeArg && before.nlink !== 1)
|
|
@@ -3222,12 +3237,46 @@ export class ControllerGitReversion {
|
|
|
3222
3237
|
if (total > maximumBytesArg) throw new Error(`${descriptionArg} exceeds its byte limit.`);
|
|
3223
3238
|
}
|
|
3224
3239
|
const after = await handle.stat();
|
|
3240
|
+
if (detectConcurrentReplacementArg && after.nlink === 0) {
|
|
3241
|
+
throw new GitReversionConcurrentRecordReadError(
|
|
3242
|
+
`${descriptionArg} was replaced while it was read.`,
|
|
3243
|
+
);
|
|
3244
|
+
}
|
|
3225
3245
|
if (
|
|
3226
3246
|
before.dev !== after.dev
|
|
3227
3247
|
|| before.ino !== after.ino
|
|
3228
3248
|
|| before.size !== after.size
|
|
3229
3249
|
|| total !== before.size
|
|
3250
|
+
|| !after.isFile()
|
|
3251
|
+
|| (requirePrivateModeArg && after.nlink !== 1)
|
|
3252
|
+
|| (requirePrivateModeArg && (after.mode & 0o077) !== 0)
|
|
3253
|
+
|| (typeof process.getuid === 'function' && after.uid !== process.getuid())
|
|
3230
3254
|
) throw new Error(`${descriptionArg} changed while it was read.`);
|
|
3255
|
+
if (detectConcurrentReplacementArg) {
|
|
3256
|
+
let pathAfter: plugins.fs.Stats;
|
|
3257
|
+
try {
|
|
3258
|
+
pathAfter = await plugins.fs.promises.lstat(pathArg);
|
|
3259
|
+
} catch (errorArg) {
|
|
3260
|
+
if ((errorArg as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
3261
|
+
throw new GitReversionConcurrentRecordReadError(
|
|
3262
|
+
`${descriptionArg} was removed while it was read.`,
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
throw errorArg;
|
|
3266
|
+
}
|
|
3267
|
+
if (
|
|
3268
|
+
!pathAfter.isFile()
|
|
3269
|
+
|| pathAfter.isSymbolicLink()
|
|
3270
|
+
|| pathAfter.nlink !== 1
|
|
3271
|
+
|| (pathAfter.mode & 0o077) !== 0
|
|
3272
|
+
|| (typeof process.getuid === 'function' && pathAfter.uid !== process.getuid())
|
|
3273
|
+
) throw new Error(`${descriptionArg} pathname is not an exact owned regular file.`);
|
|
3274
|
+
if (pathAfter.dev !== after.dev || pathAfter.ino !== after.ino) {
|
|
3275
|
+
throw new GitReversionConcurrentRecordReadError(
|
|
3276
|
+
`${descriptionArg} was replaced while its pathname was revalidated.`,
|
|
3277
|
+
);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3231
3280
|
return Buffer.concat(chunks, total);
|
|
3232
3281
|
} finally {
|
|
3233
3282
|
await handle.close();
|
|
@@ -12121,18 +12170,34 @@ export class ControllerGitReversion {
|
|
|
12121
12170
|
|
|
12122
12171
|
private async readWorktreeRecordByRef<T>(refArg: string): Promise<{ oid: string; value: T } | undefined> {
|
|
12123
12172
|
const path = this.worktreeRecordPathForRef(refArg);
|
|
12124
|
-
let bytes: Buffer;
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
this.
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12173
|
+
let bytes: Buffer | undefined;
|
|
12174
|
+
let concurrentReadError: GitReversionConcurrentRecordReadError | undefined;
|
|
12175
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
12176
|
+
try {
|
|
12177
|
+
bytes = await this.readBoundedRegularFile(
|
|
12178
|
+
path,
|
|
12179
|
+
this.limits.maximumMetadataRecordBytes,
|
|
12180
|
+
'Private worktree record',
|
|
12181
|
+
undefined,
|
|
12182
|
+
true,
|
|
12183
|
+
true,
|
|
12184
|
+
() => this.fault({ point: 'worktree.record-read.after-open', ref: refArg }),
|
|
12185
|
+
);
|
|
12186
|
+
break;
|
|
12187
|
+
} catch (errorArg) {
|
|
12188
|
+
if ((errorArg as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
|
|
12189
|
+
if (errorArg instanceof GitReversionConcurrentRecordReadError) {
|
|
12190
|
+
concurrentReadError = errorArg;
|
|
12191
|
+
continue;
|
|
12192
|
+
}
|
|
12193
|
+
throw errorArg;
|
|
12194
|
+
}
|
|
12195
|
+
}
|
|
12196
|
+
if (!bytes) {
|
|
12197
|
+
throw new GitReversionFencedError(
|
|
12198
|
+
'The private worktree record changed repeatedly while it was read.',
|
|
12199
|
+
{ cause: concurrentReadError },
|
|
12132
12200
|
);
|
|
12133
|
-
} catch (errorArg) {
|
|
12134
|
-
if ((errorArg as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
|
|
12135
|
-
throw errorArg;
|
|
12136
12201
|
}
|
|
12137
12202
|
let value: unknown;
|
|
12138
12203
|
try {
|
|
@@ -64,6 +64,7 @@ export type TGitReversionFaultPoint =
|
|
|
64
64
|
| 'worktree.after-intent'
|
|
65
65
|
| 'worktree.after-create'
|
|
66
66
|
| 'worktree.after-removed-record'
|
|
67
|
+
| 'worktree.record-read.after-open'
|
|
67
68
|
| 'index.before-sidecar-fsync'
|
|
68
69
|
| 'index.after-sidecar-fsync'
|
|
69
70
|
| 'index.after-link'
|