@neuralnomads/codenomad-dev 0.20.0-dev-20260911-ea37f97b → 0.20.0-dev-20260917-aa51cbb9
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/README.md +2 -2
- package/dist/api-types.js.map +1 -1
- package/dist/opencode/automation-plugin.test.js +32 -38
- package/dist/opencode/automation-plugin.test.js.map +1 -1
- package/dist/opencode/compatibility/contract-fixture.js +15 -0
- package/dist/opencode/compatibility/contract-fixture.js.map +1 -0
- package/dist/opencode/compatibility/events.js +9 -0
- package/dist/opencode/compatibility/events.js.map +1 -0
- package/dist/opencode/compatibility/location.js +89 -0
- package/dist/opencode/compatibility/location.js.map +1 -0
- package/dist/opencode/compatibility/location.test.js +92 -0
- package/dist/opencode/compatibility/location.test.js.map +1 -0
- package/dist/opencode/compatibility/negotiate.js +73 -0
- package/dist/opencode/compatibility/negotiate.js.map +1 -0
- package/dist/opencode/compatibility/negotiate.test.js +61 -0
- package/dist/opencode/compatibility/negotiate.test.js.map +1 -0
- package/dist/opencode/compatibility/proxy-locations.js +137 -0
- package/dist/opencode/compatibility/proxy-locations.js.map +1 -0
- package/dist/opencode/compatibility/proxy-locations.test.js +42 -0
- package/dist/opencode/compatibility/proxy-locations.test.js.map +1 -0
- package/dist/opencode/compatibility/proxy.js +60 -0
- package/dist/opencode/compatibility/proxy.js.map +1 -0
- package/dist/opencode/compatibility/proxy.test.js +47 -0
- package/dist/opencode/compatibility/proxy.test.js.map +1 -0
- package/dist/opencode/compatibility/requests.js +54 -0
- package/dist/opencode/compatibility/requests.js.map +1 -0
- package/dist/opencode/compatibility/runtime.js +26 -0
- package/dist/opencode/compatibility/runtime.js.map +1 -0
- package/dist/opencode/compatibility/transport.js +112 -0
- package/dist/opencode/compatibility/transport.js.map +1 -0
- package/dist/opencode/compatibility/transport.test.js +83 -0
- package/dist/opencode/compatibility/transport.test.js.map +1 -0
- package/dist/opencode/session-pruning/location.js +16 -0
- package/dist/opencode/session-pruning/location.js.map +1 -0
- package/dist/opencode/session-pruning/package.test.js +37 -0
- package/dist/opencode/session-pruning/package.test.js.map +1 -0
- package/dist/opencode/session-pruning/plugin.js +5 -2
- package/dist/opencode/session-pruning/plugin.js.map +1 -1
- package/dist/opencode/session-pruning/preview-store.js +3 -2
- package/dist/opencode/session-pruning/preview-store.js.map +1 -1
- package/dist/opencode/session-pruning/pruning.test.js +7 -5
- package/dist/opencode/session-pruning/pruning.test.js.map +1 -1
- package/dist/opencode/session-pruning/service.js +5 -4
- package/dist/opencode/session-pruning/service.js.map +1 -1
- package/dist/opencode/session-pruning/service.test.js +65 -53
- package/dist/opencode/session-pruning/service.test.js.map +1 -1
- package/dist/permissions/opencode-replier.js +2 -2
- package/dist/permissions/opencode-replier.js.map +1 -1
- package/dist/permissions/opencode-replier.test.js +1 -1
- package/dist/permissions/opencode-replier.test.js.map +1 -1
- package/dist/permissions/opencode-yolo-metadata.js.map +1 -1
- package/dist/permissions/opencode-yolo-metadata.test.js.map +1 -1
- package/dist/plugins/session-pruning/plugin.mjs +422 -271
- package/dist/server/__tests__/instance-proxy-legacy.test.js +111 -0
- package/dist/server/__tests__/instance-proxy-legacy.test.js.map +1 -0
- package/dist/server/__tests__/instance-proxy.test.js +95 -34
- package/dist/server/__tests__/instance-proxy.test.js.map +1 -1
- package/dist/server/http-server.js +147 -237
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/routes/session-pruning.js +4 -2
- package/dist/server/routes/session-pruning.js.map +1 -1
- package/dist/server/routes/session-pruning.test.js +16 -2
- package/dist/server/routes/session-pruning.test.js.map +1 -1
- package/dist/server/routes/worktrees.js +1 -0
- package/dist/server/routes/worktrees.js.map +1 -1
- package/dist/server/routes/worktrees.test.js +54 -1
- package/dist/server/routes/worktrees.test.js.map +1 -1
- package/dist/workspaces/host-opencode-service.test.js +56 -7
- package/dist/workspaces/host-opencode-service.test.js.map +1 -1
- package/dist/workspaces/instance-client.js.map +1 -1
- package/dist/workspaces/instance-events.js +71 -57
- package/dist/workspaces/instance-events.js.map +1 -1
- package/dist/workspaces/instance-events.test.js +122 -9
- package/dist/workspaces/instance-events.test.js.map +1 -1
- package/dist/workspaces/manager.js +24 -39
- package/dist/workspaces/manager.js.map +1 -1
- package/dist/workspaces/manager.test.js +43 -7
- package/dist/workspaces/manager.test.js.map +1 -1
- package/dist/workspaces/opencode-cli-service.js +56 -29
- package/dist/workspaces/opencode-cli-service.js.map +1 -1
- package/dist/workspaces/opencode-connection-generation.test.js +57 -0
- package/dist/workspaces/opencode-connection-generation.test.js.map +1 -0
- package/dist/workspaces/opencode-service.js +70 -16
- package/dist/workspaces/opencode-service.js.map +1 -1
- package/dist/workspaces/opencode-service.test.js +3 -4
- package/dist/workspaces/opencode-service.test.js.map +1 -1
- package/dist/workspaces/worktree-session-evacuation.js +31 -11
- package/dist/workspaces/worktree-session-evacuation.js.map +1 -1
- package/dist/workspaces/worktree-session-evacuation.test.js +211 -0
- package/dist/workspaces/worktree-session-evacuation.test.js.map +1 -1
- package/dist/workspaces/wsl-opencode-service.test.js +64 -24
- package/dist/workspaces/wsl-opencode-service.test.js.map +1 -1
- package/package.json +3 -3
- package/public/assets/App-BROhwPo9.js +54 -0
- package/public/assets/App-BROhwPo9.js.map +1 -0
- package/public/assets/{FilesTab-CRpGDs1B.js → FilesTab-DY3Vr-3r.js} +3 -3
- package/public/assets/{FilesTab-CRpGDs1B.js.map → FilesTab-DY3Vr-3r.js.map} +1 -1
- package/public/assets/{GitChangesTab-DxFebTiq.js → GitChangesTab-BD6CJngE.js} +3 -3
- package/public/assets/{GitChangesTab-DxFebTiq.js.map → GitChangesTab-BD6CJngE.js.map} +1 -1
- package/public/assets/{SplitFilePanel-DgTMrrO-.js → SplitFilePanel-BdY6d_6m.js} +2 -2
- package/public/assets/{SplitFilePanel-DgTMrrO-.js.map → SplitFilePanel-BdY6d_6m.js.map} +1 -1
- package/public/assets/StatusTab-C-_2yaPa.js +2 -0
- package/public/assets/StatusTab-C-_2yaPa.js.map +1 -0
- package/public/assets/{align-justify-v_yPXSys.js → align-justify-DI8VTZ4G.js} +2 -2
- package/public/assets/{align-justify-v_yPXSys.js.map → align-justify-DI8VTZ4G.js.map} +1 -1
- package/public/assets/{bundle-full-HzL_Jtqk.js → bundle-full-D_M65Eb_.js} +2 -2
- package/public/assets/{bundle-full-HzL_Jtqk.js.map → bundle-full-D_M65Eb_.js.map} +1 -1
- package/public/assets/{client-state-CF-PJ0GG.js → client-state-CWCzCOz6.js} +2 -2
- package/public/assets/{client-state-CF-PJ0GG.js.map → client-state-CWCzCOz6.js.map} +1 -1
- package/public/assets/{diff-viewer-BFDQBDVy.js → diff-viewer-CGPsYEr3.js} +2 -2
- package/public/assets/{diff-viewer-BFDQBDVy.js.map → diff-viewer-CGPsYEr3.js.map} +1 -1
- package/public/assets/index-B8-lAqjr.js +2 -0
- package/public/assets/index-B8-lAqjr.js.map +1 -0
- package/public/assets/index-BeUewQym.js +2 -0
- package/public/assets/index-BeUewQym.js.map +1 -0
- package/public/assets/index-BxRFHpJd.js +2 -0
- package/public/assets/{index-D2UHSOG5.js.map → index-BxRFHpJd.js.map} +1 -1
- package/public/assets/index-Bzv1kU-S.js +2 -0
- package/public/assets/{index-CTKYD_VJ.js.map → index-Bzv1kU-S.js.map} +1 -1
- package/public/assets/index-C9tI4CRG.js +2 -0
- package/public/assets/index-C9tI4CRG.js.map +1 -0
- package/public/assets/index-CGYHFZ6F.js +2 -0
- package/public/assets/index-CGYHFZ6F.js.map +1 -0
- package/public/assets/{index-CHYmKl10.js → index-CMEAjBFp.js} +2 -2
- package/public/assets/index-CMEAjBFp.js.map +1 -0
- package/public/assets/index-Cazypvyc.js +2 -0
- package/public/assets/index-Cazypvyc.js.map +1 -0
- package/public/assets/index-CwqzofWP.js +2 -0
- package/public/assets/index-CwqzofWP.js.map +1 -0
- package/public/assets/index-Da9yOa6q.js +2 -0
- package/public/assets/index-Da9yOa6q.js.map +1 -0
- package/public/assets/{index-RK9gf0uk.js → index-Ddjkirek.js} +2 -2
- package/public/assets/{index-RK9gf0uk.js.map → index-Ddjkirek.js.map} +1 -1
- package/public/assets/index-HHgvEOAH.js +3 -0
- package/public/assets/index-HHgvEOAH.js.map +1 -0
- package/public/assets/index-MeFffHqb.css +1 -0
- package/public/assets/loading-CdhHiSED.css +1 -0
- package/public/assets/{loading-BSB82snY.js → loading-Gqh6NPUr.js} +2 -2
- package/public/assets/{loading-BSB82snY.js.map → loading-Gqh6NPUr.js.map} +1 -1
- package/public/assets/main-Cbx7KdTr.js +38 -0
- package/public/assets/main-Cbx7KdTr.js.map +1 -0
- package/public/assets/{markdown-DtidoL2Z.js → markdown-BWnjyvnX.js} +4 -4
- package/public/assets/{markdown-DtidoL2Z.js.map → markdown-BWnjyvnX.js.map} +1 -1
- package/public/assets/monaco-viewer-BEJIiZWL.js +27 -0
- package/public/assets/monaco-viewer-BEJIiZWL.js.map +1 -0
- package/public/assets/{startup-state-settings-card-BY741Rt0.js → startup-state-settings-card-C4E2Itop.js} +2 -2
- package/public/assets/{startup-state-settings-card-BY741Rt0.js.map → startup-state-settings-card-C4E2Itop.js.map} +1 -1
- package/public/assets/{terminal-square-BdC_jwm3.js → terminal-square-C3wutdV0.js} +2 -2
- package/public/assets/{terminal-square-BdC_jwm3.js.map → terminal-square-C3wutdV0.js.map} +1 -1
- package/public/assets/tool-call-DNsAQ_m4.js +60 -0
- package/public/assets/tool-call-DNsAQ_m4.js.map +1 -0
- package/public/assets/{unified-picker-C0eo1chJ.js → unified-picker-tB7y17Qz.js} +2 -2
- package/public/assets/unified-picker-tB7y17Qz.js.map +1 -0
- package/public/assets/{wrap-text-BuMtdvMx.js → wrap-text-CkBxzt04.js} +2 -2
- package/public/assets/{wrap-text-BuMtdvMx.js.map → wrap-text-CkBxzt04.js.map} +1 -1
- package/public/index.html +4 -4
- package/public/loading.html +5 -5
- package/public/sw.js +1 -1
- package/public/sw.js.map +1 -1
- package/public/assets/App-0-FcHzPS.js +0 -44
- package/public/assets/App-0-FcHzPS.js.map +0 -1
- package/public/assets/StatusTab-LKUA9cbH.js +0 -2
- package/public/assets/StatusTab-LKUA9cbH.js.map +0 -1
- package/public/assets/index-8iPlm2r0.js +0 -2
- package/public/assets/index-8iPlm2r0.js.map +0 -1
- package/public/assets/index-B185mC7m.js +0 -2
- package/public/assets/index-B185mC7m.js.map +0 -1
- package/public/assets/index-Bbj1xF8i.js +0 -2
- package/public/assets/index-Bbj1xF8i.js.map +0 -1
- package/public/assets/index-BsmFN3k1.js +0 -2
- package/public/assets/index-BsmFN3k1.js.map +0 -1
- package/public/assets/index-Bz_OQJYA.css +0 -1
- package/public/assets/index-C52CfqK3.js +0 -2
- package/public/assets/index-C52CfqK3.js.map +0 -1
- package/public/assets/index-CHYmKl10.js.map +0 -1
- package/public/assets/index-CTKYD_VJ.js +0 -2
- package/public/assets/index-D2UHSOG5.js +0 -2
- package/public/assets/index-DDoB_Z36.js +0 -2
- package/public/assets/index-DDoB_Z36.js.map +0 -1
- package/public/assets/index-DcwAtZw-.js +0 -3
- package/public/assets/index-DcwAtZw-.js.map +0 -1
- package/public/assets/index-DxUD3RYr.js +0 -2
- package/public/assets/index-DxUD3RYr.js.map +0 -1
- package/public/assets/loading-CmEVQgyj.css +0 -1
- package/public/assets/main-CZztwrfv.js +0 -48
- package/public/assets/main-CZztwrfv.js.map +0 -1
- package/public/assets/monaco-viewer-CpxDfB-6.js +0 -27
- package/public/assets/monaco-viewer-CpxDfB-6.js.map +0 -1
- package/public/assets/tool-call-DbtCsJ4x.js +0 -60
- package/public/assets/tool-call-DbtCsJ4x.js.map +0 -1
- package/public/assets/unified-picker-C0eo1chJ.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeWslUncPath } from "./worktree-directory";
|
|
2
|
+
import { moveSessionToLocation, readLocationRef, sameLocation } from "../opencode/compatibility/location";
|
|
2
3
|
const PAGE_SIZE = 200;
|
|
3
4
|
const MAX_PAGES = 10_000;
|
|
4
5
|
const MAX_SESSIONS = 1_000_000;
|
|
@@ -118,13 +119,27 @@ async function waitForInventory(client, project, predicate) {
|
|
|
118
119
|
}
|
|
119
120
|
export async function evacuateWorktreeSessions(params) {
|
|
120
121
|
const identity = async (directory) => normalizeDirectory(await params.resolveDirectoryIdentity?.(directory) ?? directory);
|
|
122
|
+
// Containing-worktree identity intentionally groups descendant sessions. It
|
|
123
|
+
// cannot identify a project root or destination: nested repositories differ.
|
|
124
|
+
const exactDirectory = async (directory) => {
|
|
125
|
+
const resolved = params.resolveExactDirectory ? await params.resolveExactDirectory(directory) : directory;
|
|
126
|
+
return resolved === undefined ? undefined : normalizeDirectory(resolved);
|
|
127
|
+
};
|
|
128
|
+
const [projectDirectory, targetDirectory, rootDirectory] = await Promise.all([
|
|
129
|
+
exactDirectory(params.projectDirectory), exactDirectory(params.targetDirectory), exactDirectory(params.rootDirectory),
|
|
130
|
+
]);
|
|
131
|
+
if (projectDirectory === undefined || targetDirectory === undefined || rootDirectory === undefined) {
|
|
132
|
+
throw new Error("Unable to resolve owned directories before deleting worktree");
|
|
133
|
+
}
|
|
121
134
|
const target = await identity(params.targetDirectory);
|
|
122
135
|
const matchesTarget = async (directory) => await identity(directory) === target;
|
|
123
136
|
const projects = await params.client.project.list();
|
|
124
137
|
let project;
|
|
125
138
|
for (const candidate of projects) {
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
// Resolve aliases without collapsing a nested repository or nested sandbox
|
|
140
|
+
// into its containing worktree. Both comparisons require exact directories.
|
|
141
|
+
if (await exactDirectory(candidate.canonical) === projectDirectory
|
|
142
|
+
|| (await Promise.all(candidate.sandboxes.map(exactDirectory))).includes(targetDirectory)) {
|
|
128
143
|
project = candidate;
|
|
129
144
|
break;
|
|
130
145
|
}
|
|
@@ -141,14 +156,23 @@ export async function evacuateWorktreeSessions(params) {
|
|
|
141
156
|
};
|
|
142
157
|
await assertInactive();
|
|
143
158
|
const moved = [];
|
|
159
|
+
// Resolve before any move; a directory-only fallback could silently move a
|
|
160
|
+
// legacy session into a different native workspace.
|
|
161
|
+
const rootLocation = readLocationRef(await params.client.location.get({ location: { directory: params.rootDirectory } }));
|
|
162
|
+
if (await exactDirectory(rootLocation.directory) !== rootDirectory) {
|
|
163
|
+
throw new Error("OpenCode resolved a foreign evacuation destination");
|
|
164
|
+
}
|
|
144
165
|
try {
|
|
145
166
|
for (const session of affected) {
|
|
146
167
|
await assertInactive();
|
|
147
168
|
const original = { ...session, location: { ...session.location } };
|
|
148
|
-
await params.client
|
|
169
|
+
await moveSessionToLocation(params.client, session.id, rootLocation);
|
|
149
170
|
moved.push(original);
|
|
150
171
|
}
|
|
151
|
-
await waitForInventory(params.client, project.id, async (current) => (
|
|
172
|
+
await waitForInventory(params.client, project.id, async (current) => (moved.every((original) => {
|
|
173
|
+
const session = current.find((candidate) => candidate.id === original.id);
|
|
174
|
+
return session && sameLocation(readLocationRef(session.location), rootLocation);
|
|
175
|
+
}) && !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)));
|
|
152
176
|
const finalInventory = await inventorySessions(params.client, project.id);
|
|
153
177
|
const finalAffected = (await Promise.all(finalInventory.map(async (session) => (await matchesTarget(session.location.directory) ? session : undefined)))).filter((session) => Boolean(session));
|
|
154
178
|
await assertInactive(finalAffected);
|
|
@@ -160,22 +184,18 @@ export async function evacuateWorktreeSessions(params) {
|
|
|
160
184
|
const rollbackErrors = [];
|
|
161
185
|
for (const session of moved.reverse()) {
|
|
162
186
|
try {
|
|
163
|
-
await params.client.session.
|
|
164
|
-
sessionID: session.id,
|
|
165
|
-
directory: session.location.directory,
|
|
166
|
-
workspaceID: session.location.workspaceID,
|
|
167
|
-
});
|
|
187
|
+
await moveSessionToLocation(params.client, session.id, readLocationRef(session.location));
|
|
168
188
|
}
|
|
169
189
|
catch (rollbackError) {
|
|
170
190
|
rollbackErrors.push(rollbackError);
|
|
171
191
|
}
|
|
172
192
|
}
|
|
173
193
|
try {
|
|
174
|
-
const expected = new
|
|
194
|
+
const expected = new Map(moved.map((session) => [session.id, readLocationRef(session.location)]));
|
|
175
195
|
await waitForInventory(params.client, project.id, async (current) => {
|
|
176
196
|
const restored = current.filter((session) => expected.has(session.id));
|
|
177
197
|
return restored.length === expected.size
|
|
178
|
-
&&
|
|
198
|
+
&& restored.every((session) => sameLocation(readLocationRef(session.location), expected.get(session.id)));
|
|
179
199
|
});
|
|
180
200
|
}
|
|
181
201
|
catch (rollbackError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktree-session-evacuation.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE1D,MAAM,SAAS,GAAG,GAAG,CAAA;AACrB,MAAM,SAAS,GAAG,MAAM,CAAA;AACxB,MAAM,YAAY,GAAG,SAAS,CAAA;AAC9B,MAAM,yBAAyB,GAAG,MAAM,CAAA;AAExC,SAAS,kBAAkB,CAAC,SAAiB;IAC3C,MAAM,GAAG,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAA;IAC1C,IAAI,GAAG;QAAE,OAAO,GAAG,CAAA;IACnB,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAA;IAClF,OAAO,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;AAC/G,CAAC;AAED,MAAM,OAAO,qBAAqB;IAMhC,YAA6B,yBAAyB,yBAAyB;QAAlD,2BAAsB,GAAtB,sBAAsB,CAA4B;QAL9D,WAAM,GAAG,IAAI,GAAG,EAA4B,CAAA;QAC5C,YAAO,GAAG,IAAI,GAAG,EAAkB,CAAA;QACnC,WAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;QAClC,gBAAW,GAAG,IAAI,GAAG,EAA2B,CAAA;IAEiB,CAAC;IAEnF,SAAS,CAAC,SAAiB;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAA;IACxD,CAAC;IAED,KAAK,CAAC,WAAqB;QACzB,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAA;QACpE,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;QACjF,KAAK,MAAM,SAAS,IAAI,UAAU;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAErG,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,OAAO,GAAG,EAAE;YACV,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,IAAI,CAAA;YACf,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;gBAC7C,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;oBACd,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;oBACrC,SAAQ;gBACV,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;gBAC7B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE;oBAAE,OAAO,EAAE,CAAA;gBACtE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YACpC,CAAC;QACH,CAAC,CAAA;IACH,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,WAAqB,EAAE,SAA2B;QACpE,MAAM,aAAa,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;QAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAA;QACjE,KAAK,MAAM,SAAS,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAEpG,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QACpE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YACvD,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAC1E,OAAO,SAAS,EAAE,CAAA;QACpB,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;QACvC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;YAC1B,KAAK,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;gBAC9C,IAAI,KAAK,GAAG,CAAC;oBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;;oBAChD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YACrC,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,OAAO;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QACnF,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,WAAW,CAAC,SAAiB;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QACzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,GAAG,EAAE,CAAA;YAC5D,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,YAAY,CAAC,KAAK,CAAC,CAAA;gBACnB,OAAO,EAAE,CAAA;YACX,CAAC,CAAA;YACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACjB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACpB,IAAI,CAAC,OAAO,CAAC,IAAI;oBAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;gBACrD,MAAM,CAAC,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC,CAAA;YACzE,CAAC,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAED,KAAK,UAAU,iBAAiB,CAAC,MAAsB,EAAE,OAAe;IACtE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC/C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,IAAI,MAA0B,CAAA;IAE9B,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,SAAS,IAAI,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACzG,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;YACjC,IAAI,QAAQ,CAAC,IAAI,GAAG,YAAY;gBAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;QAClG,CAAC;QAED,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAA;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QACjD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,EAAE,CAAC,CAAA;QACxF,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;AAC9D,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,MAAsB,EACtB,OAAe,EACf,SAAkE;IAElE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAClD,IAAI,MAAM,SAAS,CAAC,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAE,OAAM;QACrE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;IACzD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,MAO9C;IACC,MAAM,QAAQ,GAAG,KAAK,EAAE,SAAiB,EAAE,EAAE,CAAC,kBAAkB,CAC9D,MAAM,MAAM,CAAC,wBAAwB,EAAE,CAAC,SAAS,CAAC,IAAI,SAAS,CAChE,CAAA;IACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,CAAA;IACrD,MAAM,aAAa,GAAG,KAAK,EAAE,SAAiB,EAAE,EAAE,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,MAAM,CAAA;IACvF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IACnD,IAAI,OAA8C,CAAA;IAClD,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,IAAI,kBAAkB,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,CAAC;eACtF,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/E,OAAO,GAAG,SAAS,CAAA;YACnB,MAAK;QACP,CAAC;IACH,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAA;IAChG,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IACnE,MAAM,QAAQ,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAClE,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACtE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAA0B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;IAClE,MAAM,cAAc,GAAG,KAAK,EAAE,UAAU,GAAG,QAAQ,EAAE,EAAE;QACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;QACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;QACzG,IAAI,QAAQ,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACtI,CAAC,CAAA;IACD,MAAM,cAAc,EAAE,CAAA;IAEtB,MAAM,KAAK,GAAkB,EAAE,CAAA;IAC/B,IAAI,CAAC;QACH,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,cAAc,EAAE,CAAA;YACtB,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAA;YAClE,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;YAC5F,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACtB,CAAC;QACD,MAAM,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CACnE,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CACxG,CAAC,CAAA;QACF,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;QACzE,MAAM,aAAa,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAC7E,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACtE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAA0B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;QAClE,MAAM,cAAc,CAAC,aAAa,CAAC,CAAA;QACnC,IAAI,aAAa,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;QAC9F,MAAM,MAAM,CAAC,MAAM,EAAE,CAAA;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,cAAc,GAAc,EAAE,CAAA;QACpC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;oBAC/B,SAAS,EAAE,OAAO,CAAC,EAAE;oBACrB,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS;oBACrC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW;iBAC1C,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,aAAa,EAAE,CAAC;gBACvB,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;YAC5D,MAAM,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;gBACtE,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,IAAI;uBACnC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC/G,CAAC,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,aAAa,EAAE,CAAC;YACvB,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACpC,CAAC;QACD,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,GAAG,cAAc,CAAC,EAAE,wDAAwD,CAAC,CAAA;QAChH,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC","sourcesContent":["import type { OpenCodeClient, SessionInfo } from \"@opencode-ai/client\"\nimport { normalizeWslUncPath } from \"./worktree-directory\"\n\nconst PAGE_SIZE = 200\nconst MAX_PAGES = 10_000\nconst MAX_SESSIONS = 1_000_000\nconst MUTATION_DRAIN_TIMEOUT_MS = 30_000\n\nfunction normalizeDirectory(directory: string): string {\n const wsl = normalizeWslUncPath(directory)\n if (wsl) return wsl\n const normalized = directory.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \"/\"\n return /^[A-Za-z]:\\//.test(normalized) || normalized.startsWith(\"//\") ? normalized.toLowerCase() : normalized\n}\n\nexport class WorktreeDeletionFence {\n private readonly queues = new Map<string, Promise<unknown>>()\n private readonly blocked = new Map<string, number>()\n private readonly active = new Map<string, number>()\n private readonly idleWaiters = new Map<string, Set<() => void>>()\n\n constructor(private readonly mutationDrainTimeoutMs = MUTATION_DRAIN_TIMEOUT_MS) {}\n\n isBlocked(directory: string): boolean {\n return this.blocked.has(normalizeDirectory(directory))\n }\n\n enter(directories: string[]): (() => void) | undefined {\n const normalized = [...new Set(directories.map(normalizeDirectory))]\n if (normalized.some((directory) => this.blocked.has(directory))) return undefined\n for (const directory of normalized) this.active.set(directory, (this.active.get(directory) ?? 0) + 1)\n\n let released = false\n return () => {\n if (released) return\n released = true\n for (const directory of normalized) {\n const count = this.active.get(directory) ?? 0\n if (count > 1) {\n this.active.set(directory, count - 1)\n continue\n }\n this.active.delete(directory)\n for (const resolve of this.idleWaiters.get(directory) ?? []) resolve()\n this.idleWaiters.delete(directory)\n }\n }\n }\n\n run<T>(key: string, directories: string[], operation: () => Promise<T>): Promise<T> {\n const normalizedKey = normalizeDirectory(key)\n const blocked = [...new Set(directories.map(normalizeDirectory))]\n for (const directory of blocked) this.blocked.set(directory, (this.blocked.get(directory) ?? 0) + 1)\n\n const previous = this.queues.get(normalizedKey) ?? Promise.resolve()\n const current = previous.catch(() => {}).then(async () => {\n await Promise.all(blocked.map((directory) => this.waitForIdle(directory)))\n return operation()\n })\n this.queues.set(normalizedKey, current)\n return current.finally(() => {\n for (const directory of blocked) {\n const count = this.blocked.get(directory) ?? 0\n if (count > 1) this.blocked.set(directory, count - 1)\n else this.blocked.delete(directory)\n }\n if (this.queues.get(normalizedKey) === current) this.queues.delete(normalizedKey)\n })\n }\n\n private waitForIdle(directory: string): Promise<void> {\n if (!this.active.has(directory)) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const waiters = this.idleWaiters.get(directory) ?? new Set()\n const done = () => {\n clearTimeout(timer)\n resolve()\n }\n waiters.add(done)\n this.idleWaiters.set(directory, waiters)\n const timer = setTimeout(() => {\n waiters.delete(done)\n if (!waiters.size) this.idleWaiters.delete(directory)\n reject(new Error(\"Timed out waiting for worktree mutations to finish\"))\n }, this.mutationDrainTimeoutMs)\n })\n }\n}\n\nasync function inventorySessions(client: OpenCodeClient, project: string): Promise<SessionInfo[]> {\n const sessions = new Map<string, SessionInfo>()\n const cursors = new Set<string>()\n let cursor: string | undefined\n\n for (let pageCount = 0; pageCount < MAX_PAGES; pageCount += 1) {\n const page = await client.session.list(cursor ? { cursor } : { project, limit: PAGE_SIZE, order: \"asc\" })\n for (const session of page.data) {\n sessions.set(session.id, session)\n if (sessions.size > MAX_SESSIONS) throw new Error(\"Session inventory exceeded its safety limit\")\n }\n\n cursor = page.cursor.next ?? undefined\n if (!cursor) return Array.from(sessions.values())\n if (cursors.has(cursor)) throw new Error(`Repeated session inventory cursor: ${cursor}`)\n cursors.add(cursor)\n }\n\n throw new Error(\"Session inventory exceeded its page limit\")\n}\n\nasync function waitForInventory(\n client: OpenCodeClient,\n project: string,\n predicate: (sessions: SessionInfo[]) => boolean | Promise<boolean>,\n): Promise<void> {\n for (let attempt = 0; attempt < 100; attempt += 1) {\n if (await predicate(await inventorySessions(client, project))) return\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n throw new Error(\"Timed out waiting for session moves\")\n}\n\nexport async function evacuateWorktreeSessions(params: {\n client: OpenCodeClient\n projectDirectory: string\n targetDirectory: string\n rootDirectory: string\n resolveDirectoryIdentity?: (directory: string) => Promise<string | undefined>\n remove: () => Promise<void>\n}): Promise<void> {\n const identity = async (directory: string) => normalizeDirectory(\n await params.resolveDirectoryIdentity?.(directory) ?? directory,\n )\n const target = await identity(params.targetDirectory)\n const matchesTarget = async (directory: string) => await identity(directory) === target\n const projects = await params.client.project.list()\n let project: (typeof projects)[number] | undefined\n for (const candidate of projects) {\n if (normalizeDirectory(candidate.canonical) === normalizeDirectory(params.projectDirectory)\n || (await Promise.all(candidate.sandboxes.map(matchesTarget))).some(Boolean)) {\n project = candidate\n break\n }\n }\n if (!project) throw new Error(\"Unable to resolve the OpenCode project before deleting worktree\")\n const sessions = await inventorySessions(params.client, project.id)\n const affected = (await Promise.all(sessions.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n const assertInactive = async (candidates = affected) => {\n const active = await params.client.session.active()\n const blockers = candidates.filter((session) => Object.prototype.hasOwnProperty.call(active, session.id))\n if (blockers.length) throw new Error(`Active sessions block worktree deletion: ${blockers.map((session) => session.id).join(\", \")}`)\n }\n await assertInactive()\n\n const moved: SessionInfo[] = []\n try {\n for (const session of affected) {\n await assertInactive()\n const original = { ...session, location: { ...session.location } }\n await params.client.session.move({ sessionID: session.id, directory: params.rootDirectory })\n moved.push(original)\n }\n await waitForInventory(params.client, project.id, async (current) => (\n !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)\n ))\n const finalInventory = await inventorySessions(params.client, project.id)\n const finalAffected = (await Promise.all(finalInventory.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n await assertInactive(finalAffected)\n if (finalAffected.length) throw new Error(\"Sessions appeared in the worktree during deletion\")\n await params.remove()\n } catch (error) {\n const rollbackErrors: unknown[] = []\n for (const session of moved.reverse()) {\n try {\n await params.client.session.move({\n sessionID: session.id,\n directory: session.location.directory,\n workspaceID: session.location.workspaceID,\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n }\n try {\n const expected = new Set(moved.map((session) => session.id))\n await waitForInventory(params.client, project.id, async (current) => {\n const restored = current.filter((session) => expected.has(session.id))\n return restored.length === expected.size\n && (await Promise.all(restored.map((session) => matchesTarget(session.location.directory)))).every(Boolean)\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n if (rollbackErrors.length) {\n throw new AggregateError([error, ...rollbackErrors], \"Session evacuation failed and could not be rolled back\")\n }\n throw error\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worktree-session-evacuation.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OpenCodeClient, SessionInfo } from \"@opencode/client\"\nimport { normalizeWslUncPath } from \"./worktree-directory\"\nimport { moveSessionToLocation, readLocationRef, sameLocation } from \"../opencode/compatibility/location\"\n\nconst PAGE_SIZE = 200\nconst MAX_PAGES = 10_000\nconst MAX_SESSIONS = 1_000_000\nconst MUTATION_DRAIN_TIMEOUT_MS = 30_000\n\nfunction normalizeDirectory(directory: string): string {\n const wsl = normalizeWslUncPath(directory)\n if (wsl) return wsl\n const normalized = directory.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \"/\"\n return /^[A-Za-z]:\\//.test(normalized) || normalized.startsWith(\"//\") ? normalized.toLowerCase() : normalized\n}\n\nexport class WorktreeDeletionFence {\n private readonly queues = new Map<string, Promise<unknown>>()\n private readonly blocked = new Map<string, number>()\n private readonly active = new Map<string, number>()\n private readonly idleWaiters = new Map<string, Set<() => void>>()\n\n constructor(private readonly mutationDrainTimeoutMs = MUTATION_DRAIN_TIMEOUT_MS) {}\n\n isBlocked(directory: string): boolean {\n return this.blocked.has(normalizeDirectory(directory))\n }\n\n enter(directories: string[]): (() => void) | undefined {\n const normalized = [...new Set(directories.map(normalizeDirectory))]\n if (normalized.some((directory) => this.blocked.has(directory))) return undefined\n for (const directory of normalized) this.active.set(directory, (this.active.get(directory) ?? 0) + 1)\n\n let released = false\n return () => {\n if (released) return\n released = true\n for (const directory of normalized) {\n const count = this.active.get(directory) ?? 0\n if (count > 1) {\n this.active.set(directory, count - 1)\n continue\n }\n this.active.delete(directory)\n for (const resolve of this.idleWaiters.get(directory) ?? []) resolve()\n this.idleWaiters.delete(directory)\n }\n }\n }\n\n run<T>(key: string, directories: string[], operation: () => Promise<T>): Promise<T> {\n const normalizedKey = normalizeDirectory(key)\n const blocked = [...new Set(directories.map(normalizeDirectory))]\n for (const directory of blocked) this.blocked.set(directory, (this.blocked.get(directory) ?? 0) + 1)\n\n const previous = this.queues.get(normalizedKey) ?? Promise.resolve()\n const current = previous.catch(() => {}).then(async () => {\n await Promise.all(blocked.map((directory) => this.waitForIdle(directory)))\n return operation()\n })\n this.queues.set(normalizedKey, current)\n return current.finally(() => {\n for (const directory of blocked) {\n const count = this.blocked.get(directory) ?? 0\n if (count > 1) this.blocked.set(directory, count - 1)\n else this.blocked.delete(directory)\n }\n if (this.queues.get(normalizedKey) === current) this.queues.delete(normalizedKey)\n })\n }\n\n private waitForIdle(directory: string): Promise<void> {\n if (!this.active.has(directory)) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const waiters = this.idleWaiters.get(directory) ?? new Set()\n const done = () => {\n clearTimeout(timer)\n resolve()\n }\n waiters.add(done)\n this.idleWaiters.set(directory, waiters)\n const timer = setTimeout(() => {\n waiters.delete(done)\n if (!waiters.size) this.idleWaiters.delete(directory)\n reject(new Error(\"Timed out waiting for worktree mutations to finish\"))\n }, this.mutationDrainTimeoutMs)\n })\n }\n}\n\nasync function inventorySessions(client: OpenCodeClient, project: string): Promise<SessionInfo[]> {\n const sessions = new Map<string, SessionInfo>()\n const cursors = new Set<string>()\n let cursor: string | undefined\n\n for (let pageCount = 0; pageCount < MAX_PAGES; pageCount += 1) {\n const page = await client.session.list(cursor ? { cursor } : { project, limit: PAGE_SIZE, order: \"asc\" })\n for (const session of page.data) {\n sessions.set(session.id, session)\n if (sessions.size > MAX_SESSIONS) throw new Error(\"Session inventory exceeded its safety limit\")\n }\n\n cursor = page.cursor.next ?? undefined\n if (!cursor) return Array.from(sessions.values())\n if (cursors.has(cursor)) throw new Error(`Repeated session inventory cursor: ${cursor}`)\n cursors.add(cursor)\n }\n\n throw new Error(\"Session inventory exceeded its page limit\")\n}\n\nasync function waitForInventory(\n client: OpenCodeClient,\n project: string,\n predicate: (sessions: SessionInfo[]) => boolean | Promise<boolean>,\n): Promise<void> {\n for (let attempt = 0; attempt < 100; attempt += 1) {\n if (await predicate(await inventorySessions(client, project))) return\n await new Promise((resolve) => setTimeout(resolve, 50))\n }\n throw new Error(\"Timed out waiting for session moves\")\n}\n\nexport async function evacuateWorktreeSessions(params: {\n client: OpenCodeClient\n projectDirectory: string\n targetDirectory: string\n rootDirectory: string\n resolveDirectoryIdentity?: (directory: string) => Promise<string | undefined>\n resolveExactDirectory?: (directory: string) => Promise<string | undefined>\n remove: () => Promise<void>\n}): Promise<void> {\n const identity = async (directory: string) => normalizeDirectory(\n await params.resolveDirectoryIdentity?.(directory) ?? directory,\n )\n // Containing-worktree identity intentionally groups descendant sessions. It\n // cannot identify a project root or destination: nested repositories differ.\n const exactDirectory = async (directory: string) => {\n const resolved = params.resolveExactDirectory ? await params.resolveExactDirectory(directory) : directory\n return resolved === undefined ? undefined : normalizeDirectory(resolved)\n }\n const [projectDirectory, targetDirectory, rootDirectory] = await Promise.all([\n exactDirectory(params.projectDirectory), exactDirectory(params.targetDirectory), exactDirectory(params.rootDirectory),\n ])\n if (projectDirectory === undefined || targetDirectory === undefined || rootDirectory === undefined) {\n throw new Error(\"Unable to resolve owned directories before deleting worktree\")\n }\n const target = await identity(params.targetDirectory)\n const matchesTarget = async (directory: string) => await identity(directory) === target\n const projects = await params.client.project.list()\n let project: (typeof projects)[number] | undefined\n for (const candidate of projects) {\n // Resolve aliases without collapsing a nested repository or nested sandbox\n // into its containing worktree. Both comparisons require exact directories.\n if (await exactDirectory(candidate.canonical) === projectDirectory\n || (await Promise.all(candidate.sandboxes.map(exactDirectory))).includes(targetDirectory)) {\n project = candidate\n break\n }\n }\n if (!project) throw new Error(\"Unable to resolve the OpenCode project before deleting worktree\")\n const sessions = await inventorySessions(params.client, project.id)\n const affected = (await Promise.all(sessions.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n const assertInactive = async (candidates = affected) => {\n const active = await params.client.session.active()\n const blockers = candidates.filter((session) => Object.prototype.hasOwnProperty.call(active, session.id))\n if (blockers.length) throw new Error(`Active sessions block worktree deletion: ${blockers.map((session) => session.id).join(\", \")}`)\n }\n await assertInactive()\n\n const moved: SessionInfo[] = []\n // Resolve before any move; a directory-only fallback could silently move a\n // legacy session into a different native workspace.\n const rootLocation = readLocationRef(await params.client.location.get({ location: { directory: params.rootDirectory } }))\n if (await exactDirectory(rootLocation.directory) !== rootDirectory) {\n throw new Error(\"OpenCode resolved a foreign evacuation destination\")\n }\n try {\n for (const session of affected) {\n await assertInactive()\n const original = { ...session, location: { ...session.location } }\n await moveSessionToLocation(params.client, session.id, rootLocation)\n moved.push(original)\n }\n await waitForInventory(params.client, project.id, async (current) => (\n moved.every((original) => {\n const session = current.find((candidate) => candidate.id === original.id)\n return session && sameLocation(readLocationRef(session.location), rootLocation)\n }) && !(await Promise.all(current.map((session) => matchesTarget(session.location.directory)))).some(Boolean)\n ))\n const finalInventory = await inventorySessions(params.client, project.id)\n const finalAffected = (await Promise.all(finalInventory.map(async (session) => (\n await matchesTarget(session.location.directory) ? session : undefined\n )))).filter((session): session is SessionInfo => Boolean(session))\n await assertInactive(finalAffected)\n if (finalAffected.length) throw new Error(\"Sessions appeared in the worktree during deletion\")\n await params.remove()\n } catch (error) {\n const rollbackErrors: unknown[] = []\n for (const session of moved.reverse()) {\n try {\n await moveSessionToLocation(params.client, session.id, readLocationRef(session.location))\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n }\n try {\n const expected = new Map(moved.map((session) => [session.id, readLocationRef(session.location)]))\n await waitForInventory(params.client, project.id, async (current) => {\n const restored = current.filter((session) => expected.has(session.id))\n return restored.length === expected.size\n && restored.every((session) => sameLocation(readLocationRef(session.location), expected.get(session.id)!))\n })\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError)\n }\n if (rollbackErrors.length) {\n throw new AggregateError([error, ...rollbackErrors], \"Session evacuation failed and could not be rolled back\")\n }\n throw error\n }\n}\n"]}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
+
import { mkdir, mkdtemp, realpath, rm, symlink } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
3
6
|
import { evacuateWorktreeSessions, WorktreeDeletionFence } from "./worktree-session-evacuation";
|
|
7
|
+
import { readLocationContext } from "../opencode/compatibility/location";
|
|
4
8
|
function session(id, directory, parentID) {
|
|
5
9
|
return { id, parentID, projectID: "project", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } };
|
|
6
10
|
}
|
|
11
|
+
const location = { get: async ({ location }) => location };
|
|
7
12
|
describe("evacuateWorktreeSessions", () => {
|
|
8
13
|
it("serializes deletion attempts for the same worktree", async () => {
|
|
9
14
|
const fence = new WorktreeDeletionFence();
|
|
@@ -55,6 +60,7 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
55
60
|
const state = new Map([root, child, grandchild].map((item) => [item.id, item]));
|
|
56
61
|
let removed = false;
|
|
57
62
|
const client = {
|
|
63
|
+
location,
|
|
58
64
|
project: {
|
|
59
65
|
list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }],
|
|
60
66
|
},
|
|
@@ -91,6 +97,7 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
91
97
|
let current = aliased;
|
|
92
98
|
let removed = false;
|
|
93
99
|
const client = {
|
|
100
|
+
location,
|
|
94
101
|
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
|
|
95
102
|
session: {
|
|
96
103
|
list: async () => ({ data: [current], cursor: {} }),
|
|
@@ -109,10 +116,164 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
109
116
|
assert.equal(current.location.directory, "/repo");
|
|
110
117
|
assert.equal(removed, true);
|
|
111
118
|
});
|
|
119
|
+
it("resolves the project canonical path without collapsing containing-worktree identity", async () => {
|
|
120
|
+
const shortRoot = "C:\\Users\\RUNNER~1\\Temp\\repo";
|
|
121
|
+
const longRoot = "C:/Users/runneradmin/Temp/repo";
|
|
122
|
+
const target = `${shortRoot}\\worktree`;
|
|
123
|
+
const original = { directory: target, workspaceID: "native-original" };
|
|
124
|
+
const current = { ...session("session", target), location: original };
|
|
125
|
+
const moves = [];
|
|
126
|
+
const client = {
|
|
127
|
+
location: { get: async () => ({ directory: longRoot }) },
|
|
128
|
+
project: { list: async () => [
|
|
129
|
+
{ id: "foreign", canonical: "C:/Users/runneradmin/Temp/repo-other", sandboxes: [] },
|
|
130
|
+
{ id: "project", canonical: longRoot, sandboxes: [] },
|
|
131
|
+
] },
|
|
132
|
+
session: {
|
|
133
|
+
list: async ({ project }) => {
|
|
134
|
+
assert.equal(project, "project");
|
|
135
|
+
return { data: [current], cursor: {} };
|
|
136
|
+
},
|
|
137
|
+
active: async () => ({}),
|
|
138
|
+
move: async ({ directory }, options) => {
|
|
139
|
+
const location = readLocationContext(options?.headers["x-codenomad-location"], "legacy") ?? { directory };
|
|
140
|
+
moves.push(location);
|
|
141
|
+
current.location = location;
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
await assert.rejects(evacuateWorktreeSessions({
|
|
146
|
+
client, projectDirectory: shortRoot, targetDirectory: target, rootDirectory: shortRoot,
|
|
147
|
+
resolveDirectoryIdentity: async (directory) => directory === shortRoot || directory === longRoot
|
|
148
|
+
? "owned:root" : directory === target ? "owned:worktree" : undefined,
|
|
149
|
+
resolveExactDirectory: async (directory) => directory === shortRoot || directory === longRoot
|
|
150
|
+
? longRoot : directory === target ? target : undefined,
|
|
151
|
+
remove: async () => {
|
|
152
|
+
assert.deepEqual(current.location, { directory: longRoot });
|
|
153
|
+
throw new Error("Synthetic Git removal refusal");
|
|
154
|
+
},
|
|
155
|
+
}), /Synthetic Git removal refusal/);
|
|
156
|
+
assert.deepEqual(moves, [{ directory: longRoot }, original]);
|
|
157
|
+
assert.deepEqual(current.location, original);
|
|
158
|
+
});
|
|
159
|
+
it("inventories the exact project when nested projects and sandboxes share its worktree identity", async () => {
|
|
160
|
+
const inventories = [];
|
|
161
|
+
let removed = false;
|
|
162
|
+
const client = {
|
|
163
|
+
location,
|
|
164
|
+
project: { list: async () => [
|
|
165
|
+
{ id: "nested", canonical: "/repo/nested", sandboxes: [] },
|
|
166
|
+
{ id: "nested-sandbox", canonical: "/other", sandboxes: ["/repo/worktree/nested"] },
|
|
167
|
+
{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"] },
|
|
168
|
+
] },
|
|
169
|
+
session: {
|
|
170
|
+
list: async ({ project }) => {
|
|
171
|
+
inventories.push(project);
|
|
172
|
+
return { data: project === "project" ? [session("active", "/repo/worktree/child")] : [], cursor: {} };
|
|
173
|
+
},
|
|
174
|
+
active: async () => ({ active: { type: "running" } }),
|
|
175
|
+
move: async () => { assert.fail("Active sessions must not move"); },
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
await assert.rejects(evacuateWorktreeSessions({
|
|
179
|
+
client, projectDirectory: "/repo", targetDirectory: "/repo/worktree", rootDirectory: "/repo",
|
|
180
|
+
resolveDirectoryIdentity: async (directory) => directory.startsWith("/repo/worktree") ? "/repo/worktree" : "/repo",
|
|
181
|
+
resolveExactDirectory: async (directory) => directory.startsWith("/repo") ? directory : undefined,
|
|
182
|
+
remove: async () => { removed = true; },
|
|
183
|
+
}), /Active sessions block worktree deletion: active/);
|
|
184
|
+
assert.deepEqual(inventories, ["project"]);
|
|
185
|
+
assert.equal(removed, false);
|
|
186
|
+
});
|
|
187
|
+
it("keeps WSL directory translation exact and refuses an owned nested evacuation destination", async () => {
|
|
188
|
+
const hostRoot = "\\\\wsl.localhost\\Ubuntu\\home\\fixture\\repo";
|
|
189
|
+
const hostTarget = `${hostRoot}\\worktree`;
|
|
190
|
+
const translate = async (directory) => directory === hostRoot ? "/home/fixture/repo"
|
|
191
|
+
: directory === hostTarget ? "/home/fixture/repo/worktree" : directory;
|
|
192
|
+
let destination = "/home/fixture/repo/nested";
|
|
193
|
+
let removed = false;
|
|
194
|
+
let moved = false;
|
|
195
|
+
const client = {
|
|
196
|
+
location: { get: async () => ({ directory: destination }) },
|
|
197
|
+
project: { list: async () => [{ id: "project", canonical: "/home/fixture/repo", sandboxes: [] }] },
|
|
198
|
+
session: {
|
|
199
|
+
list: async () => ({ data: [], cursor: {} }),
|
|
200
|
+
active: async () => ({}),
|
|
201
|
+
move: async () => { moved = true; },
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
const params = {
|
|
205
|
+
client, projectDirectory: hostRoot, targetDirectory: hostTarget, rootDirectory: hostRoot,
|
|
206
|
+
resolveDirectoryIdentity: async (directory) => (await translate(directory)).includes("/worktree") ? "owned:worktree" : "owned:root",
|
|
207
|
+
resolveExactDirectory: translate,
|
|
208
|
+
remove: async () => { removed = true; },
|
|
209
|
+
};
|
|
210
|
+
await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/);
|
|
211
|
+
assert.equal(removed, false);
|
|
212
|
+
assert.equal(moved, false);
|
|
213
|
+
destination = "/home/fixture/repo";
|
|
214
|
+
await evacuateWorktreeSessions(params);
|
|
215
|
+
assert.equal(removed, true);
|
|
216
|
+
});
|
|
217
|
+
it("fails closed when an exact directory cannot be resolved", async () => {
|
|
218
|
+
await assert.rejects(evacuateWorktreeSessions({
|
|
219
|
+
client: {}, projectDirectory: "/repo", targetDirectory: "/repo/worktree", rootDirectory: "/repo",
|
|
220
|
+
resolveDirectoryIdentity: async () => "owned:root",
|
|
221
|
+
resolveExactDirectory: async () => undefined,
|
|
222
|
+
remove: async () => { assert.fail("Unresolved directories must not be deleted"); },
|
|
223
|
+
}), /Unable to resolve owned directories/);
|
|
224
|
+
});
|
|
225
|
+
it("resolves a real junction/symlink project alias without admitting a foreign destination", async () => {
|
|
226
|
+
const temp = path.join(os.tmpdir(), "opencode");
|
|
227
|
+
await mkdir(temp, { recursive: true });
|
|
228
|
+
const root = await mkdtemp(path.join(temp, "evacuation-alias-"));
|
|
229
|
+
try {
|
|
230
|
+
const canonical = path.join(root, "repository");
|
|
231
|
+
const alias = path.join(root, "alias");
|
|
232
|
+
const target = path.join(root, "worktree");
|
|
233
|
+
const foreign = path.join(root, "foreign");
|
|
234
|
+
await Promise.all([canonical, target, foreign].map(directory => mkdir(directory)));
|
|
235
|
+
await symlink(canonical, alias, process.platform === "win32" ? "junction" : "dir");
|
|
236
|
+
let destination = await realpath(canonical);
|
|
237
|
+
let current = session("session", target);
|
|
238
|
+
const moves = [];
|
|
239
|
+
const client = {
|
|
240
|
+
location: { get: async () => ({ directory: destination }) },
|
|
241
|
+
project: { list: async () => [{ id: "project", canonical: destination, sandboxes: [] }] },
|
|
242
|
+
session: {
|
|
243
|
+
list: async () => ({ data: [current], cursor: {} }),
|
|
244
|
+
active: async () => ({}),
|
|
245
|
+
move: async ({ directory }) => {
|
|
246
|
+
moves.push(directory);
|
|
247
|
+
current = { ...current, location: { directory } };
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
const params = {
|
|
252
|
+
client, projectDirectory: alias, targetDirectory: target, rootDirectory: alias,
|
|
253
|
+
resolveDirectoryIdentity: (directory) => realpath(directory),
|
|
254
|
+
resolveExactDirectory: (directory) => realpath(directory),
|
|
255
|
+
remove: async () => { throw new Error("Synthetic Git removal refusal"); },
|
|
256
|
+
};
|
|
257
|
+
await assert.rejects(evacuateWorktreeSessions(params), /Synthetic Git removal refusal/);
|
|
258
|
+
assert.deepEqual(moves, [destination, target]);
|
|
259
|
+
assert.equal(current.location.directory, target);
|
|
260
|
+
moves.length = 0;
|
|
261
|
+
destination = foreign;
|
|
262
|
+
await assert.rejects(evacuateWorktreeSessions(params), /Unable to resolve the OpenCode project/);
|
|
263
|
+
assert.deepEqual(moves, []);
|
|
264
|
+
client.project.list = async () => [{ id: "project", canonical, sandboxes: [] }];
|
|
265
|
+
await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/);
|
|
266
|
+
assert.deepEqual(moves, []);
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
await rm(root, { recursive: true, force: true });
|
|
270
|
+
}
|
|
271
|
+
});
|
|
112
272
|
it("evacuates sessions nested under the target worktree identity", async () => {
|
|
113
273
|
let current = session("nested", "/repo/worktree/nested");
|
|
114
274
|
let removed = false;
|
|
115
275
|
const client = {
|
|
276
|
+
location,
|
|
116
277
|
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
|
|
117
278
|
session: {
|
|
118
279
|
list: async () => ({ data: [current], cursor: {} }),
|
|
@@ -135,6 +296,7 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
135
296
|
const current = session("session", "/repo/worktree");
|
|
136
297
|
const moves = [];
|
|
137
298
|
const client = {
|
|
299
|
+
location,
|
|
138
300
|
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
|
|
139
301
|
session: {
|
|
140
302
|
list: async () => ({ data: [current], cursor: {} }),
|
|
@@ -158,6 +320,7 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
158
320
|
let listCalls = 0;
|
|
159
321
|
let removed = false;
|
|
160
322
|
const client = {
|
|
323
|
+
location,
|
|
161
324
|
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: ["/repo/worktree"], time: { created: 1, updated: 1 } }] },
|
|
162
325
|
session: {
|
|
163
326
|
list: async () => {
|
|
@@ -175,5 +338,53 @@ describe("evacuateWorktreeSessions", () => {
|
|
|
175
338
|
assert.equal(removed, false);
|
|
176
339
|
assert.equal(current.location.directory, "/repo/worktree");
|
|
177
340
|
});
|
|
341
|
+
it("uses the resolved destination and restores exact legacy identity on rollback", async () => {
|
|
342
|
+
const original = { directory: "/repo/worktree", workspaceID: "native-original" };
|
|
343
|
+
const destination = { directory: "/repo", workspaceID: "native-root" };
|
|
344
|
+
const current = { ...session("session", original.directory), location: original };
|
|
345
|
+
const moves = [];
|
|
346
|
+
const client = {
|
|
347
|
+
location: { get: async () => destination },
|
|
348
|
+
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: [original.directory] }] },
|
|
349
|
+
session: {
|
|
350
|
+
list: async () => ({ data: [current], cursor: {} }),
|
|
351
|
+
active: async () => ({}),
|
|
352
|
+
move: async ({ directory }, options) => {
|
|
353
|
+
const resolved = readLocationContext(options?.headers["x-codenomad-location"], "legacy");
|
|
354
|
+
assert.equal(resolved.directory, directory);
|
|
355
|
+
moves.push(resolved);
|
|
356
|
+
current.location = { directory: resolved.directory, workspaceID: resolved.workspaceID };
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
await assert.rejects(evacuateWorktreeSessions({
|
|
361
|
+
client, projectDirectory: "/repo", targetDirectory: original.directory, rootDirectory: destination.directory,
|
|
362
|
+
remove: async () => { throw new Error("Git removal failed"); },
|
|
363
|
+
}), /Git removal failed/);
|
|
364
|
+
assert.deepEqual(moves, [destination, original]);
|
|
365
|
+
assert.deepEqual(current.location, original);
|
|
366
|
+
});
|
|
367
|
+
it("does not report rollback success when only the directory was restored", async () => {
|
|
368
|
+
const original = { directory: "/repo/worktree", workspaceID: "native-original" };
|
|
369
|
+
const current = { ...session("session", original.directory), location: original };
|
|
370
|
+
let moves = 0;
|
|
371
|
+
const client = {
|
|
372
|
+
location,
|
|
373
|
+
project: { list: async () => [{ id: "project", canonical: "/repo", sandboxes: [original.directory] }] },
|
|
374
|
+
session: {
|
|
375
|
+
list: async () => ({ data: [current], cursor: {} }),
|
|
376
|
+
active: async () => ({}),
|
|
377
|
+
move: async ({ directory }) => {
|
|
378
|
+
moves++;
|
|
379
|
+
current.location = { directory, workspaceID: undefined };
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
await assert.rejects(evacuateWorktreeSessions({
|
|
384
|
+
client, projectDirectory: "/repo", targetDirectory: original.directory, rootDirectory: "/repo",
|
|
385
|
+
remove: async () => { throw new Error("Git removal failed"); },
|
|
386
|
+
}), /could not be rolled back/);
|
|
387
|
+
assert.equal(moves, 2);
|
|
388
|
+
});
|
|
178
389
|
});
|
|
179
390
|
//# sourceMappingURL=worktree-session-evacuation.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktree-session-evacuation.test.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { describe, it } from \"node:test\"\nimport type { OpenCodeClient, SessionInfo } from \"@opencode-ai/client\"\nimport { evacuateWorktreeSessions, WorktreeDeletionFence } from \"./worktree-session-evacuation\"\n\nfunction session(id: string, directory: string, parentID?: string): SessionInfo {\n return { id, parentID, projectID: \"project\", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } } as SessionInfo\n}\n\ndescribe(\"evacuateWorktreeSessions\", () => {\n it(\"serializes deletion attempts for the same worktree\", async () => {\n const fence = new WorktreeDeletionFence()\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const calls: string[] = []\n const first = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {\n calls.push(\"first:start\")\n await gate\n calls.push(\"first:end\")\n })\n const second = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"second\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [\"first:start\"])\n assert.equal(fence.isBlocked(\"/repo/worktree/\"), true)\n release()\n await Promise.all([first, second])\n assert.deepEqual(calls, [\"first:start\", \"first:end\", \"second\"])\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n })\n\n it(\"waits for admitted mutations before deleting and rejects later admission\", async () => {\n const fence = new WorktreeDeletionFence()\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n const calls: string[] = []\n const deletion = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"delete\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [])\n assert.equal(fence.enter([\"/repo/worktree\"]), undefined)\n releaseMutation()\n await deletion\n assert.deepEqual(calls, [\"delete\"])\n })\n\n it(\"fails deletion closed when an admitted mutation does not finish\", async () => {\n const fence = new WorktreeDeletionFence(1)\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n\n await assert.rejects(\n fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {}),\n /Timed out waiting for worktree mutations/,\n )\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n releaseMutation()\n })\n\n it(\"finds later-page sessions and waits for their asynchronous moves\", async () => {\n const moves: Array<{ sessionID: string; directory: string }> = []\n const lists: unknown[] = []\n let listCall = 0\n const root = session(\"old-root\", \"/repo/worktree\")\n const child = session(\"old-child\", \"/repo/worktree\", root.id)\n const grandchild = session(\"old-grandchild\", \"/repo/worktree\", child.id)\n const state = new Map([root, child, grandchild].map((item) => [item.id, item]))\n let removed = false\n const client = {\n project: {\n list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }],\n },\n session: {\n list: async (input: unknown) => {\n lists.push(input)\n listCall += 1\n if (listCall === 1) return { data: [session(\"loaded\", \"/repo\")], cursor: { next: \"older\" } }\n if (listCall === 2) return { data: [root, child, grandchild], cursor: {} }\n return { data: [session(\"loaded\", \"/repo\"), ...state.values()], cursor: {} }\n },\n active: async () => ({}),\n move: async (input: { sessionID: string; directory: string }) => {\n moves.push(input)\n setImmediate(() => state.set(input.sessionID, { ...state.get(input.sessionID)!, location: { directory: input.directory } }))\n },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n })\n\n assert.deepEqual(moves.map(({ sessionID }) => sessionID), [root.id, child.id, grandchild.id])\n assert.equal(removed, true)\n assert.ok(listCall > 3)\n assert.ok(lists.every((input: any) => input.cursor\n ? Object.keys(input).length === 1\n : input.project === \"project\" && input.directory === undefined))\n })\n\n it(\"evacuates sessions whose directory resolves to the target alias\", async () => {\n const aliased = session(\"aliased\", \"/repo/alias\")\n let current = aliased\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory === \"/repo/alias\" ? \"/repo/worktree\" : directory,\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"evacuates sessions nested under the target worktree identity\", async () => {\n let current = session(\"nested\", \"/repo/worktree/nested\")\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory.startsWith(\"/repo/worktree\") ? \"workspace:worktree\" : \"workspace:root\",\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"rolls sessions back when Git removal fails\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const moves: string[] = []\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current.location = { directory }\n },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [\"/repo\", \"/repo/worktree\"])\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"re-inventories active sessions immediately before removal\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const intruder = session(\"intruder\", \"/repo/worktree\")\n let listCalls = 0\n let removed = false\n const client = {\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => {\n listCalls += 1\n return { data: listCalls >= 3 ? [current, intruder] : [current], cursor: {} }\n },\n active: async () => ({ intruder: { type: \"running\" } }),\n move: async ({ directory }: { directory: string }) => { current.location = { directory } },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: intruder/)\n\n assert.equal(removed, false)\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"worktree-session-evacuation.test.js","sourceRoot":"","sources":["../../src/workspaces/worktree-session-evacuation.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { describe, it } from \"node:test\"\nimport { mkdir, mkdtemp, realpath, rm, symlink } from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport type { OpenCodeClient, SessionInfo } from \"@opencode/client\"\nimport { evacuateWorktreeSessions, WorktreeDeletionFence } from \"./worktree-session-evacuation\"\nimport { readLocationContext } from \"../opencode/compatibility/location\"\n\nfunction session(id: string, directory: string, parentID?: string): SessionInfo {\n return { id, parentID, projectID: \"project\", location: { directory }, cost: 0, tokens: {}, time: { created: 1, updated: 1 } } as SessionInfo\n}\n\nconst location = { get: async ({ location }: { location: { directory: string } }) => location }\n\ndescribe(\"evacuateWorktreeSessions\", () => {\n it(\"serializes deletion attempts for the same worktree\", async () => {\n const fence = new WorktreeDeletionFence()\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const calls: string[] = []\n const first = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {\n calls.push(\"first:start\")\n await gate\n calls.push(\"first:end\")\n })\n const second = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"second\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [\"first:start\"])\n assert.equal(fence.isBlocked(\"/repo/worktree/\"), true)\n release()\n await Promise.all([first, second])\n assert.deepEqual(calls, [\"first:start\", \"first:end\", \"second\"])\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n })\n\n it(\"waits for admitted mutations before deleting and rejects later admission\", async () => {\n const fence = new WorktreeDeletionFence()\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n const calls: string[] = []\n const deletion = fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => { calls.push(\"delete\") })\n\n await new Promise((resolve) => setImmediate(resolve))\n assert.deepEqual(calls, [])\n assert.equal(fence.enter([\"/repo/worktree\"]), undefined)\n releaseMutation()\n await deletion\n assert.deepEqual(calls, [\"delete\"])\n })\n\n it(\"fails deletion closed when an admitted mutation does not finish\", async () => {\n const fence = new WorktreeDeletionFence(1)\n const releaseMutation = fence.enter([\"/repo/worktree\"])\n assert.ok(releaseMutation)\n\n await assert.rejects(\n fence.run(\"/repo/worktree\", [\"/repo/worktree\"], async () => {}),\n /Timed out waiting for worktree mutations/,\n )\n assert.equal(fence.isBlocked(\"/repo/worktree\"), false)\n releaseMutation()\n })\n\n it(\"finds later-page sessions and waits for their asynchronous moves\", async () => {\n const moves: Array<{ sessionID: string; directory: string }> = []\n const lists: unknown[] = []\n let listCall = 0\n const root = session(\"old-root\", \"/repo/worktree\")\n const child = session(\"old-child\", \"/repo/worktree\", root.id)\n const grandchild = session(\"old-grandchild\", \"/repo/worktree\", child.id)\n const state = new Map([root, child, grandchild].map((item) => [item.id, item]))\n let removed = false\n const client = {\n location,\n project: {\n list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }],\n },\n session: {\n list: async (input: unknown) => {\n lists.push(input)\n listCall += 1\n if (listCall === 1) return { data: [session(\"loaded\", \"/repo\")], cursor: { next: \"older\" } }\n if (listCall === 2) return { data: [root, child, grandchild], cursor: {} }\n return { data: [session(\"loaded\", \"/repo\"), ...state.values()], cursor: {} }\n },\n active: async () => ({}),\n move: async (input: { sessionID: string; directory: string }) => {\n moves.push(input)\n setImmediate(() => state.set(input.sessionID, { ...state.get(input.sessionID)!, location: { directory: input.directory } }))\n },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n })\n\n assert.deepEqual(moves.map(({ sessionID }) => sessionID), [root.id, child.id, grandchild.id])\n assert.equal(removed, true)\n assert.ok(listCall > 3)\n assert.ok(lists.every((input: any) => input.cursor\n ? Object.keys(input).length === 1\n : input.project === \"project\" && input.directory === undefined))\n })\n\n it(\"evacuates sessions whose directory resolves to the target alias\", async () => {\n const aliased = session(\"aliased\", \"/repo/alias\")\n let current = aliased\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory === \"/repo/alias\" ? \"/repo/worktree\" : directory,\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"resolves the project canonical path without collapsing containing-worktree identity\", async () => {\n const shortRoot = \"C:\\\\Users\\\\RUNNER~1\\\\Temp\\\\repo\"\n const longRoot = \"C:/Users/runneradmin/Temp/repo\"\n const target = `${shortRoot}\\\\worktree`\n const original = { directory: target, workspaceID: \"native-original\" }\n const current = { ...session(\"session\", target), location: original }\n const moves: unknown[] = []\n const client = {\n location: { get: async () => ({ directory: longRoot }) },\n project: { list: async () => [\n { id: \"foreign\", canonical: \"C:/Users/runneradmin/Temp/repo-other\", sandboxes: [] },\n { id: \"project\", canonical: longRoot, sandboxes: [] },\n ] },\n session: {\n list: async ({ project }: { project: string }) => {\n assert.equal(project, \"project\")\n return { data: [current], cursor: {} }\n },\n active: async () => ({}),\n move: async ({ directory }: { directory: string }, options?: { headers: Record<string, string> }) => {\n const location = readLocationContext(options?.headers[\"x-codenomad-location\"], \"legacy\") ?? { directory }\n moves.push(location)\n current.location = location as typeof original\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: shortRoot, targetDirectory: target, rootDirectory: shortRoot,\n resolveDirectoryIdentity: async directory => directory === shortRoot || directory === longRoot\n ? \"owned:root\" : directory === target ? \"owned:worktree\" : undefined,\n resolveExactDirectory: async directory => directory === shortRoot || directory === longRoot\n ? longRoot : directory === target ? target : undefined,\n remove: async () => {\n assert.deepEqual(current.location, { directory: longRoot })\n throw new Error(\"Synthetic Git removal refusal\")\n },\n }), /Synthetic Git removal refusal/)\n assert.deepEqual(moves, [{ directory: longRoot }, original])\n assert.deepEqual(current.location, original)\n })\n\n it(\"inventories the exact project when nested projects and sandboxes share its worktree identity\", async () => {\n const inventories: string[] = []\n let removed = false\n const client = {\n location,\n project: { list: async () => [\n { id: \"nested\", canonical: \"/repo/nested\", sandboxes: [] },\n { id: \"nested-sandbox\", canonical: \"/other\", sandboxes: [\"/repo/worktree/nested\"] },\n { id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"] },\n ] },\n session: {\n list: async ({ project }: { project: string }) => {\n inventories.push(project)\n return { data: project === \"project\" ? [session(\"active\", \"/repo/worktree/child\")] : [], cursor: {} }\n },\n active: async () => ({ active: { type: \"running\" } }),\n move: async () => { assert.fail(\"Active sessions must not move\") },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async directory => directory.startsWith(\"/repo/worktree\") ? \"/repo/worktree\" : \"/repo\",\n resolveExactDirectory: async directory => directory.startsWith(\"/repo\") ? directory : undefined,\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: active/)\n assert.deepEqual(inventories, [\"project\"])\n assert.equal(removed, false)\n })\n\n it(\"keeps WSL directory translation exact and refuses an owned nested evacuation destination\", async () => {\n const hostRoot = \"\\\\\\\\wsl.localhost\\\\Ubuntu\\\\home\\\\fixture\\\\repo\"\n const hostTarget = `${hostRoot}\\\\worktree`\n const translate = async (directory: string) => directory === hostRoot ? \"/home/fixture/repo\"\n : directory === hostTarget ? \"/home/fixture/repo/worktree\" : directory\n let destination = \"/home/fixture/repo/nested\"\n let removed = false\n let moved = false\n const client = {\n location: { get: async () => ({ directory: destination }) },\n project: { list: async () => [{ id: \"project\", canonical: \"/home/fixture/repo\", sandboxes: [] }] },\n session: {\n list: async () => ({ data: [], cursor: {} }),\n active: async () => ({}),\n move: async () => { moved = true },\n },\n } as unknown as OpenCodeClient\n const params = {\n client, projectDirectory: hostRoot, targetDirectory: hostTarget, rootDirectory: hostRoot,\n resolveDirectoryIdentity: async (directory: string) => (await translate(directory)).includes(\"/worktree\") ? \"owned:worktree\" : \"owned:root\",\n resolveExactDirectory: translate,\n remove: async () => { removed = true },\n }\n await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/)\n assert.equal(removed, false)\n assert.equal(moved, false)\n destination = \"/home/fixture/repo\"\n await evacuateWorktreeSessions(params)\n assert.equal(removed, true)\n })\n\n it(\"fails closed when an exact directory cannot be resolved\", async () => {\n await assert.rejects(evacuateWorktreeSessions({\n client: {} as OpenCodeClient, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async () => \"owned:root\",\n resolveExactDirectory: async () => undefined,\n remove: async () => { assert.fail(\"Unresolved directories must not be deleted\") },\n }), /Unable to resolve owned directories/)\n })\n\n it(\"resolves a real junction/symlink project alias without admitting a foreign destination\", async () => {\n const temp = path.join(os.tmpdir(), \"opencode\")\n await mkdir(temp, { recursive: true })\n const root = await mkdtemp(path.join(temp, \"evacuation-alias-\"))\n try {\n const canonical = path.join(root, \"repository\")\n const alias = path.join(root, \"alias\")\n const target = path.join(root, \"worktree\")\n const foreign = path.join(root, \"foreign\")\n await Promise.all([canonical, target, foreign].map(directory => mkdir(directory)))\n await symlink(canonical, alias, process.platform === \"win32\" ? \"junction\" : \"dir\")\n let destination = await realpath(canonical)\n let current = session(\"session\", target)\n const moves: string[] = []\n const client = {\n location: { get: async () => ({ directory: destination }) },\n project: { list: async () => [{ id: \"project\", canonical: destination, sandboxes: [] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current = { ...current, location: { directory } }\n },\n },\n } as unknown as OpenCodeClient\n const params = {\n client, projectDirectory: alias, targetDirectory: target, rootDirectory: alias,\n resolveDirectoryIdentity: (directory: string) => realpath(directory),\n resolveExactDirectory: (directory: string) => realpath(directory),\n remove: async () => { throw new Error(\"Synthetic Git removal refusal\") },\n }\n await assert.rejects(evacuateWorktreeSessions(params), /Synthetic Git removal refusal/)\n assert.deepEqual(moves, [destination, target])\n assert.equal(current.location.directory, target)\n moves.length = 0\n destination = foreign\n await assert.rejects(evacuateWorktreeSessions(params), /Unable to resolve the OpenCode project/)\n assert.deepEqual(moves, [])\n client.project.list = async () => [{ id: \"project\", canonical, sandboxes: [] }] as any\n await assert.rejects(evacuateWorktreeSessions(params), /foreign evacuation destination/)\n assert.deepEqual(moves, [])\n } finally { await rm(root, { recursive: true, force: true }) }\n })\n\n it(\"evacuates sessions nested under the target worktree identity\", async () => {\n let current = session(\"nested\", \"/repo/worktree/nested\")\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async (input: { directory: string }) => { current = { ...current, location: { directory: input.directory } } },\n },\n } as unknown as OpenCodeClient\n\n await evacuateWorktreeSessions({\n client,\n projectDirectory: \"/repo\",\n targetDirectory: \"/repo/worktree\",\n rootDirectory: \"/repo\",\n resolveDirectoryIdentity: async (directory) => directory.startsWith(\"/repo/worktree\") ? \"workspace:worktree\" : \"workspace:root\",\n remove: async () => { removed = true },\n })\n\n assert.equal(current.location.directory, \"/repo\")\n assert.equal(removed, true)\n })\n\n it(\"rolls sessions back when Git removal fails\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const moves: string[] = []\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves.push(directory)\n current.location = { directory }\n },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [\"/repo\", \"/repo/worktree\"])\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"re-inventories active sessions immediately before removal\", async () => {\n const current = session(\"session\", \"/repo/worktree\")\n const intruder = session(\"intruder\", \"/repo/worktree\")\n let listCalls = 0\n let removed = false\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [\"/repo/worktree\"], time: { created: 1, updated: 1 } }] },\n session: {\n list: async () => {\n listCalls += 1\n return { data: listCalls >= 3 ? [current, intruder] : [current], cursor: {} }\n },\n active: async () => ({ intruder: { type: \"running\" } }),\n move: async ({ directory }: { directory: string }) => { current.location = { directory } },\n },\n } as unknown as OpenCodeClient\n\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: \"/repo/worktree\", rootDirectory: \"/repo\",\n remove: async () => { removed = true },\n }), /Active sessions block worktree deletion: intruder/)\n\n assert.equal(removed, false)\n assert.equal(current.location.directory, \"/repo/worktree\")\n })\n\n it(\"uses the resolved destination and restores exact legacy identity on rollback\", async () => {\n const original = { directory: \"/repo/worktree\", workspaceID: \"native-original\" }\n const destination = { directory: \"/repo\", workspaceID: \"native-root\" }\n const current = { ...session(\"session\", original.directory), location: original }\n const moves: unknown[] = []\n const client = {\n location: { get: async () => destination },\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [original.directory] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }, options?: { headers: Record<string, string> }) => {\n const resolved = readLocationContext(options?.headers[\"x-codenomad-location\"], \"legacy\")!\n assert.equal(resolved.directory, directory)\n moves.push(resolved)\n current.location = { directory: resolved.directory, workspaceID: resolved.workspaceID! }\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: original.directory, rootDirectory: destination.directory,\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /Git removal failed/)\n assert.deepEqual(moves, [destination, original])\n assert.deepEqual(current.location, original)\n })\n\n it(\"does not report rollback success when only the directory was restored\", async () => {\n const original = { directory: \"/repo/worktree\", workspaceID: \"native-original\" }\n const current = { ...session(\"session\", original.directory), location: original }\n let moves = 0\n const client = {\n location,\n project: { list: async () => [{ id: \"project\", canonical: \"/repo\", sandboxes: [original.directory] }] },\n session: {\n list: async () => ({ data: [current], cursor: {} }),\n active: async () => ({}),\n move: async ({ directory }: { directory: string }) => {\n moves++\n current.location = { directory, workspaceID: undefined! }\n },\n },\n } as unknown as OpenCodeClient\n await assert.rejects(evacuateWorktreeSessions({\n client, projectDirectory: \"/repo\", targetDirectory: original.directory, rootDirectory: \"/repo\",\n remove: async () => { throw new Error(\"Git removal failed\") },\n }), /could not be rolled back/)\n assert.equal(moves, 2)\n })\n})\n"]}
|