dsh-git-worktree 0.7.5 → 0.8.0
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 +15 -0
- package/README.en.md +7 -3
- package/README.md +7 -3
- package/docs/i18n.md +63 -0
- package/lib/adapters/files.d.ts +0 -7
- package/lib/adapters/files.js +4 -3
- package/lib/adapters/files.js.map +1 -1
- package/lib/adapters/git.d.ts +0 -10
- package/lib/adapters/git.js +6 -5
- package/lib/adapters/git.js.map +1 -1
- package/lib/adapters/registry.d.ts +0 -7
- package/lib/adapters/registry.js +2 -1
- package/lib/adapters/registry.js.map +1 -1
- package/lib/adapters/safe-file.d.ts +0 -7
- package/lib/adapters/safe-file.js +5 -4
- package/lib/adapters/safe-file.js.map +1 -1
- package/lib/client/WorktreeCreateRow.js +4 -2
- package/lib/client/WorktreeCreateRow.js.map +1 -1
- package/lib/client/WorktreeReviewRow.js +10 -8
- package/lib/client/WorktreeReviewRow.js.map +1 -1
- package/lib/client/actions.d.ts +3 -2
- package/lib/client/actions.js +17 -13
- package/lib/client/actions.js.map +1 -1
- package/lib/client/console-remote/adapter.d.ts +2 -1
- package/lib/client/console-remote/adapter.js +35 -32
- package/lib/client/console-remote/adapter.js.map +1 -1
- package/lib/client/console-remote/index.js +6 -2
- package/lib/client/console-remote/index.js.map +1 -1
- package/lib/client/i18n.d.ts +25 -0
- package/lib/client/i18n.js +43 -0
- package/lib/client/i18n.js.map +1 -0
- package/lib/client/index.d.ts +1 -1
- package/lib/client/index.js +17 -4
- package/lib/client/index.js.map +1 -1
- package/lib/client/model.d.ts +3 -2
- package/lib/client/model.js +8 -8
- package/lib/client/model.js.map +1 -1
- package/lib/client/pre-session/PreSessionWorktreeToggle.js +14 -12
- package/lib/client/pre-session/PreSessionWorktreeToggle.js.map +1 -1
- package/lib/client/pre-session/controller.js +11 -9
- package/lib/client/pre-session/controller.js.map +1 -1
- package/lib/client/pre-session/index.d.ts +12 -1
- package/lib/client/pre-session/index.js +9 -1
- package/lib/client/pre-session/index.js.map +1 -1
- package/lib/client/review-console/DeliveryProof.js +15 -12
- package/lib/client/review-console/DeliveryProof.js.map +1 -1
- package/lib/client/review-console/PreflightStatus.js +14 -8
- package/lib/client/review-console/PreflightStatus.js.map +1 -1
- package/lib/client/review-console/ReviewActions.js +97 -92
- package/lib/client/review-console/ReviewActions.js.map +1 -1
- package/lib/client/review-console/WorktreeReviewPanel.d.ts +1 -1
- package/lib/client/review-console/WorktreeReviewPanel.js +19 -16
- package/lib/client/review-console/WorktreeReviewPanel.js.map +1 -1
- package/lib/client/review-console/recovery-continuation.d.ts +2 -1
- package/lib/client/review-console/recovery-continuation.js +12 -8
- package/lib/client/review-console/recovery-continuation.js.map +1 -1
- package/lib/client/target-console/TargetStatusAction.js +28 -25
- package/lib/client/target-console/TargetStatusAction.js.map +1 -1
- package/lib/client/target-console/WorktreeConsoleView.js +40 -36
- package/lib/client/target-console/WorktreeConsoleView.js.map +1 -1
- package/lib/client/target-console/WorktreeManagerModal.js +3 -1
- package/lib/client/target-console/WorktreeManagerModal.js.map +1 -1
- package/lib/client/target-console/WorktreeReviewStatus.js +14 -12
- package/lib/client/target-console/WorktreeReviewStatus.js.map +1 -1
- package/lib/client/workspace-sidebar/index.js +4 -2
- package/lib/client/workspace-sidebar/index.js.map +1 -1
- package/lib/client/workspace-sidebar/model.d.ts +2 -1
- package/lib/client/workspace-sidebar/model.js +14 -13
- package/lib/client/workspace-sidebar/model.js.map +1 -1
- package/lib/client.js +2173 -477
- package/lib/commands.js +32 -35
- package/lib/commands.js.map +1 -1
- package/lib/console-host/control-plane.js +82 -81
- package/lib/console-host/control-plane.js.map +1 -1
- package/lib/console-host/errors.js +2 -1
- package/lib/console-host/errors.js.map +1 -1
- package/lib/console-host/projection.js +3 -1
- package/lib/console-host/projection.js.map +1 -1
- package/lib/console-host/review-diff.js +6 -5
- package/lib/console-host/review-diff.js.map +1 -1
- package/lib/console-host/service.d.ts +24 -22
- package/lib/console-host/service.js +54 -51
- package/lib/console-host/service.js.map +1 -1
- package/lib/console-remote/descriptors.js +2 -2
- package/lib/console-remote/descriptors.js.map +1 -1
- package/lib/console-remote/remote.d.ts +22 -21
- package/lib/console-remote/remote.js.map +1 -1
- package/lib/console-remote/schemas.d.ts +4 -0
- package/lib/console-remote/schemas.js +1 -0
- package/lib/console-remote/schemas.js.map +1 -1
- package/lib/i18n/client-messages.d.ts +1523 -0
- package/lib/i18n/client-messages.js +1431 -0
- package/lib/i18n/client-messages.js.map +1 -0
- package/lib/i18n/core.d.ts +12 -0
- package/lib/i18n/core.js +10 -0
- package/lib/i18n/core.js.map +1 -0
- package/lib/i18n/host-messages.d.ts +1463 -0
- package/lib/i18n/host-messages.js +1474 -0
- package/lib/i18n/host-messages.js.map +1 -0
- package/lib/i18n/host.d.ts +9 -0
- package/lib/i18n/host.js +34 -0
- package/lib/i18n/host.js.map +1 -0
- package/lib/i18n/transport-messages.d.ts +10 -0
- package/lib/i18n/transport-messages.js +11 -0
- package/lib/i18n/transport-messages.js.map +1 -0
- package/lib/session-checkout-apply.js +93 -92
- package/lib/session-checkout-apply.js.map +1 -1
- package/lib/session-checkout-module.js +232 -229
- package/lib/session-checkout-module.js.map +1 -1
- package/lib/session-target-context.js +2 -1
- package/lib/session-target-context.js.map +1 -1
- package/lib/tools.d.ts +0 -8
- package/lib/tools.js +108 -96
- package/lib/tools.js.map +1 -1
- package/package.json +2 -1
- package/scripts/dev-dsh-lib.mjs +1 -1
- package/scripts/workspace-sidebar-upstream.mjs +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hostMessage } from '../i18n/host.js';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { resolve } from 'node:path';
|
|
3
4
|
import { consoleFailure, domainError, failure, outcome } from './errors.js';
|
|
@@ -6,7 +7,7 @@ import { ReviewDiffStaleError } from './review-diff.js';
|
|
|
6
7
|
function recordOf(registry, checkoutId) {
|
|
7
8
|
const record = registry.read().managedCheckouts[checkoutId];
|
|
8
9
|
if (record === undefined)
|
|
9
|
-
throw domainError('checkout_missing', '
|
|
10
|
+
throw domainError('checkout_missing', hostMessage('theWorktreeRecordDoesNotExist'));
|
|
10
11
|
return record;
|
|
11
12
|
}
|
|
12
13
|
function sidebarTaskState(record) {
|
|
@@ -30,14 +31,14 @@ function sidebarTaskState(record) {
|
|
|
30
31
|
}
|
|
31
32
|
function readyReview(record) {
|
|
32
33
|
if (record.phase !== 'ready' || record.delivery.state !== 'ready_for_review') {
|
|
33
|
-
throw domainError('operation_not_allowed', '
|
|
34
|
+
throw domainError('operation_not_allowed', hostMessage('theCurrentWorktreeIsNotReadyForReview'));
|
|
34
35
|
}
|
|
35
36
|
return record.delivery.review;
|
|
36
37
|
}
|
|
37
38
|
function previewReview(record) {
|
|
38
39
|
if (record.phase !== 'ready'
|
|
39
40
|
|| (record.delivery.state !== 'preview_active' && record.delivery.state !== 'preview_detached')) {
|
|
40
|
-
throw domainError('preview_not_active', '
|
|
41
|
+
throw domainError('preview_not_active', hostMessage('thereIsNoLocalPreviewAwaitingAcceptance'));
|
|
41
42
|
}
|
|
42
43
|
return record.delivery.review;
|
|
43
44
|
}
|
|
@@ -55,11 +56,11 @@ function applyConflictContinuation(result, reviewId) {
|
|
|
55
56
|
const checkoutId = result.target.checkout.id;
|
|
56
57
|
const revision = result.target.revision;
|
|
57
58
|
if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(result.localHeadOid)) {
|
|
58
|
-
throw domainError('git_error', '
|
|
59
|
+
throw domainError('git_error', hostMessage('conflictRecoveryIsMissingAValidLocalHEADIdentity'));
|
|
59
60
|
}
|
|
60
61
|
const localHeadOid = result.localHeadOid;
|
|
61
62
|
if (result.conflictingFiles.length > 500 || !result.conflictingFiles.every(safeConflictFile)) {
|
|
62
|
-
throw domainError('git_error', '
|
|
63
|
+
throw domainError('git_error', hostMessage('conflictRecoveryContainsUnsafeOrOutOfBoundsFile'));
|
|
63
64
|
}
|
|
64
65
|
const conflictingFiles = [...result.conflictingFiles];
|
|
65
66
|
return {
|
|
@@ -74,7 +75,7 @@ function applyConflictContinuation(result, reviewId) {
|
|
|
74
75
|
}
|
|
75
76
|
function preflightFailure(view) {
|
|
76
77
|
if (view === undefined)
|
|
77
|
-
return failure('git_error', '
|
|
78
|
+
return failure('git_error', hostMessage('theCurrentSessionCheckoutModuleDoesNotSupportAcceptancePreflight'));
|
|
78
79
|
if (view.status !== 'blocked')
|
|
79
80
|
return undefined;
|
|
80
81
|
const code = view.reason === 'stale_isolated' ? 'stale_isolated'
|
|
@@ -92,17 +93,17 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
92
93
|
async function unboundLocalTarget(sessionId) {
|
|
93
94
|
const session = options.lookup.getSession(sessionId);
|
|
94
95
|
if (session === undefined)
|
|
95
|
-
throw domainError('session_not_found', '
|
|
96
|
+
throw domainError('session_not_found', hostMessage('theCurrentSessionDoesNotExist'));
|
|
96
97
|
if (session.projectId === undefined)
|
|
97
|
-
throw domainError('project_not_found', '
|
|
98
|
+
throw domainError('project_not_found', hostMessage('theCurrentSessionIsNotAssociatedWithAProject'));
|
|
98
99
|
const project = options.lookup.getProject(session.projectId);
|
|
99
100
|
if (project === undefined)
|
|
100
|
-
throw domainError('project_not_found', '
|
|
101
|
+
throw domainError('project_not_found', hostMessage('theCurrentSessionSProjectDoesNotExist'));
|
|
101
102
|
if (!options.files.exists(project.root))
|
|
102
|
-
throw domainError('project_root_missing', '
|
|
103
|
+
throw domainError('project_root_missing', hostMessage('theCurrentSessionSProjectDirectoryDoesNotExist'));
|
|
103
104
|
const snapshot = await options.git.inspect(project.root);
|
|
104
105
|
if (snapshot === null)
|
|
105
|
-
throw domainError('not_git_repository', '
|
|
106
|
+
throw domainError('not_git_repository', hostMessage('theCurrentSessionSProjectIsNotAnAvailable'));
|
|
106
107
|
const status = await options.git.status(project.root);
|
|
107
108
|
return {
|
|
108
109
|
project: { id: project.id, name: project.name },
|
|
@@ -129,7 +130,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
129
130
|
const session = options.lookup.getSession(sessionId);
|
|
130
131
|
const project = session?.projectId === undefined ? undefined : options.lookup.getProject(session.projectId);
|
|
131
132
|
if (project === undefined) {
|
|
132
|
-
throw domainError('project_mismatch', '
|
|
133
|
+
throw domainError('project_mismatch', hostMessage('theCurrentSessionWorkspaceCannotBeProvenToBelong'));
|
|
133
134
|
}
|
|
134
135
|
const cleanedOwnerCwd = record.ownerSessionId === sessionId
|
|
135
136
|
&& record.phase === 'discarded'
|
|
@@ -139,28 +140,28 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
139
140
|
if (cleanedOwnerCwd)
|
|
140
141
|
return;
|
|
141
142
|
if (!options.files.exists(project.root)) {
|
|
142
|
-
throw domainError('project_mismatch', '
|
|
143
|
+
throw domainError('project_mismatch', hostMessage('theCurrentSessionWorkspaceCannotBeProvenToBelong'));
|
|
143
144
|
}
|
|
144
145
|
const workspaceRoot = await options.files.canonicalize(project.root);
|
|
145
146
|
if (!sameLocalRoot(workspaceRoot, expectedRoot)) {
|
|
146
|
-
throw domainError('project_mismatch', '
|
|
147
|
+
throw domainError('project_mismatch', hostMessage('theCurrentSessionCwdDoesNotMatchTheWorktree'));
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
async function authorize(sessionId, checkoutId) {
|
|
150
151
|
const caller = await options.module.inspect(sessionId);
|
|
151
152
|
const record = recordOf(options.registry, checkoutId);
|
|
152
153
|
if (record.ownerSessionId !== sessionId && record.sourceSessionId !== sessionId) {
|
|
153
|
-
throw domainError('not_owner', '
|
|
154
|
+
throw domainError('not_owner', hostMessage('theCurrentSessionIsNotAllowedToAccessThis'));
|
|
154
155
|
}
|
|
155
156
|
if (record.projectId !== caller.project.id) {
|
|
156
|
-
throw domainError('project_mismatch', '
|
|
157
|
+
throw domainError('project_mismatch', hostMessage('worktreeDoesNotMatchTheCurrentSessionSProject'));
|
|
157
158
|
}
|
|
158
159
|
const expectedRoot = record.ownerSessionId === sessionId ? record.managedRoot : record.localRoot;
|
|
159
160
|
await verifyCallerRoot(sessionId, record, expectedRoot);
|
|
160
161
|
if (record.phase !== 'discarded') {
|
|
161
162
|
const visible = await options.module.listManagedWorktreesForSession(sessionId, { checkoutId });
|
|
162
163
|
if (!visible.some(item => item.checkoutId === checkoutId)) {
|
|
163
|
-
throw domainError('not_owner', '
|
|
164
|
+
throw domainError('not_owner', hostMessage('theCurrentSessionIsNotAllowedToAccessThis'));
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
return record;
|
|
@@ -172,7 +173,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
172
173
|
}
|
|
173
174
|
const caller = await callerTarget(sessionId);
|
|
174
175
|
if (caller.checkout.kind !== 'isolated') {
|
|
175
|
-
throw domainError('not_owner', '
|
|
176
|
+
throw domainError('not_owner', hostMessage('theCurrentSessionIsNotAllowedToAccessThis'));
|
|
176
177
|
}
|
|
177
178
|
const anchor = await authorize(sessionId, caller.checkout.id);
|
|
178
179
|
const sourceSessionId = anchor.sourceSessionId ?? anchor.ownerSessionId;
|
|
@@ -189,10 +190,10 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
189
190
|
&& requested.projectId === anchor.projectId
|
|
190
191
|
&& sameLocalRoot(requested.localRoot, anchor.localRoot);
|
|
191
192
|
if (!sameLinkedGroup && !exactAcceptanceHolder) {
|
|
192
|
-
throw domainError('not_owner', '
|
|
193
|
+
throw domainError('not_owner', hostMessage('theCurrentSessionIsNotAllowedToAccessThis'));
|
|
193
194
|
}
|
|
194
195
|
if (!ownerSessionAvailable(requested)) {
|
|
195
|
-
throw domainError('checkout_missing', '
|
|
196
|
+
throw domainError('checkout_missing', hostMessage('theAssociatedWorktreeSOwnerSessionIsUnavailable'));
|
|
196
197
|
}
|
|
197
198
|
await verifyCallerRoot(requested.ownerSessionId, requested, requested.managedRoot);
|
|
198
199
|
if (exactAcceptanceHolder) {
|
|
@@ -206,7 +207,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
206
207
|
if (currentHolder?.checkoutId !== currentRequested.checkoutId
|
|
207
208
|
|| currentRequested.ownerSessionId !== requested.ownerSessionId
|
|
208
209
|
|| !sameLocalRoot(currentRequested.localRoot, currentAnchor.localRoot))
|
|
209
|
-
throw domainError('not_owner', '
|
|
210
|
+
throw domainError('not_owner', hostMessage('theAcceptanceSlotHolderHasChangedCheckAgain'));
|
|
210
211
|
return {
|
|
211
212
|
record: currentRequested,
|
|
212
213
|
linkedRead: true,
|
|
@@ -221,7 +222,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
221
222
|
const managedRoot = await options.module.resolveManagedRoot(record.checkoutId);
|
|
222
223
|
const snapshot = await options.git.inspect(managedRoot);
|
|
223
224
|
if (snapshot === null)
|
|
224
|
-
throw domainError('checkout_mismatch', '
|
|
225
|
+
throw domainError('checkout_mismatch', hostMessage('theWorktreeGitIdentityCannotBeVerified'));
|
|
225
226
|
const status = await options.git.status(managedRoot);
|
|
226
227
|
return { managedRoot, snapshot, dirty: status.dirty };
|
|
227
228
|
}
|
|
@@ -286,7 +287,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
286
287
|
|| !sameLocalRoot(currentRequested.localRoot, currentAnchor.localRoot)
|
|
287
288
|
|| observed.managedRoot === null
|
|
288
289
|
|| !sameLocalRoot(observed.managedRoot, currentRequested.managedRoot))
|
|
289
|
-
throw domainError('not_owner', '
|
|
290
|
+
throw domainError('not_owner', hostMessage('theAcceptanceSlotHolderHasChangedCheckAgain'));
|
|
290
291
|
projectedRecord = currentRequested;
|
|
291
292
|
}
|
|
292
293
|
return projectReviewSlot(projectedRecord, projectDetails(projectedRecord, sessionId, observed.managedRoot, observed.snapshot, observed.dirty, ownerSessionAvailable(access.record), access.linkedRead, observedDeliveryProof));
|
|
@@ -365,7 +366,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
365
366
|
const session = options.lookup.getSession(request.sessionId);
|
|
366
367
|
const project = session?.projectId === undefined ? undefined : options.lookup.getProject(session.projectId);
|
|
367
368
|
if (project === undefined || project.id !== caller.project.id || !options.files.exists(project.root)) {
|
|
368
|
-
throw domainError('project_mismatch', '
|
|
369
|
+
throw domainError('project_mismatch', hostMessage('theCurrentSessionWorkspaceCannotBeProvenToBe'));
|
|
369
370
|
}
|
|
370
371
|
localRoot = await options.files.canonicalize(project.root);
|
|
371
372
|
}
|
|
@@ -403,7 +404,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
403
404
|
if (record.sourceSessionId !== sourceSessionId
|
|
404
405
|
|| record.ownerSessionId !== targetSessionId
|
|
405
406
|
|| observed.managedRoot !== launch.managedRoot)
|
|
406
|
-
throw domainError('checkout_mismatch', '
|
|
407
|
+
throw domainError('checkout_mismatch', hostMessage('hostIdentityVerificationFailedForTheNewlyCreatedWorktree'));
|
|
407
408
|
return {
|
|
408
409
|
target: projectDetails(record, sourceSessionId, observed.managedRoot, observed.snapshot, observed.dirty, ownerSessionAvailable(record)),
|
|
409
410
|
targetSessionId,
|
|
@@ -415,17 +416,17 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
415
416
|
try {
|
|
416
417
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
417
418
|
if (record.ownerSessionId !== request.sessionId)
|
|
418
|
-
return failure('not_owner', '
|
|
419
|
+
return failure('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanReadTheReview'));
|
|
419
420
|
if (record.revision !== request.expectedRevision)
|
|
420
|
-
return failure('stale_target', '
|
|
421
|
+
return failure('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
421
422
|
const review = readyReview(record);
|
|
422
423
|
if (review.reviewId !== request.expectedReviewId)
|
|
423
|
-
return failure('stale_target', '
|
|
424
|
+
return failure('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
424
425
|
const before = await options.module.preflight?.(request.sessionId, request.expectedRevision);
|
|
425
426
|
if (before === undefined || before.status === 'blocked')
|
|
426
427
|
return preflightFailure(before);
|
|
427
428
|
if (before.reviewId !== review.reviewId || before.isolatedHeadOid !== review.isolatedHeadOid) {
|
|
428
|
-
return failure('stale_isolated', '
|
|
429
|
+
return failure('stale_isolated', hostMessage('isolatedHEADChangedAfterReady'));
|
|
429
430
|
}
|
|
430
431
|
const observed = await observe(record);
|
|
431
432
|
const diff = await options.reviewDiff.read({
|
|
@@ -437,7 +438,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
437
438
|
});
|
|
438
439
|
const after = await options.module.preflight?.(request.sessionId, request.expectedRevision);
|
|
439
440
|
if (after === undefined || after.status === 'blocked') {
|
|
440
|
-
return failure('stale_isolated', '
|
|
441
|
+
return failure('stale_isolated', hostMessage('isolatedContentChangedAfterReadyDiffBytesWereDiscarded'));
|
|
441
442
|
}
|
|
442
443
|
const current = recordOf(options.registry, request.checkoutId);
|
|
443
444
|
if (after.reviewId !== review.reviewId
|
|
@@ -446,7 +447,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
446
447
|
|| current.delivery.state !== 'ready_for_review'
|
|
447
448
|
|| current.delivery.review.reviewId !== review.reviewId
|
|
448
449
|
|| current.delivery.review.isolatedFingerprint !== review.isolatedFingerprint)
|
|
449
|
-
return failure('stale_isolated', '
|
|
450
|
+
return failure('stale_isolated', hostMessage('isolatedContentChangedAfterReadyDiffBytesWereDiscarded'));
|
|
450
451
|
return { ok: true, value: diff };
|
|
451
452
|
}
|
|
452
453
|
catch (error) {
|
|
@@ -458,31 +459,31 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
458
459
|
preflight: request => outcome(async () => {
|
|
459
460
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
460
461
|
if (record.ownerSessionId !== request.sessionId)
|
|
461
|
-
throw domainError('not_owner', '
|
|
462
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanRunSyncPreflight436'));
|
|
462
463
|
if (record.revision !== request.expectedRevision)
|
|
463
|
-
throw domainError('stale_target', '
|
|
464
|
+
throw domainError('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
464
465
|
const review = readyReview(record);
|
|
465
466
|
if (review.reviewId !== request.expectedReviewId)
|
|
466
|
-
throw domainError('stale_target', '
|
|
467
|
+
throw domainError('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
467
468
|
const preflight = await options.module.preflight?.(request.sessionId, request.expectedRevision);
|
|
468
469
|
if (preflight === undefined)
|
|
469
|
-
throw domainError('git_error', '
|
|
470
|
+
throw domainError('git_error', hostMessage('theCurrentSessionCheckoutModuleDoesNotSupportAcceptancePreflight'));
|
|
470
471
|
return { preflight };
|
|
471
472
|
}),
|
|
472
473
|
previewRecoveryPreflight: request => outcome(async () => {
|
|
473
474
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
474
475
|
if (record.ownerSessionId !== request.sessionId)
|
|
475
|
-
throw domainError('not_owner', '
|
|
476
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanCheckPreviewRecovery440'));
|
|
476
477
|
if (record.revision !== request.expectedRevision)
|
|
477
|
-
throw domainError('stale_target', '
|
|
478
|
+
throw domainError('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
478
479
|
if (record.phase !== 'ready'
|
|
479
480
|
|| record.delivery.state !== 'preview_detached'
|
|
480
481
|
|| record.delivery.review.reviewId !== request.expectedReviewId
|
|
481
482
|
|| record.delivery.preview.previewId !== request.expectedPreviewId)
|
|
482
|
-
throw domainError('stale_target', '
|
|
483
|
+
throw domainError('stale_target', hostMessage('theDetachedPreviewIdentityHasChangedRefresh'));
|
|
483
484
|
const preflight = await options.module.preflightPreviewRecovery?.(request.sessionId, request.expectedRevision, request.expectedReviewId, request.expectedPreviewId);
|
|
484
485
|
if (preflight === undefined)
|
|
485
|
-
throw domainError('git_error', '
|
|
486
|
+
throw domainError('git_error', hostMessage('theCurrentSessionCheckoutModuleDoesNotSupportPreviewRecoveryPreflight'));
|
|
486
487
|
return { preflight };
|
|
487
488
|
}),
|
|
488
489
|
preparePreviewRecoveryAnalysis: request => outcome(async () => {
|
|
@@ -493,7 +494,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
493
494
|
|| request.recoveryProof.revision !== request.expectedRevision
|
|
494
495
|
|| request.recoveryProof.reviewId !== request.expectedReviewId
|
|
495
496
|
|| request.recoveryProof.previewId !== request.expectedPreviewId)
|
|
496
|
-
throw domainError('stale_target', '
|
|
497
|
+
throw domainError('stale_target', hostMessage('thePreviewRecoveryAnalysisIdentityDoesNotMatch'));
|
|
497
498
|
const prepared = await options.module.preparePreviewRecoveryAnalysis(request.sessionId, request.recoveryProof, `preview-recovery-analysis:${randomUUID()}`);
|
|
498
499
|
const response = await mutationResponse(request.sessionId, request.checkoutId);
|
|
499
500
|
return {
|
|
@@ -517,7 +518,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
517
518
|
|| request.recoveryProof.revision !== request.expectedRevision
|
|
518
519
|
|| request.recoveryProof.reviewId !== request.expectedReviewId
|
|
519
520
|
|| request.recoveryProof.previewId !== request.expectedPreviewId)
|
|
520
|
-
throw domainError('stale_target', '
|
|
521
|
+
throw domainError('stale_target', hostMessage('thePreviewRecoveryHandoffIdentityDoesNotMatch'));
|
|
521
522
|
const targetSessionId = createTargetSessionId();
|
|
522
523
|
const launch = await options.module.createPreviewRecoveryHandoff(request.sessionId, request.recoveryProof, targetSessionId, `preview-recovery-handoff:${randomUUID()}`);
|
|
523
524
|
const created = recordOf(options.registry, launch.target.checkout.id);
|
|
@@ -526,7 +527,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
526
527
|
|| created.recoveryContinuation?.kind !== 'worktree_preview_recovery_handoff'
|
|
527
528
|
|| created.recoveryContinuation.requestId !== launch.continuation.requestId
|
|
528
529
|
|| observed.managedRoot !== launch.managedRoot)
|
|
529
|
-
throw domainError('checkout_mismatch', '
|
|
530
|
+
throw domainError('checkout_mismatch', hostMessage('hostIdentityVerificationFailedForTheRecoveryHandoffWorktree'));
|
|
530
531
|
return {
|
|
531
532
|
target: projectDetails(created, targetSessionId, observed.managedRoot, observed.snapshot, observed.dirty, false),
|
|
532
533
|
targetSessionId,
|
|
@@ -546,10 +547,10 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
546
547
|
checkpoint: request => outcome(async () => {
|
|
547
548
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
548
549
|
if (record.ownerSessionId !== request.sessionId)
|
|
549
|
-
throw domainError('not_owner', '
|
|
550
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanSaveACheckpoint447'));
|
|
550
551
|
const commitMessage = request.commitMessage.trim();
|
|
551
552
|
if (!commitMessage || commitMessage.length > 500 || !safeRecoveryRequestId(request.requestId)) {
|
|
552
|
-
throw domainError('invalid_input', '
|
|
553
|
+
throw domainError('invalid_input', hostMessage('theCheckpointCommitMessageOrRequestIdIsInvalid'));
|
|
553
554
|
}
|
|
554
555
|
const result = await options.module.operate({
|
|
555
556
|
action: 'checkpoint',
|
|
@@ -566,32 +567,32 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
566
567
|
return { ...(await mutationResponse(request.sessionId, request.checkoutId)), changedFiles: [...result.changedFiles] };
|
|
567
568
|
}
|
|
568
569
|
if (result.status !== 'checkpointed')
|
|
569
|
-
throw domainError('operation_not_allowed', '
|
|
570
|
+
throw domainError('operation_not_allowed', hostMessage('checkpointReturnedAnUnexpectedState'));
|
|
570
571
|
const response = await mutationResponse(request.sessionId, request.checkoutId);
|
|
571
572
|
if (response.target.state !== 'working' || response.target.revision !== result.target.revision) {
|
|
572
|
-
throw domainError('checkout_mismatch', '
|
|
573
|
+
throw domainError('checkout_mismatch', hostMessage('worktreeDidNotReturnToWorkingAfterCheckpoint'));
|
|
573
574
|
}
|
|
574
575
|
return { ...response, checkpoint: result.checkpoint, changedFiles: [...result.changedFiles] };
|
|
575
576
|
}),
|
|
576
577
|
preview: request => outcome(async () => {
|
|
577
578
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
578
579
|
if (record.ownerSessionId !== request.sessionId)
|
|
579
|
-
throw domainError('not_owner', '
|
|
580
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanPreviewChanges'));
|
|
580
581
|
if (record.revision !== request.expectedRevision)
|
|
581
|
-
throw domainError('stale_target', '
|
|
582
|
+
throw domainError('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
582
583
|
const review = readyReview(record);
|
|
583
584
|
if (review.reviewId !== request.expectedReviewId)
|
|
584
|
-
throw domainError('stale_target', '
|
|
585
|
+
throw domainError('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
585
586
|
const result = await options.module.operate({
|
|
586
587
|
action: 'preview', sessionId: request.sessionId, expectedRevision: request.expectedRevision,
|
|
587
588
|
});
|
|
588
589
|
if (result.status === 'error')
|
|
589
590
|
throw domainError(result.code, result.message);
|
|
590
591
|
if (result.status === 'conflict') {
|
|
591
|
-
throw domainError('apply_conflict', '
|
|
592
|
+
throw domainError('apply_conflict', hostMessage('localPreviewPreflightFoundContentConflicts'), applyConflictContinuation(result, review.reviewId));
|
|
592
593
|
}
|
|
593
594
|
if (result.status !== 'previewed')
|
|
594
|
-
throw domainError('operation_not_allowed', '
|
|
595
|
+
throw domainError('operation_not_allowed', hostMessage('previewReturnedAnUnexpectedState'));
|
|
595
596
|
return {
|
|
596
597
|
...(await mutationResponse(request.sessionId, request.checkoutId)),
|
|
597
598
|
changedFiles: [...result.changedFiles],
|
|
@@ -600,12 +601,12 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
600
601
|
resumeRevision: request => outcome(async () => {
|
|
601
602
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
602
603
|
if (record.ownerSessionId !== request.sessionId)
|
|
603
|
-
throw domainError('not_owner', '
|
|
604
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanResumeEditing456'));
|
|
604
605
|
if (record.revision !== request.expectedRevision)
|
|
605
|
-
throw domainError('stale_target', '
|
|
606
|
+
throw domainError('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
606
607
|
const review = readyReview(record);
|
|
607
608
|
if (review.reviewId !== request.expectedReviewId)
|
|
608
|
-
throw domainError('stale_target', '
|
|
609
|
+
throw domainError('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
609
610
|
const requestedRecovery = request.conflictContinuation;
|
|
610
611
|
if (requestedRecovery && (requestedRecovery.kind !== 'worktree_apply_conflict'
|
|
611
612
|
|| !safeRecoveryRequestId(requestedRecovery.requestId)
|
|
@@ -615,7 +616,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
615
616
|
|| !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(requestedRecovery.localHeadOid)
|
|
616
617
|
|| requestedRecovery.conflictingFiles.length > 500
|
|
617
618
|
|| !requestedRecovery.conflictingFiles.every(safeConflictFile)))
|
|
618
|
-
throw domainError('invalid_input', '
|
|
619
|
+
throw domainError('invalid_input', hostMessage('invalidConflictRecoveryRequestIdentity'));
|
|
619
620
|
const recovery = requestedRecovery ? {
|
|
620
621
|
kind: 'worktree_apply_conflict',
|
|
621
622
|
requestId: `conflict-recovery:${randomUUID()}`,
|
|
@@ -628,7 +629,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
628
629
|
? await options.module.resumeRevision(request.sessionId, request.expectedRevision, request.expectedReviewId, recovery)
|
|
629
630
|
: await options.module.resumeRevision(request.sessionId, request.expectedRevision, request.expectedReviewId);
|
|
630
631
|
if (target.checkout.id !== request.checkoutId || target.delivery?.state !== 'working') {
|
|
631
|
-
throw domainError('checkout_mismatch', '
|
|
632
|
+
throw domainError('checkout_mismatch', hostMessage('theWorktreeIdentityOrStateDoesNotMatchAfter'));
|
|
632
633
|
}
|
|
633
634
|
const response = await mutationResponse(request.sessionId, request.checkoutId);
|
|
634
635
|
if (!requestedRecovery)
|
|
@@ -638,7 +639,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
638
639
|
|| current.kind !== 'worktree_apply_conflict'
|
|
639
640
|
|| current.requestId !== recovery.requestId
|
|
640
641
|
|| current.workingRevision !== response.target.revision)
|
|
641
|
-
throw domainError('checkout_mismatch', '
|
|
642
|
+
throw domainError('checkout_mismatch', hostMessage('theHostCouldNotPersistTheExactConflictRecovery'));
|
|
642
643
|
return {
|
|
643
644
|
...response,
|
|
644
645
|
recoveryContinuation: {
|
|
@@ -655,12 +656,12 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
655
656
|
prepareReviewRegeneration: request => outcome(async () => {
|
|
656
657
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
657
658
|
if (record.ownerSessionId !== request.sessionId)
|
|
658
|
-
throw domainError('not_owner', '
|
|
659
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanRequestReviewRegeneration462'));
|
|
659
660
|
if (record.revision !== request.expectedRevision)
|
|
660
|
-
throw domainError('stale_target', '
|
|
661
|
+
throw domainError('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
661
662
|
const review = readyReview(record);
|
|
662
663
|
if (review.reviewId !== request.expectedReviewId)
|
|
663
|
-
throw domainError('stale_target', '
|
|
664
|
+
throw domainError('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
664
665
|
const prepared = await options.module.prepareReviewRegeneration(request.sessionId, request.expectedRevision, request.expectedReviewId, `review-regeneration:${randomUUID()}`);
|
|
665
666
|
const response = await mutationResponse(request.sessionId, request.checkoutId);
|
|
666
667
|
return {
|
|
@@ -677,7 +678,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
677
678
|
rollbackPreview: request => outcome(async () => {
|
|
678
679
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
679
680
|
if (record.ownerSessionId !== request.sessionId)
|
|
680
|
-
throw domainError('not_owner', '
|
|
681
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanRollBackLocal'));
|
|
681
682
|
const result = await options.module.operate({
|
|
682
683
|
action: 'rollback_preview',
|
|
683
684
|
sessionId: request.sessionId,
|
|
@@ -688,7 +689,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
688
689
|
if (result.status === 'error')
|
|
689
690
|
throw domainError(result.code, result.message);
|
|
690
691
|
if (result.status !== 'preview_rolled_back' && result.status !== 'preview_detached') {
|
|
691
|
-
throw domainError('operation_not_allowed', '
|
|
692
|
+
throw domainError('operation_not_allowed', hostMessage('rollbackPreviewReturnedAnUnexpectedState'));
|
|
692
693
|
}
|
|
693
694
|
return {
|
|
694
695
|
...(await mutationResponse(request.sessionId, request.checkoutId)),
|
|
@@ -698,7 +699,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
698
699
|
discard: request => outcome(async () => {
|
|
699
700
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
700
701
|
if (record.ownerSessionId !== request.sessionId && ownerSessionAvailable(record)) {
|
|
701
|
-
throw domainError('not_owner', '
|
|
702
|
+
throw domainError('not_owner', hostMessage('theOwnerSessionHasTakenOverThisWorktreeOnly467'));
|
|
702
703
|
}
|
|
703
704
|
if (record.ownerSessionId === request.sessionId) {
|
|
704
705
|
const result = await options.module.operate({
|
|
@@ -714,7 +715,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
714
715
|
return { ...(await mutationResponse(request.sessionId, request.checkoutId)), changedFiles: [...result.changedFiles] };
|
|
715
716
|
}
|
|
716
717
|
if (result.status !== 'discarded')
|
|
717
|
-
throw domainError('operation_not_allowed', '
|
|
718
|
+
throw domainError('operation_not_allowed', hostMessage('discardReturnedAnUnexpectedState'));
|
|
718
719
|
}
|
|
719
720
|
else {
|
|
720
721
|
await options.module.manageManagedWorktreeForSession(request.sessionId, {
|
|
@@ -730,15 +731,15 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
730
731
|
try {
|
|
731
732
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
732
733
|
if (record.ownerSessionId !== request.sessionId)
|
|
733
|
-
return failure('not_owner', '
|
|
734
|
+
return failure('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanCommitAcceptance'));
|
|
734
735
|
if (record.revision !== request.expectedRevision)
|
|
735
|
-
return failure('stale_target', '
|
|
736
|
+
return failure('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
736
737
|
const review = readyReview(record);
|
|
737
738
|
if (review.reviewId !== request.expectedReviewId)
|
|
738
|
-
return failure('stale_target', '
|
|
739
|
+
return failure('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
739
740
|
const commitMessage = request.commitMessage.trim();
|
|
740
741
|
if (!commitMessage || commitMessage.length > 500)
|
|
741
|
-
return failure('invalid_input', '
|
|
742
|
+
return failure('invalid_input', hostMessage('theCommitMessageMustContain1500Characters'));
|
|
742
743
|
const result = await options.module.operate({
|
|
743
744
|
sessionId: request.sessionId,
|
|
744
745
|
expectedRevision: request.expectedRevision,
|
|
@@ -750,10 +751,10 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
750
751
|
if (result.status === 'error')
|
|
751
752
|
return failure(result.code, result.message);
|
|
752
753
|
if (result.status === 'conflict') {
|
|
753
|
-
return failure('apply_conflict', '
|
|
754
|
+
return failure('apply_conflict', hostMessage('applyingToLocalCausedConflicts'), undefined, applyConflictContinuation(result, review.reviewId));
|
|
754
755
|
}
|
|
755
756
|
if (result.status !== 'finished')
|
|
756
|
-
return failure('operation_not_allowed', '
|
|
757
|
+
return failure('operation_not_allowed', hostMessage('finalizeReturnedAnUnexpectedState'));
|
|
757
758
|
return {
|
|
758
759
|
ok: true,
|
|
759
760
|
value: {
|
|
@@ -771,15 +772,15 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
771
772
|
try {
|
|
772
773
|
const record = await authorize(request.sessionId, request.checkoutId);
|
|
773
774
|
if (record.ownerSessionId !== request.sessionId)
|
|
774
|
-
return failure('not_owner', '
|
|
775
|
+
return failure('not_owner', hostMessage('onlyTheOwnerIsolatedSessionCanFinalizeLocalPreview'));
|
|
775
776
|
if (record.revision !== request.expectedRevision)
|
|
776
|
-
return failure('stale_target', '
|
|
777
|
+
return failure('stale_target', hostMessage('theSessionTargetHasChangedRefresh'));
|
|
777
778
|
const review = previewReview(record);
|
|
778
779
|
if (review.reviewId !== request.expectedReviewId)
|
|
779
|
-
return failure('stale_target', '
|
|
780
|
+
return failure('stale_target', hostMessage('theReviewIdentityHasChangedRefresh'));
|
|
780
781
|
const commitMessage = request.commitMessage.trim();
|
|
781
782
|
if (!commitMessage || commitMessage.length > 500)
|
|
782
|
-
return failure('invalid_input', '
|
|
783
|
+
return failure('invalid_input', hostMessage('theCommitMessageMustContain1500Characters'));
|
|
783
784
|
const result = await options.module.operate({
|
|
784
785
|
action: 'finalize_preview',
|
|
785
786
|
sessionId: request.sessionId,
|
|
@@ -800,7 +801,7 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
800
801
|
};
|
|
801
802
|
}
|
|
802
803
|
if (result.status !== 'finished')
|
|
803
|
-
return failure('operation_not_allowed', '
|
|
804
|
+
return failure('operation_not_allowed', hostMessage('finalizePreviewReturnedAnUnexpectedState'));
|
|
804
805
|
return {
|
|
805
806
|
ok: true,
|
|
806
807
|
value: {
|
|
@@ -836,28 +837,28 @@ export function createWorktreeConsoleControlPlane(options) {
|
|
|
836
837
|
beginNextIteration: request => outcome(async () => {
|
|
837
838
|
const predecessor = recordOf(options.registry, request.checkoutId);
|
|
838
839
|
if (predecessor.ownerSessionId !== request.sessionId) {
|
|
839
|
-
throw domainError('not_owner', '
|
|
840
|
+
throw domainError('not_owner', hostMessage('onlyTheOwnerSessionCanBeginTheNextIteration'));
|
|
840
841
|
}
|
|
841
842
|
if (predecessor.revision !== request.expectedRevision) {
|
|
842
|
-
throw domainError('stale_target', '
|
|
843
|
+
throw domainError('stale_target', hostMessage('theWorktreeStateHasChangedRefreshBeforeBeginningThe'));
|
|
843
844
|
}
|
|
844
845
|
if (predecessor.phase !== 'discarded' || predecessor.delivery.state !== 'delivered') {
|
|
845
|
-
throw domainError('operation_not_allowed', '
|
|
846
|
+
throw domainError('operation_not_allowed', hostMessage('onlyADeliveredStateWithSuccessfulCleanupCanBegin'));
|
|
846
847
|
}
|
|
847
848
|
const session = options.lookup.getSession(request.sessionId);
|
|
848
849
|
const workspace = session?.projectId === undefined ? undefined : options.lookup.getProject(session.projectId);
|
|
849
850
|
if (!workspace || !sameLocalRoot(resolve(workspace.root), resolve(predecessor.managedRoot))) {
|
|
850
|
-
throw domainError('project_mismatch', '
|
|
851
|
+
throw domainError('project_mismatch', hostMessage('theCurrentSessionSImmutableCwdDoesNotMatch'));
|
|
851
852
|
}
|
|
852
853
|
const target = await options.module.beginNextIteration(request.sessionId, request.expectedRevision);
|
|
853
854
|
if (target.checkout.kind !== 'isolated' || target.delivery?.state !== 'working') {
|
|
854
|
-
throw domainError('checkout_mismatch', '
|
|
855
|
+
throw domainError('checkout_mismatch', hostMessage('theNextWorktreeIterationDidNotEnterWorkingState484'));
|
|
855
856
|
}
|
|
856
857
|
const record = recordOf(options.registry, target.checkout.id);
|
|
857
858
|
if (record.predecessorCheckoutId !== predecessor.checkoutId
|
|
858
859
|
|| record.ownerSessionId !== request.sessionId
|
|
859
860
|
|| !sameLocalRoot(record.managedRoot, predecessor.managedRoot)) {
|
|
860
|
-
throw domainError('checkout_mismatch', '
|
|
861
|
+
throw domainError('checkout_mismatch', hostMessage('theNextIterationSWorktreeLineageOrCwdIdentity'));
|
|
861
862
|
}
|
|
862
863
|
const observed = await observe(record);
|
|
863
864
|
return {
|