using ClawdDotNet.Core.Config; namespace ClawdDotNet.Core.Organisation; /// #684: A workspace an agent may use, and how far. /// The workspace of the agent's own unit — reachable as unit. public sealed record WorkspaceGrant(WorkspaceConfig Workspace, WorkspaceAccess Access, bool IsOwnUnit); /// /// #684: Where the workspaces live on disk, and which names are taken. /// /// The instance's one shared folder is Agents/SharedWorkspace, and it stays /// there: FTP, RocketChat, the agent editor, the task files and the backups all find it by /// that path. The workspaces with member lists live beside it, one folder each under /// Agents/SharedWorkspaces/. Found from the shared folder's path, which every tool /// context already carries, so nothing new has to be passed around. /// public static class WorkspaceLayout { public const string Personal = "personal"; public const string Shared = "shared"; /// The own unit's workspace, without having to know the unit's id. public const string Unit = "unit"; /// The folder next to SharedWorkspace that holds the workspaces. public const string ContainerFolder = "SharedWorkspaces"; /// Names a workspace cannot have — they mean something else in FileRW. public static readonly IReadOnlyList Reserved = [Personal, Shared, Unit]; /// Letters, digits, '-' and '_' — the same rule as for unit ids, since a unit's /// workspace carries the unit's id — and not one of the reserved names. public static bool IsValidId(string? id) => !string.IsNullOrWhiteSpace(id) && id.Length <= 64 && id.All(c => char.IsLetterOrDigit(c) || c is '-' or '_') && !Reserved.Contains(id, StringComparer.OrdinalIgnoreCase); /// /// The folder of a workspace, from the path of the instance's shared folder; /// null when that is not configured or the id is not valid. /// public static string? PathOf(string? sharedWorkspacePath, string workspaceId) { if (string.IsNullOrWhiteSpace(sharedWorkspacePath) || !IsValidId(workspaceId)) return null; var shared = Path.GetFullPath(sharedWorkspacePath) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); var agentsDirectory = Path.GetDirectoryName(shared); return agentsDirectory is null ? null : Path.Combine(agentsDirectory, ContainerFolder, workspaceId); } }