lib/daemon-project-access.js
Rama main · réplica de lectura del repositorio original.
// Persisted Clay-level access rules for parent projects and exact worktrees.
// Filesystem and Git permissions are deliberately outside this module.
var worktreePreferences = require("./worktree-preferences");
function parentSlugFor(slug) {
if (typeof slug !== "string") return null;
var marker = slug.indexOf("--");
return marker > 0 ? slug.substring(0, marker) : null;
}
function findProject(config, slug) {
var projects = config && Array.isArray(config.projects) ? config.projects : [];
for (var i = 0; i < projects.length; i++) {
if (projects[i].slug === slug) return projects[i];
}
return null;
}
function uniqueUsers(values) {
var source = Array.isArray(values) ? values : [];
var seen = {};
var out = [];
for (var i = 0; i < source.length; i++) {
if (typeof source[i] !== "string" || !source[i] || seen[source[i]]) continue;
seen[source[i]] = true;
out.push(source[i]);
}
return out;
}
function pendingLifecycleOperation(config, project) {
return Object.values(config.projectLifecycleOperations || {}).find(function (operation) {
return operation.phase === 'pending' && operation.slug === project.slug && operation.projectKnowledgeId === project.projectKnowledgeId && operation.expected === JSON.stringify(project);
});
}
function getProjectAccess(config, slug, osUsers, worktreeIdentity) {
var parentSlug = parentSlugFor(slug);
var project = findProject(config, parentSlug || slug);
if (!project) return { error: "Project not found" };
var isMateProject = project.isMate === true || project.slug.indexOf("mate-") === 0;
if (isMateProject) {
return { slug: slug, isMate: true, visibility: "private", ownerId: project.ownerId || null,
allowedUsers: [], sessionVisibilityDefault: "private", isWorktree: !!parentSlug };
}
var visibility = isMateProject ? "private" : (project.visibility || (osUsers ? "private" : "public"));
var parentAllowedUsers = uniqueUsers(project.allowedUsers);
if (!parentSlug) {
return {
slug: slug,
lifecycleExecutionPaused: project.lifecycleExecutionPaused === true || !!pendingLifecycleOperation(config, project),
ownershipRecovery: project.ownershipRecovery || null,
visibility: visibility,
allowedUsers: parentAllowedUsers,
ownerId: project.ownerId || null,
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
isWorktree: false,
};
}
var grants = project.worktreeAllowedUsers && project.worktreeAllowedUsers[slug];
var worktreeAllowedUsers = uniqueUsers(grants);
var preference = worktreePreferences.entry(config, parentSlug, slug);
var identityKnown = worktreeIdentity && worktreeIdentity.slug === slug &&
typeof worktreeIdentity.path === "string" && worktreeIdentity.path &&
typeof worktreeIdentity.changeSetId === "string" && worktreeIdentity.changeSetId;
var boundPreference = identityKnown
? worktreePreferences.blocked(config, parentSlug, slug, worktreeIdentity.path, worktreeIdentity.changeSetId)
: preference && preference.lifecycleState;
return {
slug: slug,
lifecycleExecutionPaused: project.lifecycleExecutionPaused === true || !!pendingLifecycleOperation(config, project) || !!boundPreference,
ownershipRecovery: project.ownershipRecovery || null,
visibility: visibility,
allowedUsers: uniqueUsers(parentAllowedUsers.concat(worktreeAllowedUsers)),
ownerId: project.ownerId || null,
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
isWorktree: true,
parentSlug: parentSlug,
parentAllowedUsers: parentAllowedUsers,
worktreeAllowedUsers: worktreeAllowedUsers,
parentAccess: {
slug: parentSlug,
visibility: visibility,
allowedUsers: parentAllowedUsers,
ownerId: project.ownerId || null,
sessionVisibilityDefault: project.sessionVisibilityDefault === "shared" ? "shared" : "private",
},
};
}
function setAllowedUsers(config, slug, allowedUsers) {
var parentSlug = parentSlugFor(slug);
var project = findProject(config, parentSlug || slug);
if (!project) return { error: "Project not found" };
if (project.slug.indexOf("mate-") === 0 || project.isMate) return { error: "Clay and Mates cannot be shared." };
var clean = uniqueUsers(allowedUsers);
if (!parentSlug) {
project.allowedUsers = clean;
return { ok: true, isWorktree: false, allowedUsers: clean };
}
if (!project.worktreeAllowedUsers || typeof project.worktreeAllowedUsers !== "object" || Array.isArray(project.worktreeAllowedUsers)) {
project.worktreeAllowedUsers = {};
}
if (clean.length > 0) project.worktreeAllowedUsers[slug] = clean;
else delete project.worktreeAllowedUsers[slug];
if (Object.keys(project.worktreeAllowedUsers).length === 0) delete project.worktreeAllowedUsers;
return { ok: true, isWorktree: true, allowedUsers: clean };
}
function setSessionVisibilityDefault(config, slug, visibility) {
if (visibility !== "private" && visibility !== "shared") return { error: "Invalid session visibility default" };
if (parentSlugFor(slug)) return { error: "Worktrees inherit this setting from their parent project." };
var project = findProject(config, slug);
if (!project) return { error: "Project not found" };
if (project.slug.indexOf("mate-") === 0 || project.isMate) return { error: "Clay and Mates are always private." };
if (visibility === "shared") project.sessionVisibilityDefault = "shared";
else delete project.sessionVisibilityDefault;
return { ok: true, visibility: visibility };
}
function clearWorktreeGrant(config, slug) {
var parentSlug = parentSlugFor(slug);
var project = parentSlug ? findProject(config, parentSlug) : null;
if (!project || !project.worktreeAllowedUsers || !Object.prototype.hasOwnProperty.call(project.worktreeAllowedUsers, slug)) return false;
delete project.worktreeAllowedUsers[slug];
if (Object.keys(project.worktreeAllowedUsers).length === 0) delete project.worktreeAllowedUsers;
return true;
}
function reconcileWorktreeGrants(config, parentSlug, existingSlugs) {
var project = findProject(config, parentSlug);
if (!project || !project.worktreeAllowedUsers) return false;
var existing = {};
for (var i = 0; i < existingSlugs.length; i++) existing[existingSlugs[i]] = true;
var changed = false;
var granted = Object.keys(project.worktreeAllowedUsers);
for (var j = 0; j < granted.length; j++) {
if (existing[granted[j]]) continue;
delete project.worktreeAllowedUsers[granted[j]];
changed = true;
}
if (Object.keys(project.worktreeAllowedUsers).length === 0) delete project.worktreeAllowedUsers;
return changed;
}
module.exports = {
pendingLifecycleOperation: pendingLifecycleOperation,
parentSlugFor: parentSlugFor,
getProjectAccess: getProjectAccess,
setAllowedUsers: setAllowedUsers,
setSessionVisibilityDefault: setSessionVisibilityDefault,
clearWorktreeGrant: clearWorktreeGrant,
reconcileWorktreeGrants: reconcileWorktreeGrants,
};