fix(files): a workspace whose folder was deleted leaves the Files places

The lane payload carries folder_missing (ENOENT/ENOTDIR only). Files hides
such a workspace and never reopens it as the default place; the Workspace
sidebar strikes its name through with a hint to remove it from its menu.
A folder that cannot be read no longer keeps the previous folder's entries
under its error, and a change in Files re-reads the workspace list.
This commit is contained in:
2026-11-02 10:11:47 +07:00
parent c3d88c0685
commit 220309db12
10 changed files with 92 additions and 11 deletions
+10
View File
@@ -337,6 +337,16 @@ describe("lanes lib", () => {
);
});
it("payload says folder_missing only when the folder is gone", () => {
const fs = require("node:fs");
const dir = fs.mkdtempSync(pathMod.join(os.tmpdir(), "lane-gone-"));
const l = lanes.createLane({ cwd: dir });
assert.equal(lanes.lanePayload(lanes.getLane(l.id)).folder_missing, false);
fs.rmSync(dir, { recursive: true });
assert.equal(lanes.lanePayload(lanes.getLane(l.id)).folder_missing, true);
lanes.deleteLane(l.id);
});
it("payload carries node states and progress", () => {
const l = lanes.createLane({ cwd: "/tmp/wt/d" });
lanes.setStage(l.id, { stage: "review" });
+15
View File
@@ -9,6 +9,7 @@
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const fs = require("node:fs");
const path = require("node:path");
const { db } = require("../db");
const aiTools = require("./ai-tools");
@@ -749,6 +750,17 @@ function ownerNames(id) {
};
}
/** True only when the folder is GONE (ENOENT/ENOTDIR) — a folder this
* process may not read is still there, so it never counts as deleted. */
function folderMissing(cwd) {
try {
fs.statSync(cwd);
return false;
} catch (err) {
return err.code === "ENOENT" || err.code === "ENOTDIR";
}
}
function lanePayload(lane, ageSec = null) {
const pipeline = getPipeline(lane.pipeline);
const since = lane.stage_since ? Date.parse(lane.stage_since) : NaN;
@@ -775,6 +787,9 @@ function lanePayload(lane, ageSec = null) {
: Math.max(0, Math.round((Date.now() - since) / 1000)),
last_event_seconds: ageSec,
liveness: classifyLiveness({ status: lane.status, stage: lane.stage, ageSec }, DEAD_SEC),
// The folder was deleted under the workspace (from Files, a shell): the
// record stays — its sessions are history — but nothing can open in it.
folder_missing: folderMissing(lane.cwd),
};
}